From 0345520cf2a490dc25ad1ac65d7d405e32a708c1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com>
Date: 2025年8月23日 15:51:58 +0000
Subject: [PATCH 01/11] Initial plan
From bb926263f4cbe7b92474a320d0ad3f3acad5375a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com>
Date: 2025年8月23日 16:03:36 +0000
Subject: [PATCH 02/11] Implement complete HTML reporter for CodeceptJS
Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
---
lib/plugin/htmlReporter.js | 639 ++++++++++++++++++
output/report.html | 528 +++++++++++++++
.../html-reporter-plugin/artifacts_test.js | 14 +
.../html-reporter-plugin/codecept.conf.js | 21 +
.../html-reporter_test.js | 16 +
.../html-reporter-plugin/output/report.html | 528 +++++++++++++++
test/runner/html-reporter-plugin_test.js | 49 ++
7 files changed, 1795 insertions(+)
create mode 100644 lib/plugin/htmlReporter.js
create mode 100644 output/report.html
create mode 100644 test/data/sandbox/configs/html-reporter-plugin/artifacts_test.js
create mode 100644 test/data/sandbox/configs/html-reporter-plugin/codecept.conf.js
create mode 100644 test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js
create mode 100644 test/data/sandbox/configs/html-reporter-plugin/output/report.html
create mode 100644 test/runner/html-reporter-plugin_test.js
diff --git a/lib/plugin/htmlReporter.js b/lib/plugin/htmlReporter.js
new file mode 100644
index 000000000..737a65c7c
--- /dev/null
+++ b/lib/plugin/htmlReporter.js
@@ -0,0 +1,639 @@
+const fs = require('fs')
+const path = require('path')
+const mkdirp = require('mkdirp')
+const crypto = require('crypto')
+const { template } = require('../utils')
+
+const event = require('../event')
+const output = require('../output')
+
+const defaultConfig = {
+ output: global.output_dir || './output',
+ reportFileName: 'report.html',
+ includeArtifacts: true,
+ showSteps: true,
+ showSkipped: true,
+}
+
+/**
+ * HTML Reporter Plugin for CodeceptJS
+ *
+ * Generates comprehensive HTML reports showing:
+ * - Test statistics
+ * - Feature/Scenario details
+ * - Individual step results
+ * - Test artifacts (screenshots, etc.)
+ *
+ * ## Configuration
+ *
+ * ```js
+ * "plugins": {
+ * "htmlReporter": {
+ * "enabled": true,
+ * "output": "./output",
+ * "reportFileName": "report.html",
+ * "includeArtifacts": true,
+ * "showSteps": true,
+ * "showSkipped": true
+ * }
+ * }
+ * ```
+ */
+module.exports = function (config) {
+ const options = { ...defaultConfig, ...config }
+ let reportData = {
+ stats: {},
+ tests: [],
+ failures: [],
+ startTime: null,
+ endTime: null,
+ }
+ let currentTestSteps = []
+
+ // Initialize report directory
+ const reportDir = path.resolve(options.output)
+ mkdirp.sync(reportDir)
+
+ // Track overall test execution
+ event.dispatcher.on(event.all.before, () => {
+ reportData.startTime = new Date()
+ output.plugin('htmlReporter', 'Starting HTML report generation...')
+ })
+
+ // Track test start to initialize steps collection
+ event.dispatcher.on(event.test.before, test => {
+ currentTestSteps = []
+ })
+
+ // Collect step information
+ event.dispatcher.on(event.step.started, step => {
+ step.htmlReporterStartTime = Date.now()
+ })
+
+ event.dispatcher.on(event.step.finished, step => {
+ if (step.htmlReporterStartTime) {
+ step.duration = Date.now() - step.htmlReporterStartTime
+ }
+ currentTestSteps.push({
+ name: step.name,
+ actor: step.actor,
+ args: step.args || [],
+ status: step.failed ? 'failed' : 'success',
+ duration: step.duration || 0,
+ })
+ })
+
+ // Collect test results
+ event.dispatcher.on(event.test.finished, test => {
+ reportData.tests.push({
+ ...test,
+ id: generateTestId(test),
+ duration: test.duration || 0,
+ steps: [...currentTestSteps], // Copy the steps
+ artifacts: test.artifacts || [],
+ })
+ })
+
+ // Generate final report
+ event.dispatcher.on(event.all.result, result => {
+ reportData.endTime = new Date()
+ reportData.stats = result.stats
+ reportData.failures = result.failures || []
+ reportData.duration = reportData.endTime - reportData.startTime
+
+ generateHtmlReport(reportData, options)
+ })
+
+ function generateTestId(test) {
+ return crypto
+ .createHash('sha256')
+ .update(`${test.parent?.title || 'unknown'}_${test.title}`)
+ .digest('hex')
+ .substring(0, 8)
+ }
+
+ function generateHtmlReport(data, config) {
+ const reportPath = path.join(reportDir, config.reportFileName)
+
+ const html = template(getHtmlTemplate(), {
+ title: 'CodeceptJS Test Report',
+ timestamp: data.endTime.toISOString(),
+ duration: formatDuration(data.duration),
+ stats: JSON.stringify(data.stats),
+ statsHtml: generateStatsHtml(data.stats),
+ testsHtml: generateTestsHtml(data.tests, config),
+ failuresHtml: generateFailuresHtml(data.failures),
+ cssStyles: getCssStyles(),
+ jsScripts: getJsScripts(),
+ })
+
+ fs.writeFileSync(reportPath, html)
+ output.print(`HTML Report saved to: ${reportPath}`)
+ }
+
+ function generateStatsHtml(stats) {
+ const passed = stats.passes || 0
+ const failed = stats.failures || 0
+ const pending = stats.pending || 0
+ const total = stats.tests || 0
+
+ return `
+
+
+
Total
+ ${total}
+
+
+
Passed
+ ${passed}
+
+
+
Failed
+ ${failed}
+
+
+
Pending
+ ${pending}
+
+
+ `
+ }
+
+ function generateTestsHtml(tests, config) {
+ if (!tests || tests.length === 0) {
+ return 'No tests found.
'
+ }
+
+ return tests.map(test => {
+ const statusClass = test.state || 'unknown'
+ const feature = test.parent?.title || 'Unknown Feature'
+ const steps = config.showSteps && test.steps ? generateStepsHtml(test.steps) : ''
+ const artifacts = config.includeArtifacts && test.artifacts ? generateArtifactsHtml(test.artifacts) : ''
+
+ return `
+
+
+ ●くろまる
+
${test.title}
+ ${feature}
+ ${formatDuration(test.duration)}
+
+
+ ${test.err ? `
${escapeHtml(test.err.message || '').replace(/\x1b\[[0-9;]*m/g, '')}
` : ''}
+ ${steps}
+ ${artifacts}
+
+
+ `
+ }).join('')
+ }
+
+ function generateStepsHtml(steps) {
+ if (!steps || steps.length === 0) return ''
+
+ const stepsHtml = steps.map(step => {
+ const statusClass = step.status || 'unknown'
+ const args = step.args ? step.args.map(arg => JSON.stringify(arg)).join(', ') : ''
+ const stepName = step.name || 'unknown step'
+ const actor = step.actor || 'I'
+
+ return `
+
+ ●くろまる
+ ${actor}.${stepName}(${args})
+ ${formatDuration(step.duration)}
+
+ `
+ }).join('')
+
+ return `
+
+
Steps:
+
${stepsHtml}
+
+ `
+ }
+
+ function generateArtifactsHtml(artifacts) {
+ if (!artifacts || artifacts.length === 0) return ''
+
+ const artifactsHtml = artifacts.map(artifact => {
+ if (typeof artifact === 'string' && artifact.match(/\.(png|jpg|jpeg|gif)$/i)) {
+ const relativePath = path.relative(reportDir, artifact)
+ return `Screenshot`
+ }
+ return `${escapeHtml(artifact.toString())}
`
+ }).join('')
+
+ return `
+
+
Artifacts:
+
${artifactsHtml}
+
+ `
+ }
+
+ function generateFailuresHtml(failures) {
+ if (!failures || failures.length === 0) {
+ return 'No failures.
'
+ }
+
+ return failures.map((failure, index) => {
+ const failureText = failure.toString().replace(/\x1b\[[0-9;]*m/g, '') // Remove ANSI escape codes
+ return `
+
+
Failure ${index + 1}
+
${escapeHtml(failureText)}
+
+ `
+ }).join('')
+ }
+
+ function formatDuration(duration) {
+ if (!duration) return '0ms'
+ if (duration < 1000) return `${duration}ms` + return `${(duration / 1000).toFixed(2)}s` + } + + function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ }
+
+ function getHtmlTemplate() {
+ return `
+
+
+
+
+
+ {{title}}
+
+
+
+
+
+
+
+ Test Statistics
+ {{statsHtml}}
+
+
+
+ Test Results
+
+ {{testsHtml}}
+
+
+
+
+ Failures
+
+ {{failuresHtml}}
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+ }
+
+ function getCssStyles() {
+ return `
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ line-height: 1.6;
+ color: #333;
+ background-color: #f5f5f5;
+}
+
+.report-header {
+ background: #2c3e50;
+ color: white;
+ padding: 2rem 1rem;
+ text-align: center;
+}
+
+.report-header h1 {
+ margin-bottom: 0.5rem;
+ font-size: 2.5rem;
+}
+
+.report-meta {
+ font-size: 0.9rem;
+ opacity: 0.8;
+}
+
+.report-meta span {
+ margin: 0 1rem;
+}
+
+.report-content {
+ max-width: 1200px;
+ margin: 2rem auto;
+ padding: 0 1rem;
+}
+
+.stats-section, .tests-section, .failures-section {
+ background: white;
+ margin-bottom: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ overflow: hidden;
+}
+
+.stats-section h2, .tests-section h2, .failures-section h2 {
+ background: #34495e;
+ color: white;
+ padding: 1rem;
+ margin: 0;
+}
+
+.stats-cards {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+ padding: 1rem;
+}
+
+.stat-card {
+ flex: 1;
+ min-width: 150px;
+ padding: 1rem;
+ text-align: center;
+ border-radius: 4px;
+ color: white;
+}
+
+.stat-card.total { background: #3498db; }
+.stat-card.passed { background: #27ae60; }
+.stat-card.failed { background: #e74c3c; }
+.stat-card.pending { background: #f39c12; }
+
+.stat-card h3 {
+ font-size: 0.9rem;
+ margin-bottom: 0.5rem;
+}
+
+.stat-number {
+ font-size: 2rem;
+ font-weight: bold;
+}
+
+.test-item {
+ border-bottom: 1px solid #eee;
+ margin: 0;
+}
+
+.test-item:last-child {
+ border-bottom: none;
+}
+
+.test-header {
+ display: flex;
+ align-items: center;
+ padding: 1rem;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.test-header:hover {
+ background-color: #f8f9fa;
+}
+
+.test-status {
+ font-size: 1.2rem;
+ margin-right: 0.5rem;
+}
+
+.test-status.passed { color: #27ae60; }
+.test-status.failed { color: #e74c3c; }
+.test-status.pending { color: #f39c12; }
+.test-status.skipped { color: #95a5a6; }
+
+.test-title {
+ flex: 1;
+ font-size: 1.1rem;
+ font-weight: 500;
+}
+
+.test-feature {
+ background: #ecf0f1;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ font-size: 0.8rem;
+ color: #34495e;
+ margin-right: 0.5rem;
+}
+
+.test-duration {
+ font-size: 0.8rem;
+ color: #7f8c8d;
+}
+
+.test-details {
+ display: none;
+ padding: 1rem;
+ background: #f8f9fa;
+ border-top: 1px solid #e9ecef;
+}
+
+.error-message {
+ background: #fee;
+ border: 1px solid #fcc;
+ border-radius: 4px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.error-message pre {
+ color: #c0392b;
+ font-family: 'Courier New', monospace;
+ font-size: 0.9rem;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+.steps-section, .artifacts-section {
+ margin-top: 1rem;
+}
+
+.steps-section h4, .artifacts-section h4 {
+ color: #34495e;
+ margin-bottom: 0.5rem;
+ font-size: 1rem;
+}
+
+.step-item {
+ display: flex;
+ align-items: center;
+ padding: 0.5rem 0;
+ border-bottom: 1px solid #ecf0f1;
+}
+
+.step-item:last-child {
+ border-bottom: none;
+}
+
+.step-status {
+ margin-right: 0.5rem;
+}
+
+.step-status.success { color: #27ae60; }
+.step-status.failed { color: #e74c3c; }
+
+.step-title {
+ flex: 1;
+ font-family: 'Courier New', monospace;
+ font-size: 0.9rem;
+}
+
+.step-duration {
+ font-size: 0.8rem;
+ color: #7f8c8d;
+}
+
+.artifacts-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+
+.artifact-image {
+ max-width: 200px;
+ max-height: 150px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: transform 0.2s;
+}
+
+.artifact-image:hover {
+ transform: scale(1.05);
+}
+
+.artifact-item {
+ background: #ecf0f1;
+ padding: 0.5rem;
+ border-radius: 4px;
+ font-size: 0.9rem;
+}
+
+.modal {
+ display: none;
+ position: fixed;
+ z-index: 1000;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0.8);
+ cursor: pointer;
+}
+
+.modal img {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ max-width: 90%;
+ max-height: 90%;
+ border-radius: 4px;
+}
+
+.failure-item {
+ padding: 1rem;
+ margin-bottom: 1rem;
+ border: 1px solid #fcc;
+ border-radius: 4px;
+ background: #fee;
+}
+
+.failure-item h4 {
+ color: #c0392b;
+ margin-bottom: 0.5rem;
+}
+
+.failure-details {
+ color: #333;
+ font-family: 'Courier New', monospace;
+ font-size: 0.9rem;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+@media (max-width: 768px) {
+ .stats-cards {
+ flex-direction: column;
+ }
+
+ .test-header {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 0.5rem;
+ }
+
+ .test-feature, .test-duration {
+ align-self: flex-start;
+ }
+}
+ `
+ }
+
+ function getJsScripts() {
+ return `
+function toggleTestDetails(testId) {
+ const details = document.getElementById('details-' + testId);
+ if (details.style.display === 'none' || details.style.display === '') {
+ details.style.display = 'block';
+ } else {
+ details.style.display = 'none';
+ }
+}
+
+function openImageModal(src) {
+ const modal = document.getElementById('imageModal');
+ const modalImg = document.getElementById('modalImage');
+ modalImg.src = src;
+ modal.style.display = 'block';
+}
+
+function closeImageModal() {
+ const modal = document.getElementById('imageModal');
+ modal.style.display = 'none';
+}
+
+// Initialize - hide failures section if no failures
+document.addEventListener('DOMContentLoaded', function() {
+ const failuresSection = document.querySelector('.failures-section');
+ const failureItems = document.querySelectorAll('.failure-item');
+ if (failureItems.length === 0) {
+ failuresSection.style.display = 'none';
+ }
+});
+ `
+ }
+}
\ No newline at end of file
diff --git a/output/report.html b/output/report.html
new file mode 100644
index 000000000..5c17c9401
--- /dev/null
+++ b/output/report.html
@@ -0,0 +1,528 @@
+
+
+
+
+
+
+ CodeceptJS Test Report
+
+
+
+
+
+
+
+ Test Statistics
+
+
+
+
Total
+ 3
+
+
+
Passed
+ 1
+
+
+
Failed
+ 2
+
+
+
Pending
+ 0
+
+
+
+
+
+
+ Test Results
+
+
+
+
+ ●くろまる
+
test with multiple steps
+ HTML Reporter Test
+ 0ms
+
+
+
+ File package.json not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 1ms
+
+
+
+ ●くろまる
+ I.seeFile("package.json")
+ 1ms
+
+
+
+
+
+
+
+
+
+
+ ●くろまる
+
test that will fail
+ HTML Reporter Test
+ 0ms
+
+
+
+ File this-file-should-not-exist.txt not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("this-file-should-not-exist.txt")
+ 0ms
+
+
+
+
+
+
+
+
+
+
+ ●くろまる
+
test that will pass
+ HTML Reporter Test
+ 0ms
+
+
+
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("codecept.conf.js")
+ 0ms
+
+
+
+
+
+
+
+
+
+
+
+
+ Failures
+
+
+
+
Failure 1
+
%s) %s:
+%s
+%s
+,1,HTML Reporter Test
+ test with multiple steps,
+
+ File package.json not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+ + expected - actual
+
+ -false
+ +true
+ , AssertionError [ERR_ASSERTION]:
+ at FileSystem.seeFile (lib/helper/FileSystem.js:70:12)
+ at HelperStep.run (lib/step/helper.js:28:49)
+
+
+ ◯ File: file:///home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js
+
+ ◯ Scenario Steps:
+ ✖ I.seeFile("package.json") at Test.<anonymous> (./html-reporter_test.js:5:5)
+ ✔ I.amInPath(".") at Test.<anonymous> (./html-reporter_test.js:4:5)
+
+
+
+
+
Failure 2
+
%s) %s:
+%s
+%s
+,2,HTML Reporter Test
+ test that will fail,
+
+ File this-file-should-not-exist.txt not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+ + expected - actual
+
+ -false
+ +true
+ , AssertionError [ERR_ASSERTION]:
+ at FileSystem.seeFile (lib/helper/FileSystem.js:70:12)
+ at HelperStep.run (lib/step/helper.js:28:49)
+
+
+ ◯ File: file:///home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js
+
+ ◯ Scenario Steps:
+ ✖ I.seeFile("this-file-should-not-exist.txt") at Test.<anonymous> (./html-reporter_test.js:10:5)
+ ✔ I.amInPath(".") at Test.<anonymous> (./html-reporter_test.js:9:5)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/data/sandbox/configs/html-reporter-plugin/artifacts_test.js b/test/data/sandbox/configs/html-reporter-plugin/artifacts_test.js
new file mode 100644
index 000000000..e0de9d354
--- /dev/null
+++ b/test/data/sandbox/configs/html-reporter-plugin/artifacts_test.js
@@ -0,0 +1,14 @@
+Feature('HTML Reporter with Artifacts Test')
+
+Scenario('test with artifacts', async ({ I }) => {
+ I.amInPath('.')
+ I.seeFile('codecept.conf.js')
+
+ // Simulate adding test artifacts
+ const currentTest = global.codecept_dir ? require('../../../../lib/container').get('mocha') : null
+ if (currentTest && currentTest.currentTest) {
+ currentTest.currentTest.artifacts = currentTest.currentTest.artifacts || []
+ currentTest.currentTest.artifacts.push('fake-screenshot-1.png')
+ currentTest.currentTest.artifacts.push('fake-screenshot-2.png')
+ }
+})
\ No newline at end of file
diff --git a/test/data/sandbox/configs/html-reporter-plugin/codecept.conf.js b/test/data/sandbox/configs/html-reporter-plugin/codecept.conf.js
new file mode 100644
index 000000000..61e085e6c
--- /dev/null
+++ b/test/data/sandbox/configs/html-reporter-plugin/codecept.conf.js
@@ -0,0 +1,21 @@
+exports.config = {
+ tests: './*_test.js',
+ output: './output',
+ helpers: {
+ FileSystem: {},
+ },
+ include: {},
+ bootstrap: false,
+ plugins: {
+ htmlReporter: {
+ enabled: true,
+ output: './output',
+ reportFileName: 'report.html',
+ includeArtifacts: true,
+ showSteps: true,
+ showSkipped: true,
+ },
+ },
+ mocha: {},
+ name: 'html-reporter-plugin tests',
+}
\ No newline at end of file
diff --git a/test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js b/test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js
new file mode 100644
index 000000000..1ec50a97d
--- /dev/null
+++ b/test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js
@@ -0,0 +1,16 @@
+Feature('HTML Reporter Test')
+
+Scenario('test with multiple steps', ({ I }) => {
+ I.amInPath('.')
+ I.seeFile('package.json')
+})
+
+Scenario('test that will fail', ({ I }) => {
+ I.amInPath('.')
+ I.seeFile('this-file-should-not-exist.txt')
+})
+
+Scenario('test that will pass', ({ I }) => {
+ I.amInPath('.')
+ I.seeFile('codecept.conf.js')
+})
\ No newline at end of file
diff --git a/test/data/sandbox/configs/html-reporter-plugin/output/report.html b/test/data/sandbox/configs/html-reporter-plugin/output/report.html
new file mode 100644
index 000000000..d894b3ca9
--- /dev/null
+++ b/test/data/sandbox/configs/html-reporter-plugin/output/report.html
@@ -0,0 +1,528 @@
+
+
+
+
+
+
+ CodeceptJS Test Report
+
+
+
+
+
+
+
+ Test Statistics
+
+
+
+
Total
+ 3
+
+
+
Passed
+ 1
+
+
+
Failed
+ 2
+
+
+
Pending
+ 0
+
+
+
+
+
+
+ Test Results
+
+
+
+
+ ●くろまる
+
test with multiple steps
+ HTML Reporter Test
+ 0ms
+
+
+
+ File package.json not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("package.json")
+ 1ms
+
+
+
+
+
+
+
+
+
+
+ ●くろまる
+
test that will fail
+ HTML Reporter Test
+ 0ms
+
+
+
+ File this-file-should-not-exist.txt not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("this-file-should-not-exist.txt")
+ 0ms
+
+
+
+
+
+
+
+
+
+
+ ●くろまる
+
test that will pass
+ HTML Reporter Test
+ 0ms
+
+
+
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("codecept.conf.js")
+ 0ms
+
+
+
+
+
+
+
+
+
+
+
+
+ Failures
+
+
+
+
Failure 1
+
%s) %s:
+%s
+%s
+,1,HTML Reporter Test
+ test with multiple steps,
+
+ File package.json not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+ + expected - actual
+
+ -false
+ +true
+ , AssertionError [ERR_ASSERTION]:
+ at FileSystem.seeFile (/home/runner/work/CodeceptJS/CodeceptJS/lib/helper/FileSystem.js:70:12)
+ at HelperStep.run (/home/runner/work/CodeceptJS/CodeceptJS/lib/step/helper.js:28:49)
+
+
+ ◯ File: file:///home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js
+
+ ◯ Scenario Steps:
+ ✖ I.seeFile("package.json") at Test.<anonymous> (./html-reporter_test.js:5:5)
+ ✔ I.amInPath(".") at Test.<anonymous> (./html-reporter_test.js:4:5)
+
+
+
+
+
Failure 2
+
%s) %s:
+%s
+%s
+,2,HTML Reporter Test
+ test that will fail,
+
+ File this-file-should-not-exist.txt not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+ + expected - actual
+
+ -false
+ +true
+ , AssertionError [ERR_ASSERTION]:
+ at FileSystem.seeFile (/home/runner/work/CodeceptJS/CodeceptJS/lib/helper/FileSystem.js:70:12)
+ at HelperStep.run (/home/runner/work/CodeceptJS/CodeceptJS/lib/step/helper.js:28:49)
+
+
+ ◯ File: file:///home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin/html-reporter_test.js
+
+ ◯ Scenario Steps:
+ ✖ I.seeFile("this-file-should-not-exist.txt") at Test.<anonymous> (./html-reporter_test.js:10:5)
+ ✔ I.amInPath(".") at Test.<anonymous> (./html-reporter_test.js:9:5)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/runner/html-reporter-plugin_test.js b/test/runner/html-reporter-plugin_test.js
new file mode 100644
index 000000000..bda8c8b8f
--- /dev/null
+++ b/test/runner/html-reporter-plugin_test.js
@@ -0,0 +1,49 @@
+const { expect } = require('expect')
+const exec = require('child_process').exec
+const { codecept_dir, codecept_run } = require('./consts')
+const debug = require('debug')('codeceptjs:tests')
+const fs = require('fs')
+const path = require('path')
+
+const config_run_config = (config, grep, verbose = false) => `${codecept_run} ${verbose ? '--verbose' : ''} --config ${codecept_dir}/configs/html-reporter-plugin/${config} ${grep ? `--grep "${grep}"` : ''}`
+
+describe('CodeceptJS html-reporter-plugin', function () {
+ this.timeout(10000)
+
+ it('should generate HTML report', done => {
+ exec(config_run_config('codecept.conf.js'), (err, stdout) => {
+ debug(stdout)
+
+ // Check if HTML report file exists
+ const reportFile = path.join(`${codecept_dir}/configs/html-reporter-plugin`, 'output', 'report.html')
+ expect(fs.existsSync(reportFile)).toBe(true)
+
+ // Read and validate HTML report content
+ const reportContent = fs.readFileSync(reportFile, 'utf8')
+ expect(reportContent).toContain('CodeceptJS Test Report')
+ expect(reportContent).toContain('Test Statistics')
+ expect(reportContent).toContain('Test Results')
+
+ // Check for specific test features
+ expect(reportContent).toContain('HTML Reporter Test') // Feature name
+ expect(reportContent).toContain('test with multiple steps') // Scenario name
+ expect(reportContent).toContain('test that will fail') // Another scenario
+ expect(reportContent).toContain('test that will pass') // Another scenario
+
+ // Validate that stats are included
+ expect(reportContent).toMatch(/Total.*Passed.*Failed/s)
+
+ // Check basic HTML structure
+ expect(reportContent).toContain('')
+ expect(reportContent).toContain('')
+ expect(reportContent).toContain('')
+ expect(reportContent).toContain('')
+
+ // Should contain CSS and JS
+ expect(reportContent).toContain('
-
-
+
+
- CodeceptJS Test Report
-
- Generated: 2025年08月23日T16:04:41.601Z
- Duration: 29ms
-
+ CodeceptJS Test Report
+
+ Generated: 2025年08月23日T16:28:10.912Z
+ Duration: 28ms
+
-
- Test Statistics
-
-
-
-
Total
- 4
-
-
-
Passed
- 1
-
-
-
Failed
- 3
-
-
-
Pending
- 0
-
-
-
-
-
-
- Test Results
-
-
-
-
- ●くろまる
-
test with multiple steps
- HTML Reporter Test
- 0ms
-
-
-
- File package.json not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
-
-
-
Steps:
-
-
- ●くろまる
- I.amInPath(".")
- 0ms
-
-
-
- ●くろまる
- I.seeFile("package.json")
- 1ms
-
-
-
-
-
+
+ Test Statistics
+
+
-
-
-
-
●くろまる
-
test that will fail
-
HTML Reporter Test
-
0ms
+
+
Passed
+ 1
-
-
- File this-file-should-not-exist.txt not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
-
-
-
Steps:
-
-
- ●くろまる
- I.amInPath(".")
- 0ms
-
-
-
- ●くろまる
- I.seeFile("this-file-should-not-exist.txt")
- 0ms
-
-
-
-
-
+
+
Failed
+ 3
-
-
-
-
-
●くろまる
-
test that will pass
-
HTML Reporter Test
-
0ms
+
+
Pending
+ 0
-
-
-
-
-
Steps:
-
-
- ●くろまる
- I.amInPath(".")
- 0ms
-
-
-
●くろまる
-
I.seeFile("codecept.conf.js")
-
0ms
+
+
+
-
-
-
-
+
+
+
+ Test Results
+
+
+
+ ●くろまる
+
test with multiple steps
+ HTML Reporter Test
+ 0ms
+
+
+
File package.json not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 1ms
+
+
+
+ ●くろまる
+ I.seeFile("package.json")
+ 1ms
+
+
+
+
-
-
-
-
-
●くろまる
-
test with artifacts
-
HTML Reporter with Artifacts Test
-
0ms
+
+
+
+ ●くろまる
+
test that will fail
+ HTML Reporter Test
+ 0ms
+
+
+
File this-file-should-not-exist.txt not found in /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("this-file-should-not-exist.txt")
+ 1ms
+
+
+
+
+
+
+
+
+ ●くろまる
+
test that will pass
+ HTML Reporter Test
+ 0ms
+
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("codecept.conf.js")
+ 0ms
+
+
+
+
-
-
+
+
+
+ ●くろまる
+
test with artifacts
+ HTML Reporter with Artifacts Test
+ 0ms
+
+
+
+
Cannot find module '../../../../lib/container'
Require stack:
- /home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin/artifacts_test.js
- /home/runner/work/CodeceptJS/CodeceptJS/node_modules/mocha/lib/mocha.js
- /home/runner/work/CodeceptJS/CodeceptJS/node_modules/mocha/index.js
- ...
-
-
-
Steps:
-
-
- ●くろまる
- I.amInPath(".")
- 0ms
-
-
-
- ●くろまる
- I.seeFile("codecept.conf.js")
- 0ms
-
-
-
-
-
+ ...
+
+
+
+
Steps:
+
+
+ ●くろまる
+ I.amInPath(".")
+ 0ms
+
+
+
+ ●くろまる
+ I.seeFile("codecept.conf.js")
+ 0ms
+
+
+
+
-
-
-
-
-
- Failures
-
-
-
-
Failure 1
-
%s) %s:
+
+
+
+ Failures
+
+
+
Failure 1
+
+ %s) %s:
%s
%s
,1,HTML Reporter Test
@@ -491,12 +579,14 @@ Failure 1
◯ Scenario Steps:
✖ I.seeFile("package.json") at Test.<anonymous> (./html-reporter_test.js:5:5)
✔ I.amInPath(".") at Test.<anonymous> (./html-reporter_test.js:4:5)
-
-
-
-
-
Failure 2
-
%s) %s:
+
+
+
+
+
Failure 2
+
+ %s) %s:
%s
%s
,2,HTML Reporter Test
@@ -517,12 +607,14 @@ Failure 2
◯ Scenario Steps:
✖ I.seeFile("this-file-should-not-exist.txt") at Test.<anonymous> (./html-reporter_test.js:10:5)
✔ I.amInPath(".") at Test.<anonymous> (./html-reporter_test.js:9:5)
-
-
-
-
-
Failure 3
-
%s) %s:
+
+
+
+
+
Failure 3
+
+ %s) %s:
%s
%s
,3,HTML Reporter with Artifacts Test
@@ -538,49 +630,147 @@ Failure 3
◯ File: file:///home/runner/work/CodeceptJS/CodeceptJS/test/data/sandbox/configs/html-reporter-plugin/artifacts_test.js
-
+
+
-
-
-
+
-
+
-
+