package/LICENSE 000644 0000002124 3560116604 010264 0 ustar 00 000000 000000 Copyright (c) 2011-2019 Pivotal Labs
Copyright (c) 2011-2026 The Jasmine developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/lib/console_reporter.js 000644 0000026201 3560116604 013751 0 ustar 00 000000 000000 const util = require('util');
const ansi = {
green: '\x1B[32m',
red: '\x1B[31m',
yellow: '\x1B[33m',
none: '\x1B[0m',
};
/**
* @classdesc A reporter that prints spec and suite results to the console.
*
* @constructor
* @example
* const {ConsoleReporter} = require('@jasminejs/reporters');
* const reporter = new ConsoleReporter();
*/
class ConsoleReporter {
#print;
#isTTY;
#noColorEnvSet;
#configuredToShowColors;
#alwaysListPendingSpecs;
#listNotApplicableSpecs;
#stackFilter;
#failedSpecs;
#pendingSpecs;
#notApplicableSpecs;
#failedSuites;
// Options is intended for focused integration tests that cover
// ConsoleReporter. Production code shoudln't need to pass it.
// TODO: Document the options arg?
constructor(options) {
const stdout = options?.stdout ?? process.stdout;
this.#print = function() {
stdout.write(util.format.apply(undefined, arguments));
};
this.#isTTY = stdout.isTTY ?? false;
const noColor = (options?.env ?? process.env).NO_COLOR;
this.#noColorEnvSet = !!noColor;
this.#configuredToShowColors = undefined;
this.#alwaysListPendingSpecs = true;
this.#stackFilter = stack => stack;
this.#failedSpecs = [];
this.#pendingSpecs = [];
this.#notApplicableSpecs = [];
this.#failedSuites = [];
this.reporterCapabilities = { parallel: true };
}
/**
* Configures the reporter.
* @function
* @name ConsoleReporter#configure
* @param {ConsoleReporterOptions} options
*/
configure(options) {
if (options.print) {
this.#print = options.print;
}
/**
* @interface ConsoleReporterOptions
*/
/**
* Whether to colorize the output
*
* The color option should only be specified if the user has explicitly
* requested to enable or disable color. If set to something other than
* undefined, it takes precedence over the NO_COLOR environment variable.
*
* The color option is ignored if stdout is not a TTY.
* @name ConsoleReporterOptions#color
* @type Boolean|undefined
* @default undefined
*/
if (options.color !== undefined) {
this.#configuredToShowColors = options.color;
}
if (options.stackFilter) {
this.#stackFilter = options.stackFilter;
}
/**
* A function that takes a random seed and returns the command to reproduce
* that seed. Use this to customize the output when using ConsoleReporter
* in a different command line tool.
* @name ConsoleReporterOptions#randomSeedReproductionCmd
* @type Function|undefined
*/
if (options.randomSeedReproductionCmd) {
this.randomSeedReproductionCmd = options.randomSeedReproductionCmd;
}
/**
* Whether to list pending specs even if there are failures.
* @name ConsoleReporterOptions#this.#alwaysListPendingSpecs
* @type Boolean|undefined
* @default true
*/
if (options.alwaysListPendingSpecs !== undefined) {
this.#alwaysListPendingSpecs = options.alwaysListPendingSpecs;
}
/**
* Whether to list each spec that was marked not applicable rather than
* just summarizing them.
@name ConsoleReporterOptions#this.#listNotApplicableSpecs
@type Boolean|undefined
@default false
*/
if (options.listNotApplicableSpecs !== undefined) {
this.#listNotApplicableSpecs = options.listNotApplicableSpecs;
}
}
jasmineStarted(options) {
this._specCount = 0;
this._executableSpecCount = 0;
this._failureCount = 0;
if (options && options.order && options.order.random) {
this.#print('Randomized with seed ' + options.order.seed);
this.#printNewline();
}
this.#print('Started');
this.#printNewline();
}
jasmineDone(result) {
if (result.failedExpectations) {
this._failureCount += result.failedExpectations.length;
}
this.#printNewline();
this.#printNewline();
if (this.#failedSpecs.length > 0) {
this.#print('Failures:');
}
for (let i = 0; i < this.#failedSpecs.length; i++) {
this.#specFailureDetails(this.#failedSpecs[i], i + 1);
}
for (let i = 0; i < this.#failedSuites.length; i++) {
this.#suiteFailureDetails(this.#failedSuites[i]);
}
if (
result &&
result.failedExpectations &&
result.failedExpectations.length > 0
) {
this.#suiteFailureDetails({ fullName: 'top suite', ...result });
}
if (this.#alwaysListPendingSpecs || result.overallStatus === 'passed') {
if (this.#pendingSpecs.length > 0) {
this.#print('Pending:');
}
for (let i = 0; i < this.#pendingSpecs.length; i++) {
this.#pendingSpecDetails(this.#pendingSpecs[i], i + 1);
}
}
if (this.#notApplicableSpecs.length > 0) {
const byReason = {};
for (const spec of this.#notApplicableSpecs) {
const r = spec.notApplicableReason;
const n = spec.fullName;
if (byReason[r]) {
byReason[r].push(n);
} else {
byReason[r] = [n];
}
}
this.#printNewline();
for (const [reason, specNames] of Object.entries(byReason)) {
if (specNames.length === 1) {
this.#print('1 spec');
} else {
this.#print(`${specNames.length} specs`);
}
this.#print(` not applicable because: ${reason}`);
this.#printNewline();
if (this.#listNotApplicableSpecs) {
for (const n of specNames) {
this.#print(` * ${n}`);
this.#printNewline();
}
}
}
}
if (this._specCount > 0) {
this.#printNewline();
if (this._executableSpecCount !== this._specCount) {
this.#print(
'Ran ' +
this._executableSpecCount +
' of ' +
this._specCount +
plural(' spec', this._specCount)
);
this.#printNewline();
}
let specCounts =
this._executableSpecCount +
' ' +
plural('spec', this._executableSpecCount) +
', ' +
this._failureCount +
' ' +
plural('failure', this._failureCount);
if (this.#pendingSpecs.length) {
specCounts +=
', ' +
this.#pendingSpecs.length +
' pending ' +
plural('spec', this.#pendingSpecs.length);
}
this.#print(specCounts);
} else {
this.#print('No specs found');
}
this.#printNewline();
if (result.numWorkers) {
this.#print('Ran in parallel with ' + result.numWorkers + ' workers');
this.#printNewline();
}
const seconds = result ? result.totalTime / 1000 : 0;
this.#print('Finished in ' + seconds + ' ' + plural('second', seconds));
this.#printNewline();
if (result && result.overallStatus === 'incomplete') {
this.#print('Incomplete: ' + result.incompleteReason);
this.#printNewline();
}
if (result && result.order && result.order.random) {
this.#print('Randomized with seed ' + result.order.seed);
if (this.randomSeedReproductionCmd) {
this.#print(
' (' + this.randomSeedReproductionCmd(result.order.seed) + ')'
);
}
this.#printNewline();
}
}
specDone(result) {
this._specCount++;
switch (result.status) {
case 'pending':
this.#pendingSpecs.push(result);
this._executableSpecCount++;
this.#print(this.#colored('yellow', '*'));
break;
case 'notApplicable':
this.#notApplicableSpecs.push(result);
this._executableSpecCount++;
break;
case 'passed':
this._executableSpecCount++;
this.#print(this.#colored('green', '.'));
break;
case 'failed':
this._failureCount++;
this.#failedSpecs.push(result);
this._executableSpecCount++;
this.#print(this.#colored('red', 'F'));
break;
}
}
suiteDone(result) {
if (result.failedExpectations && result.failedExpectations.length > 0) {
this._failureCount++;
this.#failedSuites.push(result);
}
}
#printNewline() {
this.#print('\n');
}
#colored(color, str) {
return this.#shouldColorize() ? ansi[color] + str + ansi.none : str;
}
#shouldColorize() {
if (this.#configuredToShowColors !== undefined) {
return this.#configuredToShowColors;
} else {
return this.#isTTY && !this.#noColorEnvSet;
}
}
#specFailureDetails(result, failedSpecNumber) {
this.#printNewline();
this.#print(failedSpecNumber + ') ');
this.#print(result.fullName);
this.#printFailedExpectations(result);
if (result.debugLogs) {
this.#printNewline();
this.#print(indent('Debug logs:', 2));
this.#printNewline();
for (const entry of result.debugLogs) {
this.#print(indent(`${entry.timestamp}ms: ${entry.message}`, 4));
this.#printNewline();
}
}
}
#suiteFailureDetails(result) {
this.#printNewline();
this.#print('Suite error: ' + result.fullName);
this.#printFailedExpectations(result);
}
#printFailedExpectations(result) {
for (let i = 0; i < result.failedExpectations.length; i++) {
const failedExpectation = result.failedExpectations[i];
this.#printNewline();
this.#print(indent('Message:', 2));
this.#printNewline();
this.#print(this.#colored('red', indent(failedExpectation.message, 4)));
this.#printNewline();
this.#print(indent('Stack:', 2));
this.#printNewline();
this.#print(indent(this.#stackFilter(failedExpectation.stack), 4));
}
// When failSpecWithNoExpectations = true and a spec fails because of no expectations found,
// jasmine-core reports it as a failure with no message.
//
// Therefore we assume that when there are no failed or passed expectations,
// the failure was because of our failSpecWithNoExpectations setting.
//
// Same logic is used by jasmine.HtmlReporter, see https://github.com/jasmine/jasmine/blob/main/src/html/HtmlReporter.js
if (
result.failedExpectations.length === 0 &&
result.passedExpectations.length === 0
) {
this.#printNewline();
this.#print(indent('Message:', 2));
this.#printNewline();
this.#print(this.#colored('red', indent('Spec has no expectations', 4)));
}
this.#printNewline();
}
#pendingSpecDetails(result, pendingSpecNumber) {
this.#printNewline();
this.#printNewline();
this.#print(pendingSpecNumber + ') ');
this.#print(result.fullName);
this.#printNewline();
let pendingReason = 'No reason given';
if (result.pendingReason && result.pendingReason !== '') {
pendingReason = result.pendingReason;
}
this.#print(indent(this.#colored('yellow', pendingReason), 2));
this.#printNewline();
}
}
function plural(str, count) {
return count === 1 ? str : str + 's';
}
function repeat(thing, times) {
const arr = [];
for (let i = 0; i < times; i++) {
arr.push(thing);
}
return arr;
}
function indent(str, spaces) {
const lines = (str || '').split('\n');
const newArr = [];
for (let i = 0; i < lines.length; i++) {
newArr.push(repeat(' ', spaces).join('') + lines[i]);
}
return newArr.join('\n');
}
module.exports = exports = ConsoleReporter;
package/index.js 000644 0000000117 3560116604 010724 0 ustar 00 000000 000000 module.exports = {
ConsoleReporter: require('./lib/console_reporter.js'),
};
package/package.json 000644 0000002402 3560116604 011544 0 ustar 00 000000 000000 {
"name": "@jasminejs/reporters",
"license": "MIT",
"version": "1.1.0",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/reporters.git"
},
"keywords": [
"test",
"testing",
"jasmine",
"tdd",
"bdd",
"reporter"
],
"scripts": {
"lint": "eslint && prettier --check index.js \"lib/**/*.js\" \"spec/**/*.js\"",
"test": "jasmine && npm run lint",
"cleanup": "prettier --write index.js \"lib/**/*.js\" \"spec/**/*.js\""
},
"description": "Reporters for the Jasmine testing framework",
"homepage": "https://jasmine.github.io",
"main": "./index.js",
"exports": {
".": "./index.js",
"./console": "./lib/console_reporter.js"
},
"files": [
"LICENSE",
"README.md",
"index.js",
"lib/**/*.js",
"package.json"
],
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.24.0",
"eslint": "^9.24.0",
"eslint-plugin-compat": "^6.0.2",
"eslint-plugin-jasmine": "^4.2.2",
"jasmine": "github:jasmine/jasmine-npm#main",
"prettier": "1.17.1"
},
"browserslist": [
"Safari >= 16",
"Firefox >= 102",
"last 2 Chrome versions",
"last 2 Edge versions"
],
"prettier": {
"singleQuote": true,
"trailingComma": "es5"
}
}
package/README.md 000644 0000001573 3560116604 010545 0 ustar 00 000000 000000 # @jasminejs/reporters
This package contains the console reporter used by the `jasmine` and
`jasmine-browser-runner` packages. Other reporters may be added in the future.
## Usage
If you're using `jasmine` or `jasmine-browser-runner`, you don't need to do
anything. `ConsoleReporter` will automatically be set up. To use it without
either of those packages:
```javascript
import ConsoleReporter from '@jasminejs/reporters/console';
const reporter = new ConsoleReporter();
jasmine.getEnv().addReporter(reporter);
```
## Node version compatibility
20*, 22, 24, 26
\* Environments that are past end of life are supported on a best-effort basis.
They may be dropped in a future minor release of Jasmine if continued support
becomes impractical.
Copyright (c) 2011-2019 Pivotal Labs
Copyright (c) 2011-2026 The Jasmine developers
This software is licensed under the MIT License.