Skip to content

Commit

Permalink
first working TAP output
Browse files Browse the repository at this point in the history
  • Loading branch information
izelnakri committed Feb 20, 2021
1 parent 91e319c commit faaa56a
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 46 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ Experimental improvements, suggestions for qunit CLI

![Qunitx terminal output](https://raw.githubusercontent.com/izelnakri/qunitx/main/docs/qunitx-help-stdout.png)

Default test output is TAP(Test-Anything-Protocol_) thus you can use any tap reporter of your choice to display test
output in anyway you like. Example:

```
# using it with tap-difflet TAP reporter:
qunitx tests/attachments tests/user | npx tap-difflet
```

#### Installation:

```sh
Expand Down
2 changes: 1 addition & 1 deletion cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'fs/promises';
import chalk from 'chalk';
import displayHelpOutput from './lib/commands/help.js';
import run from './lib/commands/run.js';
import parseCliInputs from './lib/parse-cli-inputs.js';
import parseCliInputs from './lib/utils/parse-cli-inputs.js';

process.title = 'qunitx';

Expand Down
43 changes: 21 additions & 22 deletions lib/commands/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,34 @@ import fs from 'fs/promises';
import chalk from 'chalk';

export default async function(config) {
const { browser, fileOrFolderInputs } = fileOrFolderInputs;
const { browser, fileOrFolderInputs } = config;

if (!browser) {
const QUnit = (await import('./lib/setup-node-js-environment.js')).default;
}

fileOrFolderInputs.forEach((fileOrFolder) => {
// TODO: handle/format globs
const QUnit = (await import('../setup-node-js-environment.js')).default;

try {
const entry = await fs.stat(fileOrFolder);
fileOrFolderInputs.forEach(async (fileOrFolder, index) => {
// TODO: handle/format globs

if (entry.isDirectory()) {
console.log(entry, ' is directory');
} else if (entry.isFile()) { // what to do when its .ts
const QUnit = (await import('./lib/setup-node-js-environment.js')).default;
try {
const entry = await fs.stat(fileOrFolder);

console.log(fileOrFolder, 'if file');
await import(`${process.cwd()}/${fileOrFolder}`);
if (entry.isDirectory()) {
console.log(entry, ' is directory');
} else if (entry.isFile()) { // handle what to do when its .ts
await import(`${process.cwd()}/${fileOrFolder}`);
}
} catch (error) {
console.log(error);

return process.exit(1);
}
} catch (error) {
console.log(error);

return process.exit(1);
}
});

if (!browser) {
QUnit.start();
if (index === (fileOrFolderInputs.length - 1)) {
if (!browser) {
console.log('TAP version 13');
QUnit.start();
}
}
});
}
}
206 changes: 188 additions & 18 deletions lib/setup-node-js-environment.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import chalk from 'chalk';
import QUnit from '../index.js';
import yaml from 'js-yaml'
import indentString from 'indent-string';

let testCount = 0;
let failCount = 0;
let skipCount = 0;
let passCount = 0;

global.window = global;

Expand All @@ -13,33 +20,196 @@ window.QUNIX_TEST_TIME_COUNTER = (function() { // NOTE: might be needed for fail
};
})();

window.QUnit.moduleStart((details) => {
console.log('[', details.name, ']');
});
// tape TAP output: ['operator', 'stack', 'at', 'expected', 'actual']
// ava TAP output: ['message', 'name', 'at', 'assertion', 'values'] // Assertion #5, message
window.QUnit.on('testEnd', (details) => { // NOTE: https://github.com/qunitjs/qunit/blob/master/src/html-reporter/diff.js
testCount++;

if (details.status === 'skipped') {
skipCount++;
console.log(`ok ${testCount}`, details.fullName.join(' | '), '# skip');
} else if (details.status === 'todo') {
console.log(`not ok ${testCount}`, details.fullName.join(' | '), '# skip');
} else if (details.status === 'failed') {
passCount++;
console.log(`not ok ${testCount}`, details.fullName.join(' | '));
details.assertions.reduce((errorCount, assertion, index) => {
if (!assertion.passed && assertion.todo === false) {
errorCount++;
let stack = assertion.stack.match(/\(.+\)/g);

console.log(' ---');
console.log(indentString(yaml.safeDump({
name: `Assertion #${index + 1}`,
actual: assertion.actual || null,
expected: assertion.expected || null,
message: assertion.message || null,
stack: assertion.stack || null,
at: stack ? stack[0].replace('(file://', '').replace(')', '') : null
}).trim(), 4));
console.log(' ...');
}

