diff --git a/slides.md b/slides.md index 7445180..82ab3dc 100644 --- a/slides.md +++ b/slides.md @@ -603,7 +603,7 @@ test('delayedHello executes the callback after the specified delay', () => { - In this exercise about context, we will focus on child tests (also known as subtests) - In the file `index.test.js` you will find multiple tests for the `sum` and the `average` functions -- Group together all the subtests related to the same function +- Group together all the subtests related to the same function using the `describe` function --- @@ -611,16 +611,16 @@ test('delayedHello executes the callback after the specified delay', () => { ```javascript // Grouping tests for `sum` function -test('sum function tests', async t => { - await t.test('Sum works correctly with valid input', () => { +describe('sum function tests', () => { + test('Sum works correctly with valid input', () => { assert.deepStrictEqual(sum([1, 2, 3]), 6) }) - await t.test('Sum returns 0 in case of empty array', () => { + test('Sum returns 0 in case of empty array', () => { assert.deepStrictEqual(sum([]), 0) }) - await t.test('Sum throws in case of bad input', () => { + test('Sum throws in case of bad input', () => { assert.throws(() => sum('abc'), { message: 'Input must be an array of numbers' }) @@ -634,16 +634,16 @@ test('sum function tests', async t => { ```javascript // Grouping tests for `average` function -test('average function tests', async t => { - await t.test('Average works correctly with valid input', () => { +describe('average function tests', () => { + test('Average works correctly with valid input', () => { assert.deepStrictEqual(average([1, 2, 3]), 2) }) - await t.test('Average returns 0 in case of empty array', () => { + test('Average returns 0 in case of empty array', () => { assert.deepStrictEqual(average([]), 0) }) - await t.test('Average throws in case of bad input', () => { + test('Average throws in case of bad input', () => { assert.throws(() => average('abc'), { message: 'Input must be an array of numbers' }) diff --git a/src/a02-assertions/package.json b/src/a02-assertions/package.json index e548008..cb39da3 100644 --- a/src/a02-assertions/package.json +++ b/src/a02-assertions/package.json @@ -8,7 +8,8 @@ "test": "test" }, "scripts": { - "test": "node --test" + "test": "node --test", + "verify": "node --test NODE_NO_WARNINGS=1 --loader=../../verify/loader.js ./verifiers/handler.js" }, "keywords": [], "author": "", diff --git a/src/a02-assertions/verifiers/handler.js b/src/a02-assertions/verifiers/handler.js new file mode 100644 index 0000000..4a7acdd --- /dev/null +++ b/src/a02-assertions/verifiers/handler.js @@ -0,0 +1,86 @@ +import { test } from '../../../verify/test.verify.js' +import { assertCalls } from '../../../verify/assert.verify.js' +import { sum, sumAsync } from './index.verify.js' + +await import('../test/index.test.js') + +// I'm creating this wrapper in order to be able to spy the process.exit +process.on('exit', () => { + console.log('Validating the test completion...') + + const expectations = [ + { + condition: test.mock.calls.length < 2, + message: + 'You need to create a test for both the sum and sumAsync functions' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sum' && a.method === 'deepStrictEqual' + ) === 'undefined', + message: 'You need to call "deepStrictEqual" inside the "sum" test' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sum' && a.method === 'ok' + ) === 'undefined', + message: 'You need to call "ok" inside the "sum" test' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sum' && a.method === 'doesNotThrow' + ) === 'undefined', + message: 'You need to call "doesNotThrow" inside the "sum" test' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sum' && a.method === 'throws' + ) === 'undefined', + message: 'You need to call "throws" inside the "sum" test' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sumAsync' && a.method === 'deepStrictEqual' + ) === 'undefined', + message: 'You need to call "deepStrictEqual" inside the "sumAsync" test' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sumAsync' && a.method === 'ok' + ) === 'undefined', + message: 'You need to call "ok" inside the "sumAsync" test' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sumAsync' && a.method === 'doesNotReject' + ) === 'undefined', + message: 'You need to call "doesNotReject" inside the "sumAsync" test' + }, + { + condition: + typeof assertCalls.find( + a => a.testName === 'sumAsync' && a.method === 'rejects' + ) === 'undefined', + message: 'You need to call "rejects" inside the "sumAsync" test' + }, + { + condition: sum.mock.calls.length < 1, + message: 'You need to call "sum" at least once in your test' + }, + { + condition: sumAsync.mock.calls.length < 1, + message: 'You need to call "sumAsync" at least once in your test' + } + ] + + for (const expectation of expectations) { + if (expectation.condition) throw new Error(expectation.message) + } +}) diff --git a/src/a02-assertions/verifiers/index.verify.js b/src/a02-assertions/verifiers/index.verify.js new file mode 100644 index 0000000..4f1cb31 --- /dev/null +++ b/src/a02-assertions/verifiers/index.verify.js @@ -0,0 +1,8 @@ +import { + sum as originalSum, + sumAsync as originalSumAsync +} from '../src/index.js' +import { mock } from 'node:test' + +export const sum = mock.fn(originalSum) +export const sumAsync = mock.fn(originalSumAsync) diff --git a/src/a04-hooks/package.json b/src/a04-hooks/package.json index 6bbaa04..d2899b9 100644 --- a/src/a04-hooks/package.json +++ b/src/a04-hooks/package.json @@ -9,6 +9,7 @@ }, "scripts": { "test": "node --test ./test/index.test.js", + "verify": "node --test NODE_NO_WARNINGS=1 --loader=../../verify/loader.js ./verifiers/handler.js", "solution": "node --test ./test/solution.test.js" }, "keywords": [], diff --git a/src/a04-hooks/test/index.test.js b/src/a04-hooks/test/index.test.js index 7a9bdab..f93c2e8 100644 --- a/src/a04-hooks/test/index.test.js +++ b/src/a04-hooks/test/index.test.js @@ -14,47 +14,45 @@ let user databaseConnection = await connectToDatabase() -test('Authentication Module Tests', async () => { - await test('should authenticate a valid user', async () => { - user = await createUser(databaseConnection, 'testuser', 'password123') - const result = await authenticateUser( - databaseConnection, - 'testuser', - 'password123' - ) - assert.strictEqual(result, true, 'Authentication should succeed') - await deleteUser(databaseConnection, user) - }) +test('should authenticate a valid user', async () => { + user = await createUser(databaseConnection, 'testuser', 'password123') + const result = await authenticateUser( + databaseConnection, + 'testuser', + 'password123' + ) + assert.strictEqual(result, true, 'Authentication should succeed') + await deleteUser(databaseConnection, user) +}) - await test('should reject invalid password', async () => { - user = await createUser(databaseConnection, 'testuser', 'password123') - const result = await authenticateUser( - databaseConnection, - 'testuser', - 'wrongpassword' - ) - assert.strictEqual( - result, - false, - 'Authentication should fail with incorrect password' - ) - await deleteUser(databaseConnection, user) - }) +test('should reject invalid password', async () => { + user = await createUser(databaseConnection, 'testuser', 'password123') + const result = await authenticateUser( + databaseConnection, + 'testuser', + 'wrongpassword' + ) + assert.strictEqual( + result, + false, + 'Authentication should fail with incorrect password' + ) + await deleteUser(databaseConnection, user) +}) - await test('should reject non-existing user', async () => { - user = await createUser(databaseConnection, 'testuser', 'password123') - const result = await authenticateUser( - databaseConnection, - 'nonexistentuser', - 'password123' - ) - assert.strictEqual( - result, - false, - 'Authentication should fail with non-existing user' - ) - await deleteUser(databaseConnection, user) - }) +test('should reject non-existing user', async () => { + user = await createUser(databaseConnection, 'testuser', 'password123') + const result = await authenticateUser( + databaseConnection, + 'nonexistentuser', + 'password123' + ) + assert.strictEqual( + result, + false, + 'Authentication should fail with non-existing user' + ) + await deleteUser(databaseConnection, user) }) await closeDatabaseConnection(databaseConnection) diff --git a/src/a04-hooks/test/solution.test.js b/src/a04-hooks/test/solution.test.js index 248f8a1..92bfdb5 100644 --- a/src/a04-hooks/test/solution.test.js +++ b/src/a04-hooks/test/solution.test.js @@ -27,39 +27,37 @@ afterEach(async () => { await deleteUser(databaseConnection, user) }) -test('Authentication Module Tests', async () => { - await test('should authenticate a valid user', async () => { - const result = await authenticateUser( - databaseConnection, - 'testuser', - 'password123' - ) - assert.strictEqual(result, true, 'Authentication should succeed') - }) +test('should authenticate a valid user', async () => { + const result = await authenticateUser( + databaseConnection, + 'testuser', + 'password123' + ) + assert.strictEqual(result, true, 'Authentication should succeed') +}) - await test('should reject invalid password', async () => { - const result = await authenticateUser( - databaseConnection, - 'testuser', - 'wrongpassword' - ) - assert.strictEqual( - result, - false, - 'Authentication should fail with incorrect password' - ) - }) +test('should reject invalid password', async () => { + const result = await authenticateUser( + databaseConnection, + 'testuser', + 'wrongpassword' + ) + assert.strictEqual( + result, + false, + 'Authentication should fail with incorrect password' + ) +}) - await test('should reject non-existing user', async () => { - const result = await authenticateUser( - databaseConnection, - 'nonexistentuser', - 'password123' - ) - assert.strictEqual( - result, - false, - 'Authentication should fail with non-existing user' - ) - }) +test('should reject non-existing user', async () => { + const result = await authenticateUser( + databaseConnection, + 'nonexistentuser', + 'password123' + ) + assert.strictEqual( + result, + false, + 'Authentication should fail with non-existing user' + ) }) diff --git a/src/a04-hooks/verifiers/handler.js b/src/a04-hooks/verifiers/handler.js new file mode 100644 index 0000000..019f421 --- /dev/null +++ b/src/a04-hooks/verifiers/handler.js @@ -0,0 +1,50 @@ +import { test } from '../../../verify/test.verify.js' +import { fnCalls } from './index.verify.js' + +await import('../test/index.test.js') + +// I'm creating this wrapper in order to be able to spy the process.exit +process.on('exit', () => { + console.log('Validating the test completion...') + + const expectations = [ + { + condition: test.mock.calls.length < 3, + message: 'You need to create a test for at least three cases' + }, + { + condition: + typeof fnCalls.find( + call => call.name === 'connectToDatabase' && call.caller === 'before' + ) === 'undefined', + message: 'You need to call "connectToDatabase" inside a "before" hook' + }, + { + condition: + typeof fnCalls.find( + call => call.name === 'createUser' && call.caller === 'beforeEach' + ) === 'undefined', + message: 'You need to call "createUser" inside a "beforeEach" hook' + }, + { + condition: + typeof fnCalls.find( + call => call.name === 'deleteUser' && call.caller === 'afterEach' + ) === 'undefined', + message: 'You need to call "deleteUser" inside a "afterEach" hook' + }, + { + condition: + typeof fnCalls.find( + call => + call.name === 'closeDatabaseConnection' && call.caller === 'after' + ) === 'undefined', + message: + 'You need to call "closeDatabaseConnection" inside a "after" hook' + } + ] + + for (const expectation of expectations) { + if (expectation.condition) throw new Error(expectation.message) + } +}) diff --git a/src/a04-hooks/verifiers/index.verify.js b/src/a04-hooks/verifiers/index.verify.js new file mode 100644 index 0000000..d75568f --- /dev/null +++ b/src/a04-hooks/verifiers/index.verify.js @@ -0,0 +1,47 @@ +import { + connectToDatabase as originalConnectToDatabase, + closeDatabaseConnection as originalCloseDatabaseConnection, + createUser as originalCreateUser, + authenticateUser as originalAuthenticateUser, + deleteUser as originalDeleteUser +} from '../src/index.js' +import { getHook } from '../../../verify/test.verify.js' +import { mock } from 'node:test' + +export const fnCalls = [] + +export const connectToDatabase = mock.fn((...args) => { + fnCalls.push({ + name: 'connectToDatabase', + caller: getHook() + }) + return originalConnectToDatabase(...args) +}) +export const closeDatabaseConnection = mock.fn((...args) => { + fnCalls.push({ + name: 'closeDatabaseConnection', + caller: getHook() + }) + return originalCloseDatabaseConnection(...args) +}) +export const createUser = mock.fn((...args) => { + fnCalls.push({ + name: 'createUser', + caller: getHook() + }) + return originalCreateUser(...args) +}) +export const authenticateUser = mock.fn((...args) => { + fnCalls.push({ + name: 'authenticateUser', + caller: getHook() + }) + return originalAuthenticateUser(...args) +}) +export const deleteUser = mock.fn((...args) => { + fnCalls.push({ + name: 'deleteUser', + caller: getHook() + }) + return originalDeleteUser(...args) +}) diff --git a/src/a05-keywords/package.json b/src/a05-keywords/package.json index f479d57..c57eb1e 100644 --- a/src/a05-keywords/package.json +++ b/src/a05-keywords/package.json @@ -9,6 +9,7 @@ }, "scripts": { "test": "node --test ./test/index.test.js", + "verify": "node --test NODE_NO_WARNINGS=1 --loader=../../verify/loader.js ./verifiers/handler.js", "solution": "node --test --test-only ./test/solution.test.js" }, "keywords": [], diff --git a/src/a05-keywords/verifiers/handler.js b/src/a05-keywords/verifiers/handler.js new file mode 100644 index 0000000..ce36174 --- /dev/null +++ b/src/a05-keywords/verifiers/handler.js @@ -0,0 +1,31 @@ +import { test, only, skip, todo } from '../../../verify/test.verify.js' + +await import('../test/index.test.js') + +// I'm creating this wrapper in order to be able to spy the process.exit +process.on('exit', () => { + console.log('Validating the test completion...') + + const expectations = [ + { + condition: test.mock.calls.length < 1, + message: 'You need to call "test" at least once' + }, + { + condition: only.mock.calls.length < 1, + message: 'You need to call "only" at least once' + }, + { + condition: skip.mock.calls.length < 1, + message: 'You need to call "skip" at least once' + }, + { + condition: todo.mock.calls.length < 1, + message: 'You need to call "todo" at least once' + } + ] + + for (const expectation of expectations) { + if (expectation.condition) throw new Error(expectation.message) + } +}) diff --git a/src/a05-keywords/verifiers/index.verify.js b/src/a05-keywords/verifiers/index.verify.js new file mode 100644 index 0000000..667c241 --- /dev/null +++ b/src/a05-keywords/verifiers/index.verify.js @@ -0,0 +1,5 @@ +import { sum as originalSum, product as originalProduct } from '../src/index.js' +import { mock } from 'node:test' + +export const sum = mock.fn(originalSum) +export const product = mock.fn(originalProduct) diff --git a/src/a06-mocks/package.json b/src/a06-mocks/package.json index c2aa3db..a1ab762 100644 --- a/src/a06-mocks/package.json +++ b/src/a06-mocks/package.json @@ -9,6 +9,7 @@ }, "scripts": { "test": "node --test ./test/index.test.js", + "verify": "node --test NODE_NO_WARNINGS=1 --loader=../../verify/loader.js ./verifiers/handler.js", "solution": "node --test --test-only ./test/solution.test.js" }, "keywords": [], diff --git a/src/a06-mocks/test/index.test.js b/src/a06-mocks/test/index.test.js index a867cdf..e9033ed 100644 --- a/src/a06-mocks/test/index.test.js +++ b/src/a06-mocks/test/index.test.js @@ -1,13 +1,9 @@ /* eslint-disable no-unused-vars */ -import assert from 'node:assert' -import { sum } from '../src/index.js' -import { test, mock } from 'node:test' +import { test } from 'node:test' test('spies on a sum', () => { // use the mockedSum spy to check // how many times its called before // and after being used // check when its called wich argoument has received, result, and error - - const mockedSum = mock.fn(sum) }) diff --git a/src/a06-mocks/verifiers/handler.js b/src/a06-mocks/verifiers/handler.js new file mode 100644 index 0000000..1ed138a --- /dev/null +++ b/src/a06-mocks/verifiers/handler.js @@ -0,0 +1,19 @@ +import { mockCalls } from '../../../verify/test.verify.js' + +await import('../test/index.test.js') + +// I'm creating this wrapper in order to be able to spy the process.exit +process.on('exit', () => { + console.log('Validating the test completion...') + + const expectations = [ + { + condition: !mockCalls.includes('fn'), + message: 'You need to call "mock.fn" at least once' + } + ] + + for (const expectation of expectations) { + if (expectation.condition) throw new Error(expectation.message) + } +}) diff --git a/src/a06-mocks/verifiers/index.verify.js b/src/a06-mocks/verifiers/index.verify.js new file mode 100644 index 0000000..f266290 --- /dev/null +++ b/src/a06-mocks/verifiers/index.verify.js @@ -0,0 +1,4 @@ +import { sum as originalSum } from '../src/index.js' +import { mock } from 'node:test' + +export const sum = mock.fn(originalSum) diff --git a/src/a11-timers/package.json b/src/a11-timers/package.json index 0ef2230..9ca5571 100644 --- a/src/a11-timers/package.json +++ b/src/a11-timers/package.json @@ -6,7 +6,8 @@ "main": "index.js", "scripts": { "test": "node --test", - "solution": "node --test ./test/solution.test.js" + "solution": "node --test ./test/solution.test.js", + "verify": "node --test NODE_NO_WARNINGS=1 --loader=../../verify/loader.js ./verifiers/handler.js" }, "keywords": [], "author": "", diff --git a/src/a11-timers/verifiers/handler.js b/src/a11-timers/verifiers/handler.js new file mode 100644 index 0000000..54f740f --- /dev/null +++ b/src/a11-timers/verifiers/handler.js @@ -0,0 +1,39 @@ +import { delayedHello } from './index.verify.js' +import { mockCalls } from '../../../verify/test.verify.js' +import { assertCalls } from '../../../verify/assert.verify.js' + +await import('../test/index.test.js') + +// I'm creating this wrapper in order to be able to spy the process.exit +process.on('exit', () => { + console.log('Validating the test completion...') + + const expectations = [ + { + condition: !mockCalls.includes('timers.enable'), + message: 'You need to call "timers.enable" at least once' + }, + { + condition: !mockCalls.includes('timers.tick'), + message: 'You need to call "timers.tick" at least once' + }, + { + condition: !mockCalls.includes('timers.reset'), + message: 'You need to call "timers.reset" at least once' + }, + { + condition: delayedHello.mock.calls.length === 0, + message: 'You need to call "delayedHello" at least once' + }, + { + condition: + typeof assertCalls.find(a => a.method === 'strictEqual') === + 'undefined', + message: 'You need to call "strictEqual" inside the test' + } + ] + + for (const expectation of expectations) { + if (expectation.condition) throw new Error(expectation.message) + } +}) diff --git a/src/a11-timers/verifiers/index.verify.js b/src/a11-timers/verifiers/index.verify.js new file mode 100644 index 0000000..0fe4ed4 --- /dev/null +++ b/src/a11-timers/verifiers/index.verify.js @@ -0,0 +1,4 @@ +import { delayedHello as originalDlayedHello } from '../src/index.js' +import { mock } from 'node:test' + +export const delayedHello = mock.fn(originalDlayedHello) diff --git a/src/a12-context/package.json b/src/a12-context/package.json index ca14aa7..babb95c 100644 --- a/src/a12-context/package.json +++ b/src/a12-context/package.json @@ -6,7 +6,8 @@ "main": "index.js", "scripts": { "test": "node --test", - "solution": "node --test ./test/solution.test.js" + "solution": "node --test ./test/solution.test.js", + "verify": "node --test NODE_NO_WARNINGS=1 --loader=../../verify/loader.js ./verifiers/handler.js" }, "keywords": [], "author": "", diff --git a/src/a12-context/test/solution.test.js b/src/a12-context/test/solution.test.js index 5c188cc..b37d9aa 100644 --- a/src/a12-context/test/solution.test.js +++ b/src/a12-context/test/solution.test.js @@ -1,18 +1,18 @@ import { average, sum } from '../src/index.js' -import { test } from 'node:test' +import { test, describe } from 'node:test' import assert from 'node:assert' // Grouping tests for `sum` function -test('sum function tests', async t => { - await t.test('Sum works correctly with valid input', () => { +describe('sum function tests', () => { + test('Sum works correctly with valid input', () => { assert.deepStrictEqual(sum([1, 2, 3]), 6) }) - await t.test('Sum returns 0 in case of empty array', () => { + test('Sum returns 0 in case of empty array', () => { assert.deepStrictEqual(sum([]), 0) }) - await t.test('Sum throws in case of bad input', () => { + test('Sum throws in case of bad input', () => { assert.throws(() => sum('abc'), { message: 'Input must be an array of numbers' }) @@ -20,16 +20,16 @@ test('sum function tests', async t => { }) // Grouping tests for `average` function -test('average function tests', async t => { - await t.test('Average works correctly with valid input', () => { +describe('average function tests', () => { + test('Average works correctly with valid input', () => { assert.deepStrictEqual(average([1, 2, 3]), 2) }) - await t.test('Average returns 0 in case of empty array', () => { + test('Average returns 0 in case of empty array', () => { assert.deepStrictEqual(average([]), 0) }) - await t.test('Average throws in case of bad input', () => { + test('Average throws in case of bad input', () => { assert.throws(() => average('abc'), { message: 'Input must be an array of numbers' }) diff --git a/src/a12-context/verifiers/handler.js b/src/a12-context/verifiers/handler.js new file mode 100644 index 0000000..ad7ac15 --- /dev/null +++ b/src/a12-context/verifiers/handler.js @@ -0,0 +1,24 @@ +import { test, describe } from '../../../verify/test.verify.js' + +await import('../test/index.test.js') + +// I'm creating this wrapper in order to be able to spy the process.exit +process.on('exit', () => { + console.log('Validating the test completion...') + + const expectations = [ + { + condition: test.mock.calls.length !== 6, + message: 'You need to all the tests' + }, + { + condition: describe.mock.calls.length !== 2, + message: + 'You need to use "describe" to create the test suites for the average and sum function' + } + ] + + for (const expectation of expectations) { + if (expectation.condition) throw new Error(expectation.message) + } +}) diff --git a/src/a12-context/verifiers/index.verify.js b/src/a12-context/verifiers/index.verify.js new file mode 100644 index 0000000..f2b8dd1 --- /dev/null +++ b/src/a12-context/verifiers/index.verify.js @@ -0,0 +1,5 @@ +import { sum as originalSum, average as originalAverage } from '../src/index.js' +import { mock } from 'node:test' + +export const sum = mock.fn(originalSum) +export const average = mock.fn(originalAverage) diff --git a/verify/assert.verify.js b/verify/assert.verify.js new file mode 100644 index 0000000..9005751 --- /dev/null +++ b/verify/assert.verify.js @@ -0,0 +1,25 @@ +import * as originalAssert from 'node:assert' +import { getCurrentTestName } from './test.verify.js' + +export const assertCalls = [] + +const assert = new Proxy(originalAssert, { + get(target, prop) { + const originalMethod = target[prop] + + if (typeof originalMethod === 'function') { + return (...args) => { + assertCalls.push({ + method: prop, + arguments: args, + testName: getCurrentTestName() + }) + return originalMethod.apply(target, args) + } + } + + return originalMethod + } +}) + +export default assert diff --git a/verify/loader.js b/verify/loader.js new file mode 100644 index 0000000..cf473ca --- /dev/null +++ b/verify/loader.js @@ -0,0 +1,30 @@ +import path from 'path' + +export async function resolve(url, context, defaultResolve) { + // To avoid circular dependencies + // We suppose that all the verifiers include "verify" in the name + if ( + typeof context.parentURL !== 'undefined' && + context.parentURL.includes('verify') + ) { + return defaultResolve(url, context, defaultResolve) + } + + const modulesToPatch = { + 'node:test': './test.verify.js', + 'node:assert': './assert.verify.js', + // We're always mocking the src in order to spy the calls + '../src/index.js': path.join(process.cwd(), 'verifiers', 'index.verify.js') + } + + if (Object.keys(modulesToPatch).includes(url)) { + return defaultResolve( + new URL(modulesToPatch[url], import.meta.url).href, + context, + defaultResolve + ) + } + + // For all other modules, use the default loader + return defaultResolve(url, context, defaultResolve) +} diff --git a/verify/test.verify.js b/verify/test.verify.js new file mode 100644 index 0000000..b59b48d --- /dev/null +++ b/verify/test.verify.js @@ -0,0 +1,80 @@ +import { AsyncLocalStorage } from 'async_hooks' +import t from 'node:test' + +const asyncLocalStorage = new AsyncLocalStorage() + +export const test = t.mock.fn((...args) => { + asyncLocalStorage.run(new Map([['testName', args[0]]]), () => { + return t.test(...args) + }) +}) +export function getCurrentTestName() { + const store = asyncLocalStorage.getStore() + return store?.get('testName') +} + +export const before = t.mock.fn((...args) => { + asyncLocalStorage.run(new Map([['hook', 'before']]), () => { + return t.before(...args) + }) +}) + +export const beforeEach = t.mock.fn((...args) => { + asyncLocalStorage.run(new Map([['hook', 'beforeEach']]), () => { + return t.beforeEach(...args) + }) +}) + +export const after = t.mock.fn((...args) => { + asyncLocalStorage.run(new Map([['hook', 'after']]), () => { + return t.after(...args) + }) +}) + +export const afterEach = t.mock.fn((...args) => { + asyncLocalStorage.run(new Map([['hook', 'afterEach']]), () => { + return t.afterEach(...args) + }) +}) + +export function getHook() { + const store = asyncLocalStorage.getStore() + return store?.get('hook') +} + +export const only = t.mock.fn(t.only) +export const skip = t.mock.fn(t.skip) +export const todo = t.mock.fn(t.todo) +export const describe = t.mock.fn(t.describe) + +export const mockCalls = [] +export const mock = new Proxy(t.mock, { + get(target, prop) { + const originalMethod = target[prop] + + if (typeof originalMethod === 'function') { + return (...args) => { + mockCalls.push(prop) + return originalMethod.apply(target, args) + } + } + + if (prop === 'timers') { + const timers = target[prop] + return new Proxy(timers, { + get(timersTarget, timersProp) { + const originalMethod = timersTarget[timersProp] + if (typeof originalMethod === 'function') { + return (...args) => { + mockCalls.push('timers.' + timersProp) + return originalMethod.apply(timersTarget, args) + } + } + return originalMethod + } + }) + } + + return originalMethod + } +})