UNPKG

@olton/latte

Version:

Simple test framework for JavaScript and TypeScript with DOM supports

195 lines (184 loc) 5.84 kB
import { styles } from './styles.js' export function htmlTemplate (data) { const { summary, files, coverage, timestamp, duration } = data return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Latte Report</title> <style>${styles}</style> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> </head> <body> <div class="container"> <header> <h1>Latte Report</h1> <div class="timestamp">Generated on ${timestamp}</div> </header> <section class="summary"> <h2>Summary</h2> <div class="summary-row"> <span class="summary-label">Total Tests:</span> <span class="metric">${summary.total}</span> </div> <div class="summary-row"> <span class="summary-label">Passed:</span> <span class="metric success">${summary.passed}</span> </div> <div class="summary-row"> <span class="summary-label">Failed:</span> <span class="metric failure">${summary.failed}</span> </div> <div class="summary-row"> <span class="summary-label">Duration:</span> <span class="metric">${duration} ms</span> </div> </section> <div class="charts"> <div class="chart"> <h3>Test Results</h3> <canvas id="testResultsChart"></canvas> </div> ${coverage ? ` <div class="chart"> <h3>Code Coverage</h3> <canvas id="coverageChart"></canvas> </div> ` : ''} </div> <section class="test-files"> <h2>Test Files</h2> ${files.map(file => ` <div class="file-block"> <div class="file-header"> <div class="file-name">${file.file}</div> <div class="file-status ${file.allPassed ? 'passed' : 'failed'}">${file.allPassed ? 'Passed' : 'Failed'}</div> </div> <div class="file-meta"> <span>Duration: ${file.duration} ms</span> | <span>Tests: ${file.totalTests}</span> | <span>Passed: ${file.passedTests}</span> | <span>Failed: ${file.failedTests}</span> </div> ${file.tests.map(suite => ` <div class="test-suite"> <div class="suite-name">${suite.name || 'File-level tests'}</div> ${suite.tests.map(test => ` <div class="test-case ${test.passed ? 'passed' : 'failed'}"> <div class="test-name">${test.name}</div> ${!test.passed ? ` <div class="error-details">${test.message || 'Test failed'}</div> ` : ''} </div> `).join('')} </div> `).join('')} </div> `).join('')} </section> ${coverage ? ` <section class="coverage-section"> <h2 class="coverage-header">Code Coverage</h2> ${coverage.map(file => { const linePercentage = +file.percent const colorClass = linePercentage < 50 ? 'low' : linePercentage < 80 ? 'medium' : '' return ` <div class="coverage-file"> <div class="file-name">${file.name}</div> <div class="coverage-details"> <div class="coverage-item"> <span class="coverage-label">Coverage</span> <span class="coverage-value">${linePercentage.toFixed(2)}%</span> </div> <div class="coverage-item"> <span class="coverage-label">Lines</span> <span class="coverage-value">${file.covered}/${file.total}</span> </div> <div class="progress-bar"> <div class="progress-value ${colorClass}" style="width: ${linePercentage}%"></div> </div> </div> </div> ` }).join('')} </section> ` : ''} <footer> Generated by Latte | ${new Date().toISOString()} <div> Latte created by <a href="https://pimenov.com.ua">Serhii Pimenov</a> </div> </footer> </div> <script> // Настройка графика тестовых результатов const testResultsCtx = document.getElementById('testResultsChart').getContext('2d'); new Chart(testResultsCtx, { type: 'doughnut', data: { labels: ['Passed', 'Failed'], datasets: [{ data: [${summary.passed}, ${summary.failed}], backgroundColor: ['#28a745', '#dc3545'], borderWidth: 0 }] }, options: { responsive: true, plugins: { legend: { position: 'bottom' } } } }); ${coverage ? ` // Настройка графика покрытия кода const coverageCtx = document.getElementById('coverageChart').getContext('2d'); new Chart(coverageCtx, { type: 'bar', data: { labels: [${coverage.map(file => `'${file.name}'`).join(', ')}], datasets: [{ label: 'Line Coverage (%)', data: [${coverage.map(file => file.percent).join(', ')}], backgroundColor: '#4caf50', borderWidth: 0 }] }, options: { responsive: true, aspectRatio: 1, scales: { y: { beginAtZero: true, max: 100 } }, plugins: { legend: { position: 'bottom' } } } }); ` : ''} </script> </body> </html>` }