Skip to content

Commit

Permalink
Add initial assert.step, expect & async support
Browse files Browse the repository at this point in the history
  • Loading branch information
izelnakri committed Jul 27, 2023
1 parent 53a71b1 commit ef2aa00
Show file tree
Hide file tree
Showing 2 changed files with 303 additions and 86 deletions.
194 changes: 157 additions & 37 deletions shims/node/assert.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,145 @@
import QUnit from '../../vendor/qunit.js';
import { objectValues, objectValuesSubset, validateExpectedExceptionArgs, validateException } from '../shared/index.js';
import assert, { AssertionError } from 'node:assert';
import assert, { AssertionError as _AssertionError } from 'node:assert';
import util from 'node:util';

// NOTE: Maybe do the expect, steps in some object, and also do timeout and async(?)
export default {
_steps: [],
timeout() {
return true; // NOTE: NOT implemented
},
step(value = '') {
this._steps.push(value);
},
export const AssertionError = _AssertionError;

// const STEP_ERROR = { result: false, message: 'You must provide a string or object to assert.step()' };

// More: contexts needed for timeout
// NOTE: QUnit API provides assert on hooks, which makes it hard to make it concurrent
// NOTE: Another approach for a global report Make this._assertions.set(this.currentTest, (this._assertions.get(this.currentTest) || 0) + 1); for pushResult
// NOTE: This should *always* be a singleton(?), passed around as an argument for hooks. Seems difficult with concurrency. Singleton needs to be a concurrent data structure.

export default class Assert {
AssertionError = _AssertionError;

#asyncOps = [];

constructor(module, test) {
this.module = module;
this.test = test;
}
_incrementAssertionCount() {
if (this.test) {
this.test.totalExecutedAssertions++;
} else {
this.module.tests.forEach(test => {
test.totalExecutedAssertions++;
});
}
}
// _applyToContext(func) {
// if (this.test) {
// return func(this.test);
// } else {
// return this.module.tests.map(func);
// }
// },
timeout(number) {
if (!Number.isInteger(number) || number < 0) {
throw new Error('assert.timeout() expects a positive integer.');
}

if (this.test) {
this.test.timeout = number;
} else {
this.module.tests.forEach(test => {
test.timeout = number;
});
}
}
step(message) {
let assertionMessage = message;
let result = !!message;

if (this.test) {
this.test.steps.push(message);
} else {
this.module.tests.forEach(test => {
test.steps.push(message);
});
}

if (typeof message === 'undefined' || message === '') {
assertionMessage = 'You must provide a message to assert.step';
} else if (typeof message !== 'string') {
assertionMessage = 'You must provide a string value to assert.step';
result = false;
}

this.pushResult({
result,
message: assertionMessage
});
}
verifySteps(steps, message = 'Verify steps failed!') {
const result = this.deepEqual(this._steps, steps, message);
// const actualStepsClone = this.module.test.steps.slice(); // TODO: is this needed(?)

this._steps.length = 0;
if (this.test) {
let result = this.deepEqual(this.test.steps, steps, message);
this.test.steps.length = 0;
} else {
this.module.tests.forEach(test => {
let result = this.deepEqual(test.steps, steps, message);
test.steps.length = 0;
});
}
}
expect(number) {
if (!Number.isInteger(number) || number < 0) {
throw new Error('assert.expect() expects a positive integer.');
}

return result;
},
expect() {
return () => {}; // NOTE: NOT implemented
},
if (this.test) {
this.test.expectedAssertionCount = number;
} else {
this.module.tests.forEach(test => {
test.expectedAssertionCount = number;
});
}
}
async() {
return () => {}; // NOTE: noop, node should have sanitizeResources
},
let resolveFn;
let done = new Promise(resolve => { resolveFn = resolve; });
this.#asyncOps.push(done);
return () => { resolveFn(); };

// let resolve;
// const promise = new Promise((_resolve) => {
// resolve = _resolve;
// });
// const doneFn = () => {
// resolve();
// };
// if (this.test) {
// this.test.asyncOp = promise;
// } else {
// this.module.tests.forEach(test => {
// test.asyncOp = promise;
// });
// }
// return doneFn;
}
async waitForAsyncOps() {
return Promise.all(this.#asyncOps);
}
pushResult(resultInfo = {}) {
if (!result) {
this._incrementAssertionCount();
if (!resultInfo.result) {
throw new AssertionError({
actual: resultInfo.actual,
expected: resultInfo.expected,
message: result.Infomessage || 'Custom assertion failed!',
message: resultInfo.message || 'Custom assertion failed!',
stackStartFn: this.pushResult,
});
}
},

return this;
}
ok(state, message) {
this._incrementAssertionCount();
if (!state) {
throw new AssertionError({
actual: state,
Expand All @@ -44,8 +148,9 @@ export default {
stackStartFn: this.ok,
});
}
},
}
notOk(state, message) {
this._incrementAssertionCount();
if (state) {
throw new AssertionError({
actual: state,
Expand All @@ -54,8 +159,9 @@ export default {
stackStartFn: this.notOk,
});
}
},
}
true(state, message) {
this._incrementAssertionCount();
if (state !== true) {
throw new AssertionError({
actual: state,
Expand All @@ -64,8 +170,9 @@ export default {
stackStartFn: this.true,
});
}
},
}
false(state, message) {
this._incrementAssertionCount();
if (state !== false) {
throw new AssertionError({
actual: state,
Expand All @@ -74,8 +181,9 @@ export default {
stackStartFn: this.false,
});
}
},
}
equal(actual, expected, message) {
this._incrementAssertionCount();
if (actual != expected) {
throw new AssertionError({
actual,
Expand All @@ -85,8 +193,9 @@ export default {
stackStartFn: this.equal,
});
}
},
}
notEqual(actual, expected, message) {
this._incrementAssertionCount();
if (actual == expected) {
throw new AssertionError({
actual,
Expand All @@ -96,8 +205,9 @@ export default {
stackStartFn: this.notEqual,
});
}
},
}
propEqual(actual, expected, message) {
this._incrementAssertionCount();
let targetActual = objectValues(actual);
let targetExpected = objectValues(expected);
if (!QUnit.equiv(targetActual, targetExpected)) {
Expand All @@ -108,8 +218,9 @@ export default {
stackStartFn: this.propEqual,
});
}
},
}
notPropEqual(actual, expected, message) {
this._incrementAssertionCount();
let targetActual = objectValues(actual);
let targetExpected = objectValues(expected);
if (QUnit.equiv(targetActual, targetExpected)) {
Expand All @@ -120,8 +231,9 @@ export default {
stackStartFn: this.notPropEqual,
});
}
},
}
propContains(actual, expected, message) {
this._incrementAssertionCount();
let targetActual = objectValuesSubset(actual, expected);
let targetExpected = objectValues(expected, false);
if (!QUnit.equiv(targetActual, targetExpected)) {
Expand All @@ -132,8 +244,9 @@ export default {
stackStartFn: this.propContains,
});
}
},
}
notPropContains(actual, expected, message) {
this._incrementAssertionCount();
let targetActual = objectValuesSubset(actual, expected);
let targetExpected = objectValues(expected);
if (QUnit.equiv(targetActual, targetExpected)) {
Expand All @@ -144,8 +257,9 @@ export default {
stackStartFn: this.notPropContains,
});
}
},
}
deepEqual(actual, expected, message) {
this._incrementAssertionCount();
if (!QUnit.equiv(actual, expected)) {
throw new AssertionError({
actual,
Expand All @@ -155,8 +269,9 @@ export default {
stackStartFn: this.deepEqual,
});
}
},
}
notDeepEqual(actual, expected, message) {
this._incrementAssertionCount();
if (QUnit.equiv(actual, expected)) {
throw new AssertionError({
actual,
Expand All @@ -166,8 +281,9 @@ export default {
stackStartFn: this.notDeepEqual,
});
}
},
}
strictEqual(actual, expected, message) {
this._incrementAssertionCount();
if (actual !== expected) {
throw new AssertionError({
actual,
Expand All @@ -177,8 +293,9 @@ export default {
stackStartFn: this.strictEqual,
});
}
},
}
notStrictEqual(actual, expected, message) {
this._incrementAssertionCount();
if (actual === expected) {
throw new AssertionError({
actual,
Expand All @@ -188,8 +305,10 @@ export default {
stackStartFn: this.notStrictEqual,
});
}
},
}
throws(blockFn, expectedInput, assertionMessage) {
// TODO: This probably happens on increse shit
this?._incrementAssertionCount();
let [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
if (typeof blockFn !== 'function') {
throw new AssertionError({
Expand Down Expand Up @@ -222,8 +341,9 @@ export default {
message: 'Function passed to `assert.throws` did not throw an exception!',
stackStartFn: this.throws,
});
},
}
async rejects(promise, expectedInput, assertionMessage) {
this._incrementAssertionCount();
let [expected, message] = validateExpectedExceptionArgs(expectedInput, assertionMessage, 'rejects');
let then = promise && promise.then;
if (typeof then !== 'function') {
Expand Down
Loading

0 comments on commit ef2aa00

Please sign in to comment.