diff --git a/package.json b/package.json index 016f015..1575f9d 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build": "webpack --config webpack.config.cjs", - "test": "node ./src/test.js" + "test": "node ./src/test.js --" }, "devDependencies": { "@babel/core": "^7.24.5", diff --git a/src/ZetoTestingEngine.js b/src/ZetoTestingEngine.js index 6a1cf1a..fdd3a51 100644 --- a/src/ZetoTestingEngine.js +++ b/src/ZetoTestingEngine.js @@ -1,3 +1,5 @@ +import { readdirSync } from 'fs'; + const colors = { reset: '\x1b[0m', fgRed: '\x1b[31m', @@ -6,38 +8,55 @@ const colors = { }; class ZetoTestingEngine { + filter; + testFiles; + + constructor(options = {}) { + this.filter = options.filter ?? false; + } + + async load(testDir = './tests') { + this.testFiles = readdirSync(testDir).filter((file) => file.endsWith('.js')); + if (this.filter) { + this.testFiles = this.testFiles.filter((file) => file.includes(this.filter)); + } + } + + async run() { + let allTestsPassed = true; + for (const file of this.testFiles) { + const tester = new ZetoTester(); + const testModule = await import(`../tests/${file}`); + console.log(`Running tests from ${file}`); + try { + testModule.test({ tester: tester }); + if (tester.failed) { + allTestsPassed = false; + } + } catch (error) { + console.error(`Test failed: ${file}`, error); + allTestsPassed = false; + } + } + return allTestsPassed; + } +} + +class ZetoTester { expectedAsserts; assertCount; errors; expectedCalls; callIndex; - disabled = false; - filter = false; testSetup = false; failed = false; - constructor(options = {}) { - if (options.disabled) { - this.disabled = true; - return; - } - this.filter = options.filter ?? false; - } - setup(setup) { this.testSetup = setup; } test(name, testCase) { - if (this.disabled) { - return; - } - - if (this.filter && name.indexOf(this.filter) == -1) { - return; - } - this.errors = []; this.expectedCalls = []; this.callIndex = 0; @@ -79,12 +98,39 @@ class ZetoTestingEngine { console.log(colors.fgGreen + 'Test passed: ' + colors.reset + name + ' (' + this.assertCount + '/' + this.expectedAsserts + ' assertions)'); } - assert(what, value, message) { + assertTrue(condition, message) { this.expectedAsserts++; - if (what != value) { - this.errors.push('Error asserting that ' + what + ' equals ' + value + ', checking ' + message); + if (condition) { + this.assertCount++; } else { + this.errors.push('Error asserting that ' + condition + ' is true, checking ' + message); + } + } + + assertFalse(condition, message) { + this.expectedAsserts++; + if (!condition) { this.assertCount++; + } else { + this.errors.push('Error asserting that ' + condition + ' is false, checking ' + message); + } + } + + assertSame(expected, actual, message) { + this.expectedAsserts++; + if (expected == actual) { + this.assertCount++; + } else { + this.errors.push('Error asserting that ' + expected + ' equals ' + actual + ', checking ' + message); + } + } + + assertNotSame(unexpected, actual, message) { + this.expectedAsserts++; + if (unexpected != actual) { + this.assertCount++; + } else { + this.errors.push('Error asserting that ' + unexpected + ' is not ' + actual + ', checking ' + message); } } @@ -114,4 +160,4 @@ class ZetoTestingEngine { } } -export { ZetoTestingEngine }; +export { ZetoTestingEngine, ZetoTester }; diff --git a/src/test.js b/src/test.js index 5b409e3..f93dd3e 100644 --- a/src/test.js +++ b/src/test.js @@ -1,31 +1,20 @@ -import { readdirSync } from 'fs'; import { ZetoTestingEngine } from './ZetoTestingEngine.js'; -async function runTests() { - const testFiles = readdirSync('./tests').filter((file) => file.endsWith('.js')); - let allTestsPassed = true; +const args = process.argv.slice(2); +const options = {}; - for (const file of testFiles) { - const testingEngine = new ZetoTestingEngine(); - const testModule = await import(`../tests/${file}`); - if (typeof testModule.test === 'function') { - console.log(`Running tests from ${file}`); - try { - testModule.test({ tester: testingEngine }); - if (testingEngine.failed) { - allTestsPassed = false; - } - } catch (error) { - console.error(`Test failed: ${file}`, error); - allTestsPassed = false; - } - } else { - console.warn(`No test function exported from ${file}`); - } - } +args.forEach((arg) => { + const [key, value] = arg.split('='); + options[key] = value; +}); + +async function runTests() { + const testingEngine = new ZetoTestingEngine(options); + await testingEngine.load(); + const allTestsPassed = await testingEngine.run(); if (!allTestsPassed) { - process.exit(1); // Exit with an error code if any tests failed + process.exit(1); } } diff --git a/tests/ZetoTestingEngineTests.js b/tests/ZetoTestingEngineTests.js new file mode 100644 index 0000000..413f57e --- /dev/null +++ b/tests/ZetoTestingEngineTests.js @@ -0,0 +1,55 @@ +import { ZetoTimerEngine } from '../src/ZetoTimerEngine.js'; + +export function test({ tester }) { + tester.setup(() => { + return { + true: true, + false: false, + null: null, + undefined: undefined, + zero: 0, + truthy: 1, + falsy: 0, + }; + }); + + tester.test('test setup & assertSame', (setup) => { + tester.assertSame(setup.true, true, 'true should be true'); + tester.assertSame(setup.false, false, 'false should be false'); + tester.assertSame(setup.null, null, 'null should be null'); + tester.assertSame(setup.undefined, undefined, 'undefined should be undefined'); + tester.assertSame(setup.null, setup.undefined, 'null and undefined are both nothing'); + tester.assertSame(setup.zero, 0, 'zero should be 0'); + tester.assertSame(setup.truthy, 1, 'truthy and one are both truthy'); + tester.assertSame(setup.falsy, 0, 'falsy and zero are both falsy'); + }); + + tester.test('test setup & assertNotSame', (setup) => { + tester.assertNotSame(setup.true, false, 'true should not be false'); + tester.assertNotSame(setup.false, true, 'false should not be true'); + tester.assertNotSame(setup.zero, 1, 'zero should not be 1'); + tester.assertNotSame(setup.truthy, 0, 'truthy should not be 0'); + tester.assertNotSame(setup.falsy, 1, 'falsy should not be 1'); + }); + + tester.test('test assert is true', (setup) => { + tester.assertTrue(true, 'true should be true'); + }); + + tester.test('test assert is false', (setup) => { + tester.assertFalse(false, 'false should be false'); + }); + + tester.test('test expectCall is called', (setup) => { + let listener1 = tester.expectCall('listener', 1); + listener1(); + + let listener2 = tester.expectCall('listener', 2); + listener2(); + listener2(); + }); + + tester.test('test expectCall is not called', (setup) => { + let listener1 = tester.expectCall('listener', 0); + }); +} diff --git a/tests/ZetoTimerEngineTests.js b/tests/ZetoTimerEngineTests.js index 2e9119b..735caf0 100644 --- a/tests/ZetoTimerEngineTests.js +++ b/tests/ZetoTimerEngineTests.js @@ -9,10 +9,10 @@ export function test({ tester }) { tester.test('performWithDelay creates timer', (setup) => { let timer1 = setup.timerEngine.performWithDelay(0, () => {}); - tester.assert(setup.timerEngine.timers.length, 1, 'timers should have 1 item'); + tester.assertSame(setup.timerEngine.timers.length, 1, 'timers should have 1 item'); let timer2 = setup.timerEngine.performWithDelay(0, () => {}); - tester.assert(setup.timerEngine.timers.length, 2, 'timers should have 2 items'); + tester.assertSame(setup.timerEngine.timers.length, 2, 'timers should have 2 items'); }); tester.test('performWithDelay with delay is executed after delta', (setup) => { @@ -43,10 +43,10 @@ export function test({ tester }) { tester.test('cancel removes timer', (setup) => { let timer = setup.timerEngine.performWithDelay(1200, () => {}); - tester.assert(setup.timerEngine.timers.length, 1, 'timers should have 1 items'); + tester.assertSame(setup.timerEngine.timers.length, 1, 'timers should have 1 items'); setup.timerEngine.cancel(timer); setup.timerEngine.update({ delta: 0 }); - tester.assert(setup.timerEngine.timers.length, 0, 'timers should have 0 items'); + tester.assertSame(setup.timerEngine.timers.length, 0, 'timers should have 0 items'); }); tester.test('cancel prevents timer listener execution', (setup) => {