Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: test_runner#mock:timers housekeeping/refactoring #55506

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions test/parallel/test-runner-mock-timers-date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
'use strict';
process.env.NODE_TEST_KNOWN_GLOBALS = 0;
require('../common');

const assert = require('node:assert');
const { it, describe } = require('node:test');

describe('Mock Timers Date Test Suite', () => {
it('should return the initial UNIX epoch if not specified', (t) => {
t.mock.timers.enable({ apis: ['Date'] });
const date = new Date();
assert.strictEqual(date.getTime(), 0);
assert.strictEqual(Date.now(), 0);
});

it('should throw an error if setTime is called without enabling timers', (t) => {
assert.throws(
() => {
t.mock.timers.setTime(100);
},
{ code: 'ERR_INVALID_STATE' }
);
});

it('should throw an error if epoch passed to enable is not valid', (t) => {
assert.throws(
() => {
t.mock.timers.enable({ now: -1 });
},
{ code: 'ERR_INVALID_ARG_VALUE' }
);

assert.throws(
() => {
t.mock.timers.enable({ now: 'string' });
},
{ code: 'ERR_INVALID_ARG_TYPE' }
);

assert.throws(
() => {
t.mock.timers.enable({ now: NaN });
},
{ code: 'ERR_INVALID_ARG_VALUE' }
);
});

it('should replace the original Date with the mocked one', (t) => {
t.mock.timers.enable({ apis: ['Date'] });
assert.ok(Date.isMock);
});

it('should return the ticked time when calling Date.now after tick', (t) => {
t.mock.timers.enable({ apis: ['Date'] });
const time = 100;
t.mock.timers.tick(time);
assert.strictEqual(Date.now(), time);
});

it('should return the Date as string when calling it as a function', (t) => {
t.mock.timers.enable({ apis: ['Date'] });
const returned = Date();
// Matches the format: 'Mon Jan 01 1970 00:00:00'
// We don't care about the date, just the format
assert.ok(/\w{3}\s\w{3}\s\d{1,2}\s\d{2,4}\s\d{1,2}:\d{2}:\d{2}/.test(returned));
});

it('should return the date with different argument calls', (t) => {
t.mock.timers.enable({ apis: ['Date'] });
assert.strictEqual(new Date(0).getTime(), 0);
assert.strictEqual(new Date(100).getTime(), 100);
assert.strictEqual(new Date('1970-01-01T00:00:00.000Z').getTime(), 0);
assert.strictEqual(new Date(1970, 0).getFullYear(), 1970);
assert.strictEqual(new Date(1970, 0).getMonth(), 0);
assert.strictEqual(new Date(1970, 0, 1).getDate(), 1);
assert.strictEqual(new Date(1970, 0, 1, 11).getHours(), 11);
assert.strictEqual(new Date(1970, 0, 1, 11, 10).getMinutes(), 10);
assert.strictEqual(new Date(1970, 0, 1, 11, 10, 45).getSeconds(), 45);
assert.strictEqual(new Date(1970, 0, 1, 11, 10, 45, 898).getMilliseconds(), 898);
assert.strictEqual(new Date(1970, 0, 1, 11, 10, 45, 898).toDateString(), 'Thu Jan 01 1970');
});

it('should return native code when calling Date.toString', (t) => {
t.mock.timers.enable({ apis: ['Date'] });
assert.strictEqual(Date.toString(), 'function Date() { [native code] }');
});

it('should start with a custom epoch if the second argument is specified', (t) => {
t.mock.timers.enable({ apis: ['Date'], now: 100 });
const date1 = new Date();
assert.strictEqual(date1.getTime(), 100);

t.mock.timers.reset();
t.mock.timers.enable({ apis: ['Date'], now: new Date(200) });
const date2 = new Date();
assert.strictEqual(date2.getTime(), 200);
});

it('should replace epoch if setTime is lesser than now and not tick', (t) => {
t.mock.timers.enable();
const fn = t.mock.fn();
const id = setTimeout(fn, 1000);
t.mock.timers.setTime(800);
assert.strictEqual(Date.now(), 800);
t.mock.timers.setTime(500);
assert.strictEqual(Date.now(), 500);
assert.strictEqual(fn.mock.callCount(), 0);
clearTimeout(id);
});

it('should not tick time when setTime is called', (t) => {
t.mock.timers.enable();
const fn = t.mock.fn();
const id = setTimeout(fn, 1000);
t.mock.timers.setTime(1200);
assert.strictEqual(Date.now(), 1200);
assert.strictEqual(fn.mock.callCount(), 0);
clearTimeout(id);
});
});
124 changes: 124 additions & 0 deletions test/parallel/test-runner-mock-timers-scheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
'use strict';
process.env.NODE_TEST_KNOWN_GLOBALS = 0;
const common = require('../common');

