From b6393b1d3fc9adc59bc387b8013cbad30e3a164f Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 28 Mar 2023 19:49:53 -0300 Subject: data steps: unfinished behavior & fixes in test lib --- src/util/sugar.js | 9 ++++- test/lib/content-function.js | 36 ++++++++--------- test/lib/generic-mock.js | 92 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 98 insertions(+), 39 deletions(-) diff --git a/src/util/sugar.js b/src/util/sugar.js index ad36d16b..f3b9e732 100644 --- a/src/util/sugar.js +++ b/src/util/sugar.js @@ -431,6 +431,7 @@ export function _withAggregate(mode, aggregateOpts, fn) { export function showAggregate(topError, { pathToFileURL = f => f, showTraces = true, + print = true, } = {}) { const recursive = (error, {level}) => { let header = showTraces @@ -475,7 +476,13 @@ export function showAggregate(topError, { } }; - console.error(recursive(topError, {level: 0})); + const message = recursive(topError, {level: 0}); + + if (print) { + console.error(message); + } else { + return message; + } } export function decorateErrorWithIndex(fn) { diff --git a/test/lib/content-function.js b/test/lib/content-function.js index 21af0e5a..a6ff64af 100644 --- a/test/lib/content-function.js +++ b/test/lib/content-function.js @@ -1,13 +1,19 @@ +import chroma from 'chroma-js'; +import * as path from 'path'; +import {fileURLToPath} from 'url'; + +import mock from './generic-mock.js'; import {quickEvaluate} from '../../src/content-function.js'; import {quickLoadContentDependencies} from '../../src/content/dependencies/index.js'; -import chroma from 'chroma-js'; -import * as html from '../../src/util/html.js'; import urlSpec from '../../src/url-spec.js'; +import * as html from '../../src/util/html.js'; +import {empty, showAggregate} from '../../src/util/sugar.js'; import {getColors} from '../../src/util/colors.js'; import {generateURLs} from '../../src/util/urls.js'; +import {processLanguageFile} from '../../src/data/language.js'; -import mock from './generic-mock.js'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); export function testContentFunctions(t, message, fn) { const urls = generateURLs(urlSpec); @@ -15,6 +21,7 @@ export function testContentFunctions(t, message, fn) { t.test(message, async t => { let loadedContentDependencies; + const language = await processLanguageFile('./src/strings-default.json'); const mocks = []; const evaluate = ({ @@ -38,6 +45,7 @@ export function testContentFunctions(t, message, fn) { }, extraDependencies: { html, + language, to, urls, appendIndexHTML: false, @@ -74,7 +82,7 @@ export function testContentFunctions(t, message, fn) { await fn(t, evaluate); - if (mocks.length) { + if (!empty(mocks)) { cleanCatchAggregate(() => { const errors = []; for (const {close} of mocks) { @@ -84,7 +92,7 @@ export function testContentFunctions(t, message, fn) { errors.push(error); } } - if (errors.length) { + if (!empty(errors)) { throw new AggregateError(errors, `Errors closing mocks`); } }); @@ -92,21 +100,13 @@ export function testContentFunctions(t, message, fn) { }); } -function cleanAggregate(error) { - if (error instanceof AggregateError) { - return new Error(`[AggregateError: ${error.message}\n${ - error.errors - .map(cleanAggregate) - .map(err => ` * ${err.message.split('\n').map((l, i) => (i > 0 ? ' ' + l : l)).join('\n')}`) - .join('\n')}]`); - } else { - return error; - } -} - function printAggregate(error) { if (error instanceof AggregateError) { - const {message} = cleanAggregate(error); + const message = showAggregate(error, { + showTraces: true, + print: false, + pathToFileURL: f => path.relative(path.join(__dirname, '../..'), fileURLToPath(f)), + }); for (const line of message.split('\n')) { console.error(line); } diff --git a/test/lib/generic-mock.js b/test/lib/generic-mock.js index 841ba462..2a346448 100644 --- a/test/lib/generic-mock.js +++ b/test/lib/generic-mock.js @@ -1,5 +1,7 @@ import {same} from 'tcompare'; +import {empty} from '../../src/util/sugar.js'; + export default function mock(callback) { const mocks = []; @@ -24,7 +26,7 @@ export default function mock(callback) { errors.push(error); } } - if (errors.length) { + if (!empty(errors)) { throw new AggregateError(errors, `Errors closing sub-mocks`); } }, @@ -120,8 +122,29 @@ export function mockFunction(...args) { return fn; }; + fn.neverCalled = (...args) => { + if (!empty(args)) { + throw new TypeError(`Didn't expect any arguments`); + } + + if (allCallDescriptions[0].described) { + throw new TypeError(`Unexpected .neverCalled() when any descriptions provided`); + } + + limitCallCount = true; + allCallDescriptions.splice(0, allCallDescriptions.length); + + currentCallDescription = new Proxy({}, { + set() { + throw new Error(`Unexpected description when .neverCalled() has been called`); + }, + }); + + return fn; + }; + fn.once = (...args) => { - if (args.length) { + if (!empty(args)) { throw new TypeError(`Didn't expect any arguments`); } @@ -129,6 +152,7 @@ export function mockFunction(...args) { throw new TypeError(`Unexpected .once() when providing multiple descriptions`); } + currentCallDescription.described = true; limitCallCount = true; markedAsOnce = true; @@ -136,7 +160,7 @@ export function mockFunction(...args) { }; fn.next = (...args) => { - if (args.length) { + if (!empty(args)) { throw new TypeError(`Didn't expect any arguments`); } @@ -148,6 +172,7 @@ export function mockFunction(...args) { allCallDescriptions.push(currentCallDescription); limitCallCount = true; + return fn; }; @@ -156,9 +181,9 @@ export function mockFunction(...args) { // call description which is being repeated. if (!( - typeof value === 'number' && - value === parseInt(value) && - value >= 2 + typeof times === 'number' && + times === parseInt(times) && + times >= 2 )) { throw new TypeError(`Expected whole number of at least 2`); } @@ -185,6 +210,19 @@ export function mockFunction(...args) { return { value: fn, close: () => { + const totalCallCount = runningCallCount; + const expectedCallCount = countDescribedCalls(); + + if (limitCallCount && totalCallCount !== expectedCallCount) { + if (expectedCallCount > 1) { + topLevelErrors.push(new Error(`Expected ${expectedCallCount} calls, got ${totalCallCount}`)); + } else if (expectedCallCount === 1) { + topLevelErrors.push(new Error(`Expected 1 call, got ${totalCallCount}`)); + } else { + topLevelErrors.push(new Error(`Unexpectedly called at all`)); + } + } + if (topLevelErrors.length) { throw new AggregateError(topLevelErrors, `Errors in mock ${name}`); } @@ -204,6 +242,13 @@ export function mockFunction(...args) { const callErrors = []; runningCallCount++; + + // No further processing, this indicates the function shouldn't have been + // called at all and there aren't any descriptions to match this call with. + if (empty(allCallDescriptions)) { + return; + } + const currentCallNumber = runningCallCount; const currentDescription = selectCallDescription(currentCallNumber); @@ -212,10 +257,9 @@ export function mockFunction(...args) { argsPattern, } = currentDescription; - if (argumentCount !== null) { - if (args.length !== argumentCount) { - callErrors.push(new Error(`Argument count mismatch: expected ${argumentCount}, got ${args.length}`)); - } + if (argumentCount !== null && args.length !== argumentCount) { + callErrors.push( + new Error(`Argument count mismatch: expected ${argumentCount}, got ${args.length}`)); } if (argsPattern !== null) { @@ -232,7 +276,7 @@ export function mockFunction(...args) { } } - if (callErrors.length) { + if (!empty(callErrors)) { const aggregate = new AggregateError(callErrors, `Errors in call #${currentCallNumber}`); topLevelErrors.push(aggregate); } @@ -241,15 +285,8 @@ export function mockFunction(...args) { } function selectCallDescription(currentCallNumber) { - // console.log(currentCallNumber, allCallDescriptions[0]); - - const lastDescription = allCallDescriptions[allCallDescriptions.length - 1]; - const describedCount = - (lastDescription.described - ? allCallDescriptions.length - : allCallDescriptions.length - 1); - - if (currentCallNumber > describedCount) { + if (currentCallNumber > countDescribedCalls()) { + const lastDescription = lastCallDescription(); if (lastDescription.described) { return newCallDescription(); } else { @@ -259,4 +296,19 @@ export function mockFunction(...args) { return allCallDescriptions[currentCallNumber - 1]; } } + + function countDescribedCalls() { + if (empty(allCallDescriptions)) { + return 0; + } + + return ( + (lastCallDescription().described + ? allCallDescriptions.length + : allCallDescriptions.length - 1)); + } + + function lastCallDescription() { + return allCallDescriptions[allCallDescriptions.length - 1]; + } } -- cgit 1.3.0-6-gf8a5