Skip to content

Commit 5471265

Browse files
authored
Upgrade to jest-30; use sinonjs directly (#2)
* Upgrade to jest-30 * Remove unsupported flag * Add Jest30 support * Restore old tests * Type only * Ignore certain dependencies * Refactoring * No @jest/globals * No need * Remove yet more unnecessary * Jest 30 support * Upgrade Node types * Lint * Update CI
1 parent acac9f5 commit 5471265

5 files changed

Lines changed: 1683 additions & 2488 deletions

File tree

.github/workflows/ci.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,29 @@ env:
77

88
jobs:
99
test:
10-
runs-on: ubuntu-18.04
10+
runs-on: ubuntu-latest
1111

1212
strategy:
1313
matrix:
14-
node-version: [10.x, 12.x, 14.x]
14+
node-version: [20.x, 22.x, 24.x]
1515

1616
steps:
17-
- uses: actions/checkout@v1
17+
- uses: actions/checkout@v6
1818
- name: Use Node.js ${{ matrix.node-version }}
19-
uses: actions/setup-node@v1
19+
uses: actions/setup-node@v6
2020
with:
2121
node-version: ${{ matrix.node-version }}
2222
- run: yarn --frozen-lockfile
2323
- run: yarn jest --ci
2424

2525
lint:
26-
runs-on: ubuntu-18.04
26+
runs-on: ubuntu-latest
2727

2828
steps:
29-
- uses: actions/checkout@v1
30-
- name: Use Node.js 14.x
31-
uses: actions/setup-node@v1
29+
- uses: actions/checkout@v6
30+
- name: Use Node.js 24
31+
uses: actions/setup-node@v6
3232
with:
33-
node-version: 14.x
33+
node-version: 24.x
3434
- run: yarn --frozen-lockfile
3535
- run: yarn lint

package.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"lint": "yarn prettier:check && eslint --ext .js,.jsx,.ts,.tsx,.graphql .",
1111
"lint:fix": "eslint --ext .js,.jsx,.ts,.tsx,.graphql . --fix; prettier --ignore-path .eslintignore --write '**/*.{js,jsx,ts,tsx,graphql,md,json}'",
1212
"prettier:check": "prettier --ignore-path .eslintignore --check '**/*.{js,jsx,ts,tsx,graphql,md,json}'",
13-
"test": "yarn prepack && jest && depcheck",
13+
"test": "yarn prepack && jest && depcheck --ignores @types/*,@tsconfig/*",
1414
"preversion": "grep '^### Pending' RELEASE_NOTES.md && echo \"⚠️ Cannot publish with 'Pending' in RELEASE_NOTES ⚠️\" && exit 1 || true"
1515
},
1616
"repository": {
@@ -39,10 +39,12 @@
3939
},
4040
"homepage": "https://github.com/graphile/jest-time-helpers#readme",
4141
"peerDependencies": {
42-
"jest": ">=22 <27"
42+
"jest": ">=22 <31"
4343
},
4444
"devDependencies": {
45-
"@types/node": "^14.14.21",
45+
"@tsconfig/node20": "^20.1.9",
46+
"@types/jest": "^30.0.0",
47+
"@types/node": "^20.0.0",
4648
"@typescript-eslint/eslint-plugin": "^4.13.0",
4749
"@typescript-eslint/parser": "^4.13.0",
4850
"depcheck": "^1.3.1",
@@ -51,11 +53,11 @@
5153
"eslint-plugin-import": "^2.22.1",
5254
"eslint-plugin-jest": "^24.1.3",
5355
"eslint-plugin-simple-import-sort": "^7.0.0",
54-
"jest": "^26.6.3",
56+
"jest": "^30.2.0",
5557
"prettier": "^2.2.1",
56-
"ts-jest": "^26.4.4",
58+
"ts-jest": "^29.4.6",
5759
"typedoc": "^0.20.14",
58-
"typescript": "^4.1.3"
60+
"typescript": "^5.9.3"
5961
},
6062
"files": [
6163
"dist"

src/index.ts

Lines changed: 30 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import * as util from "util";
44
// Grab the setTimeout from global before jest overwrites it with useFakeTimers
55
const setTimeoutBypassingFakes = global.setTimeout;
66

7+
type Timeout = ReturnType<typeof setTimeout>;
8+
79
/**
810
* Wait a number of milliseconds of _real time_ (not mocked time); useful for
911
* allowing the runloop or external systems to advance.
@@ -13,8 +15,8 @@ const setTimeoutBypassingFakes = global.setTimeout;
1315
export const sleep = (
1416
ms: number,
1517
unref = false,
16-
): Promise<void> & { timeout: NodeJS.Timeout } => {
17-
let timeout!: NodeJS.Timeout;
18+
): Promise<void> & { timeout: Timeout } => {
19+
let timeout!: Timeout;
1820
const promise = new Promise<void>((resolve) => {
1921
timeout = setTimeoutBypassingFakes(resolve, ms);
2022
});
@@ -74,6 +76,15 @@ async function aFewRunLoops(count = 5) {
7476
}
7577
}
7678

79+
const OriginalDate = global.Date;
80+
81+
/**
82+
* Returns the real-world current timestamp (unaffected by fake timers).
83+
*/
84+
function realNow() {
85+
return OriginalDate.now();
86+
}
87+
7788
/**
7889
* Sets up fake timers and Date implementation within your Jest test file. You
7990
* should call this at the top of the file (**not** within a test or an
@@ -89,106 +100,48 @@ async function aFewRunLoops(count = 5) {
89100
* offset.
90101
*/
91102
export function setupFakeTimers() {
92-
jest.useFakeTimers();
93-
94-
const OriginalDate = global.Date;
95-
96-
/** The offset, in milliseconds, to apply to results from `Date.now()` */
97-
let offset = 0;
98-
99-
function fakeNow() {
100-
return OriginalDate.now() + offset;
101-
}
102-
103-
/**
104-
* A copy of `Date`, but overrides `new Date()` and `Date.now()` to return a
105-
* date/timestamp factoring in `setTime()` calls.
106-
*/
107-
const FakeDate: typeof Date = function (...args: any[]) {
108-
if (args.length === 0) {
109-
// `new Date()` becomes `new Date(fakeNow())`
110-
return new OriginalDate(fakeNow());
111-
} else if (args.length === 1) {
112-
// As before
113-
return new OriginalDate(args[0]);
114-
} else {
115-
// As before
116-
return new OriginalDate(
117-
args[0],
118-
args[1],
119-
args[2],
120-
args[3],
121-
args[4],
122-
args[5],
123-
args[6],
124-
);
125-
}
126-
} as any;
127-
128-
// Copy static methods of Date, overriding Date.now()
129-
FakeDate.now = () => fakeNow();
130-
FakeDate.parse = Date.parse;
131-
FakeDate.UTC = Date.UTC;
103+
beforeEach(() => void jest.useFakeTimers());
104+
afterEach(() => void jest.useRealTimers());
132105

133106
/**
134-
* Sets the `offset` such that a call to `Date.now()` (or `new Date()`) would
135-
* return this timestamp if called immediately (but time continues to
107+
* Sets the fake time such that a call to `Date.now()` (or `new Date()`)
108+
* would return this timestamp if called immediately (but time continues to
136109
* progress as expected after this). Also advances the timers by the
137-
* difference from the previous `offset`, if positive. Setting time backwards
138-
* is allowed (like setting back the system clock on a computer), but will
139-
* not advance (or undo the advancement of) any timers.
110+
* difference from the previous time, if positive. Setting time backwards is
111+
* allowed (like setting back the system clock on a computer), but will not
112+
* advance (or undo the advancement of) any timers.
140113
*
141114
* Since advancing the time a few hours might not run all the intermediary
142115
* code quite right, we actually step it up by a configurable increment
143116
* (defaults to one minute) at a time. Setting time backwards (no matter how
144117
* far back) is done all at once.
145118
*
146-
* @param timestamp - the target timestamp
119+
* @param targetTimestamp - the target timestamp
147120
* @param increment - the maximum we should advance time by at once in order to step towards `timestamp`
148121
*/
149-
async function setTime(timestamp: number, increment = MINUTE) {
122+
async function setTime(targetTimestamp: number, increment = MINUTE) {
150123
assert.strictEqual(
151-
typeof timestamp,
124+
typeof targetTimestamp,
152125
"number",
153126
`Expected \`setTime\` to be passed a number of milliseconds, instead received '${util.inspect(
154-
timestamp,
127+
targetTimestamp,
155128
)}'`,
156129
);
157-
const finalOffset = timestamp - OriginalDate.now();
158-
const advancement = finalOffset - offset;
159-
if (advancement < 0) {
160-
offset = finalOffset;
130+
131+
if (targetTimestamp < Date.now()) {
132+
jest.setSystemTime(targetTimestamp);
161133
} else {
162-
let previousOffset = offset;
163-
while (previousOffset + increment < finalOffset) {
164-
offset = previousOffset + increment;
165-
previousOffset = offset;
134+
while (Date.now() + increment < targetTimestamp) {
166135
jest.advanceTimersByTime(increment);
167136
await aFewRunLoops();
168137
}
169-
if (previousOffset < finalOffset) {
170-
offset = finalOffset;
171-
jest.advanceTimersByTime(finalOffset - previousOffset);
138+
if (Date.now() < targetTimestamp) {
139+
jest.advanceTimersByTime(targetTimestamp - Date.now());
172140
await aFewRunLoops();
173141
}
174142
}
175143
}
176144

177-
beforeEach(() => {
178-
offset = 0;
179-
global.Date = FakeDate;
180-
});
181-
afterEach(() => {
182-
global.Date = OriginalDate;
183-
});
184-
185-
/**
186-
* Returns the real-world current timestamp (unaffected by fake timers).
187-
*/
188-
function realNow() {
189-
return OriginalDate.now();
190-
}
191-
192145
// In future we may add other methods such as `setTimeWithoutAdvancingTimers`
193146
// to emulate the system clock changing without real time elapsing.
194147
return { setTime, realNow };

tsconfig.json

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
{
2+
"extends": "@tsconfig/node20",
23
"compilerOptions": {
34
"rootDir": "src",
45
"declarationDir": "./dist",
56
"outDir": "./dist",
7+
"isolatedModules": true,
68
"declaration": true,
79
"allowJs": false,
8-
"target": "es2018",
9-
"module": "commonjs",
10-
"moduleResolution": "node",
1110
"sourceMap": true,
1211
"pretty": true,
1312
"experimentalDecorators": true,
1413
"noImplicitAny": true,
15-
"suppressImplicitAnyIndexErrors": true,
1614
"strictNullChecks": true,
1715
"noFallthroughCasesInSwitch": true,
1816
"noUnusedParameters": true,
1917
"noUnusedLocals": true,
20-
"preserveWatchOutput": true,
21-
"lib": ["es2018", "esnext.asynciterable"]
18+
"preserveWatchOutput": true
2219
},
2320
"include": ["src/**/*"],
2421
"exclude": ["node_modules", "dist"]

0 commit comments

Comments
 (0)