1 import chalk from "chalk";
2 import { getBrowserString } from "./lib/getBrowserString.js";
3 import { prettyMs } from "./lib/prettyMs.js";
4 import * as Diff from "diff";
6 function serializeForDiff( value ) {
8 // Use naive serialization for everything except types with confusable values
9 if ( typeof value === "string" ) {
10 return JSON.stringify( value );
12 if ( typeof value === "bigint" ) {
18 export function reportTest( test, reportId, { browser, headless } ) {
19 if ( test.status === "passed" ) {
21 // Write to console without newlines
22 process.stdout.write( "." );
26 let message = `${ chalk.bold( `${ test.suiteName }: ${ test.name }` ) }`;
27 message += `\nTest ${ test.status } on ${ chalk.yellow(
28 getBrowserString( browser, headless )
29 ) } (${ chalk.bold( reportId ) }).`;
31 // test.assertions only contains passed assertions;
32 // test.errors contains all failed asssertions
33 if ( test.errors.length ) {
34 for ( const error of test.errors ) {
36 if ( error.message ) {
37 message += `\n${ error.message }`;
39 message += `\n${ chalk.gray( error.stack ) }`;
41 // Show expected and actual values
42 // if either is defined and non-null.
43 // error.actual is set to null for failed
44 // assert.expect() assertions, so skip those as well.
45 // This should be fine because error.expected would
46 // have to also be null for this to be skipped.
47 if ( error.expected != null || error.actual != null ) {
48 message += `\nexpected: ${ chalk.red( JSON.stringify( error.expected ) ) }`;
49 message += `\nactual: ${ chalk.green( JSON.stringify( error.actual ) ) }`;
52 if ( Array.isArray( error.expected ) && Array.isArray( error.actual ) ) {
55 diff = Diff.diffArrays( error.expected, error.actual );
57 typeof error.expected === "object" &&
58 typeof error.actual === "object"
62 diff = Diff.diffJson( error.expected, error.actual );
64 typeof error.expected === "number" &&
65 typeof error.actual === "number"
68 // Diff numbers directly
69 const value = error.actual - error.expected;
71 diff = [ { added: true, value: `+${ value }` } ];
73 diff = [ { removed: true, value: `${ value }` } ];
76 typeof error.expected === "string" &&
77 typeof error.actual === "string"
80 // Diff the characters of strings
81 diff = Diff.diffChars( error.expected, error.actual );
84 // Diff everything else as words
85 diff = Diff.diffWords(
86 serializeForDiff( error.expected ),
87 serializeForDiff( error.actual )
96 return chalk.green( part.value );
99 return chalk.red( part.value );
101 return chalk.gray( part.value );
109 console.log( `\n\n${ message }` );
111 // Only return failed messages
112 if ( test.status === "failed" ) {
117 export function reportEnd( result, reportId, { browser, headless, modules } ) {
118 const fullBrowser = getBrowserString( browser, headless );
120 `\n\nTests finished in ${ prettyMs( result.runtime ) } ` +
121 `for ${ chalk.yellow( modules.join( "," ) ) } ` +
122 `in ${ chalk.yellow( fullBrowser ) } (${ chalk.bold( reportId ) })...`
125 ( result.status !== "passed" ?
126 `${ chalk.red( result.testCounts.failed ) } failed. ` :
128 `${ chalk.green( result.testCounts.total ) } passed. ` +
129 `${ chalk.gray( result.testCounts.skipped ) } skipped.`
131 return result.testCounts;