return errorCount;
}, 0);
} else if (details.status === 'passed') {
passCount++;
console.log(`ok ${testCount}`, details.fullName.join(' | '));
}

window.QUnit.testDone((details) => {
console.log(getTestStatusCode(details), details.module, details.name);
});

window.QUnit.done((details) => {
console.log('DONE DETAILS');
console.log(details);
window.QUNIT_RESULT = Object.assign(details, {
timeTaken: window.QUNIX_TEST_TIME_COUNTER.stop()
});
console.log(details.timeTaken); // runtime
displayResults(details.timeTaken);
// console.log(details.timeTaken); // runtime
});

export default window.QUnit;

function getTestStatusCode(details) {
if (details.failed > 0) {
return chalk.red('NOT OK -');
} else if (details.skipped) {
return chalk.blue('SKIPPED -');
} else if (details.todo) {
return chalk.yellow('TODO -');
} else if (details.passed > 0) {
return chalk.green('OK -');
}
function displayResults(timeTaken) {
console.log('');
console.log(`1..${testCount}`);
console.log(`# tests ${testCount}`);
console.log(`# pass ${passCount}`);
console.log(`# skip ${skipCount}`);
console.log(`# fail ${failCount}`);

// let seconds = timeTaken > 1000 ? Math.floor(timeTaken / 1000) : 0;
// let milliseconds = timeTaken % 100;

console.log(`# duration ${timeTaken}`);
console.log('');
}

// not ok 1 - t true works
// ---
// name: AssertionError
// message: Error thrown in test
// values:
// 'Error thrown in test:': |-
// TypeError {
// message: 't.expect is not a function',
// }
// at: 'test/failing-test.js:4:5'
// ...
// not ok 2 - deepEqual true works
// ---
// name: AssertionError
// assertion: deepEqual
// values:
// 'Difference:': |2-
// {
// - firstName: 'Izel',
// + firstName: 'Isaac',
// lastName: 'Nakri',
// }
// at: 'test/failing-test.js:34:5'
// ...
// not ok 3 - async test finishes
// ---
// name: AssertionError
// message: Rejected promise returned by test
// values:
// 'Rejected promise returned by test. Reason:': |-
// TypeError {
// message: 't.expect is not a function',
// }
// at: 'test/failing-test.js:11:5'
// ...


// ---
// operator: error
// stack: |-
// Error: lol
// at Test.<anonymous> (/home/izelnakri/ava-test/tape.js:18:9)
// ...

// ---
// operator: ok
// expected: true
// actual: false
// at: Test.<anonymous> (/home/izelnakri/ava-test/tape.js:19:9)
// stack: |-
// Error: should be truthy
// at Test.assert [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:269:54)
// at Test.bound [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.assert (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:388:10)
// at Test.bound [as true] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.<anonymous> (/home/izelnakri/ava-test/tape.js:19:9)
// ...
// ok 5 should be strictly equal
// ok 6 should be strictly equal
// after wait
// not ok 7 should be strictly equal
// ---
// operator: equal
// expected: true
// actual: false
// at: Test.<anonymous> (/home/izelnakri/ava-test/tape.js:25:5)
// stack: |-
// Error: should be strictly equal
// at Test.assert [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:269:54)
// at Test.bound [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.strictEqual (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:433:10)
// at Test.bound [as is] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.<anonymous> (/home/izelnakri/ava-test/tape.js:25:5)
// ...
// not ok 8 plan != count
// ---
// operator: fail
// expected: 5
// actual: 4
// stack: |-
// Error: plan != count
// at Test.assert [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:269:54)
// at Test.bound [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.fail (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:363:10)
// at Test.bound [as fail] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test._end (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:209:14)
// at Test.bound [as _end] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.end (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:190:10)
// at Test.bound [as end] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at onResolve (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:117:22)
// ...
// # deepEqual true works
// not ok 9 should be deeply equivalent
// ---
// operator: deepEqual
// expected: |-
// { firstName: 'Isaac', lastName: 'Nakri' }
// actual: |-
// { firstName: 'Izel', lastName: 'Nakri' }
// at: Test.<anonymous> (/home/izelnakri/ava-test/tape.js:31:5)
// stack: |-
// Error: should be deeply equivalent
// at Test.assert [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:269:54)
// at Test.bound [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.tapeDeepEqual (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:510:10)
// at Test.bound [as deepEqual] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.<anonymous> (/home/izelnakri/ava-test/tape.js:31:5)
// at Test.bound [as _cb] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.run (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:107:31)
// at Test.bound [as run] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Immediate.next [as _onImmediate] (/home/izelnakri/ava-test/node_modules/tape/lib/results.js:89:19)
// at processImmediate (node:internal/timers:463:21)
// ...
// not ok 10 test exited without ending: deepEqual true works
// ---
// operator: fail
// at: process.<anonymous> (/home/izelnakri/ava-test/node_modules/tape/index.js:85:19)
// stack: |-
// Error: test exited without ending: deepEqual true works
// at Test.assert [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:269:54)
// at Test.bound [as _assert] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test.fail (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:363:10)
// at Test.bound [as fail] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at Test._exit (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:226:14)
// at Test.bound [as _exit] (/home/izelnakri/ava-test/node_modules/tape/lib/test.js:90:32)
// at process.<anonymous> (/home/izelnakri/ava-test/node_modules/tape/index.js:85:19)
// at process.emit (node:events:376:20)
// ...

8 changes: 8 additions & 0 deletions lib/utils/count-time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function() {
const startTime = new Date();

return {
startTime: startTime,
stop: () => +(new Date()) - (+startTime)
};
}
11 changes: 6 additions & 5 deletions lib/utils/parse-cli-input.js → lib/utils/parse-cli-inputs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'fs/promises';
import findProjectRoot from './find-project-root.js';

// { fileOrFolderInputs: [], projectRoot: '', browser: true, debug: true, watch: true, failFast: true, reporter: '', coverage: true }
export default async function() {
const projectRoot = await findProjectRoot();
const packageJSON = await fs.readFile(`${projectRoot}/package.json`);
Expand All @@ -9,15 +10,15 @@ export default async function() {
const providedFlags = process.argv.slice(2).reduce((result, arg) => {
if (arg.startsWith('--browser')) {
return Object.assign(result, { browser: parseBoolean(arg.split('=')[1]) });
} else if (args.startsWith('--debug')) {
} else if (arg.startsWith('--debug')) {
return Object.assign(result, { debug: parseBoolean(arg.split('=')[1]) });
} else if (args.startsWith('--watch')) {
} else if (arg.startsWith('--watch')) {
return Object.assign(result, { watch: parseBoolean(arg.split('=')[1]) });
} else if (args.startsWith('--failfast') || args.startsWith('--failFast')) {
} else if (arg.startsWith('--failfast') || arg.startsWith('--failFast')) {
return Object.assign(result, { failFast: parseBoolean(arg.split('=')[1]) });
} else if (args.startsWith('--reporter') {
} else if (arg.startsWith('--reporter')) {
return Object.assign(result, { reporter: arg.split('=')[1] || 'spec' });
} else if (args.startsWith('--coverage')) {
} else if (arg.startsWith('--coverage')) {
return Object.assign(result, { coverageDist: arg.split('=')[1] || './dist' });
}

Expand Down

0 comments on commit faaa56a

Please sign in to comment.