mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-07 16:40:50 +08:00
feat: enhance HTML report with modals for summary JSON and log content, and improve download functionality
This commit is contained in:
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2506251440
|
v5.0.0-beta-2506251530
|
||||||
|
|||||||
@@ -2337,25 +2337,25 @@ const htmlTemplate = `<!DOCTYPE html>
|
|||||||
<div class="subtitle">Start Time: {{.Time.StartAt.Format "2006-01-02 15:04:05"}}</div>
|
<div class="subtitle">Start Time: {{.Time.StartAt.Format "2006-01-02 15:04:05"}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="download-section">
|
<div class="download-section">
|
||||||
<div class="download-title">📥 Download & View</div>
|
<div class="download-title">📥 View & Download</div>
|
||||||
<div class="download-buttons">
|
<div class="download-buttons">
|
||||||
<button class="download-btn" onclick="downloadSummary()">
|
{{if getCaseContentBase64}}
|
||||||
<span>📄</span>
|
<button class="download-btn" onclick="showCaseJson()">
|
||||||
<span>summary.json</span>
|
<span>📋</span>
|
||||||
</button>
|
<span>case.json</span>
|
||||||
<button class="download-btn" onclick="downloadLog()">
|
</button>
|
||||||
<span>📋</span>
|
{{end}}
|
||||||
<span>hrp.log</span>
|
<button class="download-btn" onclick="showSummaryJson()">
|
||||||
</button>
|
<span>📄</span>
|
||||||
{{if getCaseContentBase64}}
|
<span>summary.json</span>
|
||||||
<button class="download-btn" onclick="showCaseJson()">
|
</button>
|
||||||
<span>📋</span>
|
<button class="download-btn" onclick="showLogContent()">
|
||||||
<span>case.json</span>
|
<span>📋</span>
|
||||||
</button>
|
<span>hrp.log</span>
|
||||||
{{end}}
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2891,6 +2891,46 @@ const htmlTemplate = `<!DOCTYPE html>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Summary JSON Modal -->
|
||||||
|
<div id="summaryModal" class="json-modal">
|
||||||
|
<div class="json-modal-content">
|
||||||
|
<div class="json-modal-header">
|
||||||
|
<h2 class="json-modal-title">📄 Summary JSON</h2>
|
||||||
|
<button class="json-close" onclick="closeSummaryModal()">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="json-modal-body">
|
||||||
|
<div class="json-toolbar">
|
||||||
|
<div>
|
||||||
|
<button onclick="copySummaryContent()">📋 Copy to Clipboard</button>
|
||||||
|
<button onclick="downloadSummary()">📥 Download summary.json</button>
|
||||||
|
</div>
|
||||||
|
<span class="copy-status" id="summaryStatus">✅ Copied!</span>
|
||||||
|
</div>
|
||||||
|
<pre class="json-content" id="summaryContent"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Log Content Modal -->
|
||||||
|
<div id="logModal" class="json-modal">
|
||||||
|
<div class="json-modal-content">
|
||||||
|
<div class="json-modal-header">
|
||||||
|
<h2 class="json-modal-title">📋 Log Content</h2>
|
||||||
|
<button class="json-close" onclick="closeLogModal()">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="json-modal-body">
|
||||||
|
<div class="json-toolbar">
|
||||||
|
<div>
|
||||||
|
<button onclick="copyLogContent()">📋 Copy to Clipboard</button>
|
||||||
|
<button onclick="downloadLog()">📥 Download hrp.log</button>
|
||||||
|
</div>
|
||||||
|
<span class="copy-status" id="logStatus">✅ Copied!</span>
|
||||||
|
</div>
|
||||||
|
<pre class="json-content" id="logContentDisplay"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Embedded file contents for download (Base64 encoded)
|
// Embedded file contents for download (Base64 encoded)
|
||||||
const summaryContentBase64 = "{{getSummaryContentBase64}}";
|
const summaryContentBase64 = "{{getSummaryContentBase64}}";
|
||||||
@@ -3000,6 +3040,85 @@ const htmlTemplate = `<!DOCTYPE html>
|
|||||||
downloadFile(caseContent, 'case.json', 'application/json');
|
downloadFile(caseContent, 'case.json', 'application/json');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Summary JSON Modal functions
|
||||||
|
function showSummaryJson() {
|
||||||
|
if (!summaryContent) {
|
||||||
|
alert('Summary JSON content not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse and format JSON for beautiful display
|
||||||
|
const jsonObj = JSON.parse(summaryContent);
|
||||||
|
const formattedJson = JSON.stringify(jsonObj, null, 2);
|
||||||
|
|
||||||
|
document.getElementById('summaryContent').textContent = formattedJson;
|
||||||
|
document.getElementById('summaryModal').style.display = 'block';
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse JSON:', e);
|
||||||
|
// Fallback to raw content if parsing fails
|
||||||
|
document.getElementById('summaryContent').textContent = summaryContent;
|
||||||
|
document.getElementById('summaryModal').style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSummaryModal() {
|
||||||
|
document.getElementById('summaryModal').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function copySummaryContent() {
|
||||||
|
const content = document.getElementById('summaryContent').textContent;
|
||||||
|
if (!content) {
|
||||||
|
alert('No content to copy');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(content).then(function() {
|
||||||
|
const copyStatus = document.getElementById('summaryStatus');
|
||||||
|
copyStatus.classList.add('show');
|
||||||
|
setTimeout(function() {
|
||||||
|
copyStatus.classList.remove('show');
|
||||||
|
}, 2000);
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.error('Failed to copy to clipboard:', err);
|
||||||
|
alert('Failed to copy to clipboard. Please select and copy manually.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log Content Modal functions
|
||||||
|
function showLogContent() {
|
||||||
|
if (!logContent) {
|
||||||
|
alert('Log content not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('logContentDisplay').textContent = logContent;
|
||||||
|
document.getElementById('logModal').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLogModal() {
|
||||||
|
document.getElementById('logModal').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyLogContent() {
|
||||||
|
const content = document.getElementById('logContentDisplay').textContent;
|
||||||
|
if (!content) {
|
||||||
|
alert('No content to copy');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(content).then(function() {
|
||||||
|
const copyStatus = document.getElementById('logStatus');
|
||||||
|
copyStatus.classList.add('show');
|
||||||
|
setTimeout(function() {
|
||||||
|
copyStatus.classList.remove('show');
|
||||||
|
}, 2000);
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.error('Failed to copy to clipboard:', err);
|
||||||
|
alert('Failed to copy to clipboard. Please select and copy manually.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function toggleStep(stepIndex) {
|
function toggleStep(stepIndex) {
|
||||||
const content = document.getElementById('step-' + stepIndex);
|
const content = document.getElementById('step-' + stepIndex);
|
||||||
const icon = document.getElementById('toggle-' + stepIndex);
|
const icon = document.getElementById('toggle-' + stepIndex);
|
||||||
@@ -3068,10 +3187,12 @@ const htmlTemplate = `<!DOCTYPE html>
|
|||||||
document.getElementById('imageModal').style.display = 'none';
|
document.getElementById('imageModal').style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close modal when clicking outside the image or JSON modal
|
// Close modal when clicking outside the image or JSON modals
|
||||||
window.onclick = function(event) {
|
window.onclick = function(event) {
|
||||||
const imageModal = document.getElementById('imageModal');
|
const imageModal = document.getElementById('imageModal');
|
||||||
const jsonModal = document.getElementById('jsonModal');
|
const jsonModal = document.getElementById('jsonModal');
|
||||||
|
const summaryModal = document.getElementById('summaryModal');
|
||||||
|
const logModal = document.getElementById('logModal');
|
||||||
|
|
||||||
if (event.target == imageModal) {
|
if (event.target == imageModal) {
|
||||||
imageModal.style.display = 'none';
|
imageModal.style.display = 'none';
|
||||||
@@ -3079,6 +3200,12 @@ const htmlTemplate = `<!DOCTYPE html>
|
|||||||
if (event.target == jsonModal) {
|
if (event.target == jsonModal) {
|
||||||
jsonModal.style.display = 'none';
|
jsonModal.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
if (event.target == summaryModal) {
|
||||||
|
summaryModal.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (event.target == logModal) {
|
||||||
|
logModal.style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-expand all steps on load to show actions
|
// Auto-expand all steps on load to show actions
|
||||||
|
|||||||
@@ -424,6 +424,9 @@ func resizeImage(src image.Image, width, height int) image.Image {
|
|||||||
|
|
||||||
// CompressImageFile compresses an image file and returns the compressed data
|
// CompressImageFile compresses an image file and returns the compressed data
|
||||||
func CompressImageFile(imagePath string, enableResize bool, maxWidth int) ([]byte, error) {
|
func CompressImageFile(imagePath string, enableResize bool, maxWidth int) ([]byte, error) {
|
||||||
|
log.Debug().Str("imagePath", imagePath).Bool("enableResize", enableResize).
|
||||||
|
Int("maxWidth", maxWidth).Msg("compress image file")
|
||||||
|
|
||||||
// Read the original image file
|
// Read the original image file
|
||||||
file, err := os.Open(imagePath)
|
file, err := os.Open(imagePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user