From ea882c2f1e09b7779f7fb5ffd4192d10fc79d6c4 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Thu, 16 Oct 2025 19:20:56 -0700 Subject: [PATCH] HtmlReporterV2: Show a non-color indication of status while running --- lib/jasmine-core/jasmine-html.js | 27 +++++++++++-- lib/jasmine-core/jasmine.css | 4 ++ spec/html/HtmlReporterV2Spec.js | 69 +++++++++++++++++++++++++++++++- src/html/HtmlReporterV2.js | 10 +++-- src/html/OverallStatusBar.js | 17 +++++++- src/html/_HTMLReporter.scss | 5 +++ 6 files changed, 123 insertions(+), 9 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index c9aa426f..5ca93110 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -978,6 +978,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { // Sub-views #alerts; + #statusBar; #progress; #banner; #failures; @@ -1012,6 +1013,9 @@ jasmineRequire.HtmlReporterV2 = function(j$) { this.#stateBuilder = new j$.private.ResultsStateBuilder(); this.#alerts = new j$.private.AlertsView(this.#urlBuilder); + this.#statusBar = new j$.private.OverallStatusBar(this.#urlBuilder); + this.#statusBar.showRunning(); + this.#alerts.addBar(this.#statusBar.rootEl); this.#progress = new ProgressView(); this.#banner = new j$.private.Banner( this.#queryString.navigateWithNewParam.bind(this.#queryString), @@ -1044,6 +1048,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { if (result.status === 'failed') { this.#failures.append(result, this.#stateBuilder.currentParent); + this.#statusBar.showFailing(); } } @@ -1064,6 +1069,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { if (result.status === 'failed') { this.#failures.append(result, this.#stateBuilder.currentParent); + this.#statusBar.showFailing(); } } @@ -1082,9 +1088,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { ); } - const statusBar = new j$.private.OverallStatusBar(this.#urlBuilder); - statusBar.showDone(doneResult, this.#stateBuilder); - this.#alerts.addBar(statusBar.rootEl); + this.#statusBar.showDone(doneResult, this.#stateBuilder); if (doneResult.failedExpectations) { for (const f of doneResult.failedExpectations) { @@ -1327,6 +1331,7 @@ jasmineRequire.OverallStatusBar = function(j$) { 'use strict'; const { createDom } = j$.private.htmlReporterUtils; + const staticClassNames = 'jasmine-overall-result jasmine-bar'; class OverallStatusBar { #urlBuilder; @@ -1334,11 +1339,25 @@ jasmineRequire.OverallStatusBar = function(j$) { constructor(urlBuilder) { this.#urlBuilder = urlBuilder; this.rootEl = createDom('span', { - className: 'jasmine-overall-result jasmine-bar' + className: staticClassNames, + 'aria-live': 'polite' }); } + showRunning() { + this.rootEl.textContent = 'Running...'; + this.rootEl.classList.add('jasmine-in-progress'); + } + + showFailing() { + this.rootEl.textContent = 'Failing...'; + this.rootEl.classList.add('jasmine-failed'); + } + showDone(doneResult, stateBuilder) { + // Clear any classes added to represent in-progress state + this.rootEl.className = staticClassNames; + let statusBarMessage = ''; const globalFailures = (doneResult && doneResult.failedExpectations) || []; diff --git a/lib/jasmine-core/jasmine.css b/lib/jasmine-core/jasmine.css index 180ec354..9f223563 100644 --- a/lib/jasmine-core/jasmine.css +++ b/lib/jasmine-core/jasmine.css @@ -162,8 +162,12 @@ body { display: block; color: #eee; } +.jasmine_html-reporter .jasmine-bar.jasmine-in-progress { + color: #333; +} .jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; + color: #eee; border-bottom: 1px solid #eee; } .jasmine_html-reporter .jasmine-bar.jasmine-passed { diff --git a/spec/html/HtmlReporterV2Spec.js b/spec/html/HtmlReporterV2Spec.js index 788c6e9c..d5e0b70f 100644 --- a/spec/html/HtmlReporterV2Spec.js +++ b/spec/html/HtmlReporterV2Spec.js @@ -1132,7 +1132,74 @@ describe('HtmlReporterV2', function() { }); }); - describe('The overall result bar', function() { + describe('The overall status bar', function() { + describe('Before the jasmineDone event fires', function() { + describe('When nothing has failed', function() { + it('shows "Running..." and the has class jasmine-in-progress', function() { + const reporter = setup(); + reporter.initialize(); + const alertBar = container.querySelector('.jasmine-overall-result'); + + expect(alertBar.textContent).toEqual('Running...'); + expect(alertBar).not.toHaveClass('jasmine-passed'); + expect(alertBar).not.toHaveClass('jasmine-failed'); + expect(alertBar).toHaveClass('jasmine-in-progress'); + + for (const status of ['passed', 'excluded', 'pending']) { + reporter.specDone({ + status, + fullName: `Some ${status} spec`, + passedExpectations: [], + failedExpectations: [] + }); + } + + expect(alertBar.textContent).toEqual('Running...'); + expect(alertBar).not.toHaveClass('jasmine-passed'); + expect(alertBar).not.toHaveClass('jasmine-failed'); + expect(alertBar).toHaveClass('jasmine-in-progress'); + }); + }); + + describe('When a spec has failed', function() { + it('shows "Failing..." and the has class jasmine-failed', function() { + const reporter = setup(); + reporter.initialize(); + const alertBar = container.querySelector('.jasmine-overall-result'); + + reporter.specDone({ + status: 'failed', + fullName: 'Some failed spec', + passedExpectations: [], + failedExpectations: [] + }); + + expect(alertBar.textContent).toEqual('Failing...'); + expect(alertBar).toHaveClass('jasmine-failed'); + expect(alertBar).not.toHaveClass('jasmine-passed'); + }); + }); + + describe('When a suite has failed', function() { + it('shows "Failing..." and the has class jasmine-failed', function() { + const reporter = setup(); + reporter.initialize(); + const alertBar = container.querySelector('.jasmine-overall-result'); + + reporter.suiteDone({ + status: 'failed', + fullName: 'Some failed suite', + passedExpectations: [], + failedExpectations: [] + }); + + expect(alertBar.textContent).toEqual('Failing...'); + expect(alertBar).toHaveClass('jasmine-failed'); + expect(alertBar).not.toHaveClass('jasmine-passed'); + }); + }); + }); + describe("When the jasmineDone event's overallStatus is 'passed'", function() { it('has class jasmine-passed', function() { const reporter = setup(); diff --git a/src/html/HtmlReporterV2.js b/src/html/HtmlReporterV2.js index f1b9b552..468f8224 100644 --- a/src/html/HtmlReporterV2.js +++ b/src/html/HtmlReporterV2.js @@ -30,6 +30,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { // Sub-views #alerts; + #statusBar; #progress; #banner; #failures; @@ -64,6 +65,9 @@ jasmineRequire.HtmlReporterV2 = function(j$) { this.#stateBuilder = new j$.private.ResultsStateBuilder(); this.#alerts = new j$.private.AlertsView(this.#urlBuilder); + this.#statusBar = new j$.private.OverallStatusBar(this.#urlBuilder); + this.#statusBar.showRunning(); + this.#alerts.addBar(this.#statusBar.rootEl); this.#progress = new ProgressView(); this.#banner = new j$.private.Banner( this.#queryString.navigateWithNewParam.bind(this.#queryString), @@ -96,6 +100,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { if (result.status === 'failed') { this.#failures.append(result, this.#stateBuilder.currentParent); + this.#statusBar.showFailing(); } } @@ -116,6 +121,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { if (result.status === 'failed') { this.#failures.append(result, this.#stateBuilder.currentParent); + this.#statusBar.showFailing(); } } @@ -134,9 +140,7 @@ jasmineRequire.HtmlReporterV2 = function(j$) { ); } - const statusBar = new j$.private.OverallStatusBar(this.#urlBuilder); - statusBar.showDone(doneResult, this.#stateBuilder); - this.#alerts.addBar(statusBar.rootEl); + this.#statusBar.showDone(doneResult, this.#stateBuilder); if (doneResult.failedExpectations) { for (const f of doneResult.failedExpectations) { diff --git a/src/html/OverallStatusBar.js b/src/html/OverallStatusBar.js index 2c9a1e7c..e489f810 100644 --- a/src/html/OverallStatusBar.js +++ b/src/html/OverallStatusBar.js @@ -2,6 +2,7 @@ jasmineRequire.OverallStatusBar = function(j$) { 'use strict'; const { createDom } = j$.private.htmlReporterUtils; + const staticClassNames = 'jasmine-overall-result jasmine-bar'; class OverallStatusBar { #urlBuilder; @@ -9,11 +10,25 @@ jasmineRequire.OverallStatusBar = function(j$) { constructor(urlBuilder) { this.#urlBuilder = urlBuilder; this.rootEl = createDom('span', { - className: 'jasmine-overall-result jasmine-bar' + className: staticClassNames, + 'aria-live': 'polite' }); } + showRunning() { + this.rootEl.textContent = 'Running...'; + this.rootEl.classList.add('jasmine-in-progress'); + } + + showFailing() { + this.rootEl.textContent = 'Failing...'; + this.rootEl.classList.add('jasmine-failed'); + } + showDone(doneResult, stateBuilder) { + // Clear any classes added to represent in-progress state + this.rootEl.className = staticClassNames; + let statusBarMessage = ''; const globalFailures = (doneResult && doneResult.failedExpectations) || []; diff --git a/src/html/_HTMLReporter.scss b/src/html/_HTMLReporter.scss index a742b9e2..cb5e738c 100644 --- a/src/html/_HTMLReporter.scss +++ b/src/html/_HTMLReporter.scss @@ -233,8 +233,13 @@ body { display: block; color: #eee; + &.jasmine-in-progress { + color: $text-color; + } + &.jasmine-failed, &.jasmine-errored { background-color: $failing-color; + color: #eee; // Override jasmine-in-progress border-bottom: 1px solid $page-background-color; }