const assert = require('node:assert');
const { it, describe } = require('node:test');
const nodeTimersPromises = require('node:timers/promises');

describe('Mock Timers Scheduler Test Suite', () => {
it('should advance in time and trigger timers when calling the .tick function', (t) => {
t.mock.timers.enable({ apis: ['scheduler.wait'] });

const now = Date.now();
const durationAtMost = 100;

const p = nodeTimersPromises.scheduler.wait(4000);
t.mock.timers.tick(4000);

return p.then(common.mustCall((result) => {
assert.strictEqual(result, undefined);
assert.ok(
Date.now() - now < durationAtMost,
`time should be advanced less than the ${durationAtMost}ms`
);
}));
});

it('should advance in time and trigger timers when calling the .tick function multiple times', async (t) => {
t.mock.timers.enable({ apis: ['scheduler.wait'] });

const fn = t.mock.fn();

nodeTimersPromises.scheduler.wait(9999).then(fn);

t.mock.timers.tick(8999);
assert.strictEqual(fn.mock.callCount(), 0);
t.mock.timers.tick(500);

await nodeTimersPromises.setImmediate();

assert.strictEqual(fn.mock.callCount(), 0);
t.mock.timers.tick(500);

await nodeTimersPromises.setImmediate();
assert.strictEqual(fn.mock.callCount(), 1);
});

it('should work with the same params as the original timers/promises/scheduler.wait', async (t) => {
t.mock.timers.enable({ apis: ['scheduler.wait'] });
const controller = new AbortController();
const p = nodeTimersPromises.scheduler.wait(2000, {
ref: true,
signal: controller.signal,
});

t.mock.timers.tick(1000);
t.mock.timers.tick(500);
t.mock.timers.tick(500);
t.mock.timers.tick(500);

const result = await p;
assert.strictEqual(result, undefined);
});

it('should abort operation if timers/promises/scheduler.wait received an aborted signal', async (t) => {
t.mock.timers.enable({ apis: ['scheduler.wait'] });
const controller = new AbortController();
const p = nodeTimersPromises.scheduler.wait(2000, {
ref: true,
signal: controller.signal,
});

t.mock.timers.tick(1000);
controller.abort();
t.mock.timers.tick(500);
t.mock.timers.tick(500);
t.mock.timers.tick(500);

await assert.rejects(() => p, {
name: 'AbortError',
});
});
it('should abort operation even if the .tick was not called', async (t) => {
t.mock.timers.enable({ apis: ['scheduler.wait'] });
const controller = new AbortController();
const p = nodeTimersPromises.scheduler.wait(2000, {
ref: true,
signal: controller.signal,
});

controller.abort();

await assert.rejects(() => p, {
name: 'AbortError',
});
});

it('should abort operation when .abort is called before calling setInterval', async (t) => {
t.mock.timers.enable({ apis: ['scheduler.wait'] });
const controller = new AbortController();
controller.abort();
const p = nodeTimersPromises.scheduler.wait(2000, {
ref: true,
signal: controller.signal,
});

await assert.rejects(() => p, {
name: 'AbortError',
});
});

it('should reject given an an invalid signal instance', async (t) => {
t.mock.timers.enable({ apis: ['scheduler.wait'] });
const p = nodeTimersPromises.scheduler.wait(2000, {
ref: true,
signal: {},
});

await assert.rejects(() => p, {
name: 'TypeError',
code: 'ERR_INVALID_ARG_TYPE',
});
});
});
Loading
Loading