@rvoh/psychic-spec-helpers 3.2.2 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/src/feature/helpers/launchDevServer.js +49 -20
- package/dist/esm/src/feature/matchers/toHaveSelector.js +8 -1
- package/dist/esm/src/feature/matchers/toNotHaveSelector.js +31 -14
- package/dist/types/src/index.d.ts +1 -4
- package/package.json +3 -3
- package/src/feature/helpers/launchDevServer.ts +62 -23
- package/src/feature/matchers/toHaveSelector.ts +9 -1
- package/src/feature/matchers/toNotHaveSelector.ts +33 -11
- package/src/index.ts +8 -4
|
@@ -2,7 +2,7 @@ import { spawn } from 'child_process';
|
|
|
2
2
|
import { createServer } from 'net';
|
|
3
3
|
import sleep from '../../shared/sleep.js';
|
|
4
4
|
const devServerProcesses = {};
|
|
5
|
-
export default async function launchDevServer(key, { port = 3000, cmd = 'pnpm client', timeout =
|
|
5
|
+
export default async function launchDevServer(key, { port = 3000, cmd = 'pnpm client', timeout = 30000, } = {}) {
|
|
6
6
|
if (devServerProcesses[key])
|
|
7
7
|
return;
|
|
8
8
|
if (process.env.DEBUG === '1')
|
|
@@ -17,9 +17,14 @@ export default async function launchDevServer(key, { port = 3000, cmd = 'pnpm cl
|
|
|
17
17
|
},
|
|
18
18
|
});
|
|
19
19
|
devServerProcesses[key] = proc;
|
|
20
|
-
|
|
20
|
+
let spawnError;
|
|
21
|
+
let exited = false;
|
|
22
|
+
let exitCode = null;
|
|
23
|
+
// attached before any await so that an immediate spawn failure (e.g. ENOENT)
|
|
24
|
+
// cannot emit 'error' with no listener and crash the process
|
|
21
25
|
proc.on('error', err => {
|
|
22
|
-
|
|
26
|
+
spawnError = err;
|
|
27
|
+
console.error(`Dev server "${key}" (\`${cmd}\`) process error: ${err.message}`);
|
|
23
28
|
});
|
|
24
29
|
proc.stdout.on('data', data => {
|
|
25
30
|
if (process.env.DEBUG === '1')
|
|
@@ -29,13 +34,37 @@ export default async function launchDevServer(key, { port = 3000, cmd = 'pnpm cl
|
|
|
29
34
|
if (process.env.DEBUG === '1')
|
|
30
35
|
console.error(`Server error: ${data}`);
|
|
31
36
|
});
|
|
32
|
-
proc.on('error', err => {
|
|
33
|
-
console.error(`Server process error: ${err}`);
|
|
34
|
-
});
|
|
35
37
|
proc.on('close', code => {
|
|
36
|
-
|
|
38
|
+
exited = true;
|
|
39
|
+
exitCode = code;
|
|
40
|
+
if (devServerProcesses[key] === proc) {
|
|
41
|
+
// the process died on its own (it was not stopped via stopDevServer); evict it
|
|
42
|
+
// so it is not treated as still running, and log unconditionally so specs that
|
|
43
|
+
// subsequently fail on a dead dev server are comprehensible
|
|
44
|
+
delete devServerProcesses[key];
|
|
45
|
+
if (!spawnError)
|
|
46
|
+
console.error(`Dev server "${key}" (\`${cmd}\`) exited unexpectedly with code ${code}`);
|
|
47
|
+
}
|
|
48
|
+
else if (process.env.DEBUG === '1') {
|
|
37
49
|
console.log(`Server process exited with code ${code}`);
|
|
50
|
+
}
|
|
38
51
|
});
|
|
52
|
+
try {
|
|
53
|
+
await waitForPort(key, cmd, port, timeout, () => {
|
|
54
|
+
if (spawnError)
|
|
55
|
+
return new Error(`Dev server "${key}" (\`${cmd}\`) failed to start: ${spawnError.message}`, {
|
|
56
|
+
cause: spawnError,
|
|
57
|
+
});
|
|
58
|
+
if (exited)
|
|
59
|
+
return new Error(`Dev server "${key}" (\`${cmd}\`) exited with code ${exitCode} before listening on port ${port}`);
|
|
60
|
+
return undefined;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (devServerProcesses[key] === proc)
|
|
65
|
+
delete devServerProcesses[key];
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
39
68
|
}
|
|
40
69
|
export function stopDevServer(key) {
|
|
41
70
|
const proc = devServerProcesses[key];
|
|
@@ -45,9 +74,11 @@ export function stopDevServer(key) {
|
|
|
45
74
|
if (proc?.pid) {
|
|
46
75
|
if (process.env.DEBUG === '1')
|
|
47
76
|
console.log('Stopping server...');
|
|
77
|
+
// delete before killing so the 'close' listener can distinguish an intentional
|
|
78
|
+
// stop from the dev server dying on its own
|
|
79
|
+
delete devServerProcesses[key];
|
|
48
80
|
// serverProcess.kill('SIGINT')
|
|
49
81
|
process.kill(-proc.pid, 'SIGKILL');
|
|
50
|
-
delete devServerProcesses[key];
|
|
51
82
|
if (process.env.DEBUG === '1')
|
|
52
83
|
console.log('server stopped');
|
|
53
84
|
}
|
|
@@ -75,21 +106,19 @@ async function isPortAvailable(port) {
|
|
|
75
106
|
.listen(port, '127.0.0.1');
|
|
76
107
|
});
|
|
77
108
|
}
|
|
78
|
-
async function waitForPort(key, port, timeout
|
|
79
|
-
if (await isPortAvailable(port)) {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
109
|
+
async function waitForPort(key, cmd, port, timeout, abortError) {
|
|
82
110
|
const startTime = Date.now();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
111
|
+
// the port being available means nothing is listening on it yet,
|
|
112
|
+
// so keep waiting until the dev server has actually bound it
|
|
113
|
+
while (await isPortAvailable(port)) {
|
|
114
|
+
const error = abortError();
|
|
115
|
+
if (error)
|
|
116
|
+
throw error;
|
|
87
117
|
if (Date.now() > startTime + timeout) {
|
|
88
|
-
|
|
89
|
-
|
|
118
|
+
if (devServerProcesses[key])
|
|
119
|
+
stopDevServer(key);
|
|
120
|
+
throw new Error(`Dev server "${key}" (\`${cmd}\`) did not listen on port ${port} within ${timeout}ms`);
|
|
90
121
|
}
|
|
91
122
|
await sleep(50);
|
|
92
|
-
return await recursiveWaitForPort();
|
|
93
123
|
}
|
|
94
|
-
return await recursiveWaitForPort();
|
|
95
124
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js';
|
|
2
2
|
export default async function toHaveSelector(page, selector, opts) {
|
|
3
|
+
// Presence-only contract: this matcher asserts that the selector is
|
|
4
|
+
// attached to the DOM, regardless of visibility. Strip visible/hidden so
|
|
5
|
+
// a caller-passed { visible: true } can't narrow the check to also
|
|
6
|
+
// require visibility.
|
|
7
|
+
const waitForOpts = applyDefaultWaitForOpts(opts);
|
|
8
|
+
delete waitForOpts.visible;
|
|
9
|
+
delete waitForOpts.hidden;
|
|
3
10
|
try {
|
|
4
|
-
await page.waitForSelector(selector,
|
|
11
|
+
await page.waitForSelector(selector, waitForOpts);
|
|
5
12
|
return {
|
|
6
13
|
pass: true,
|
|
7
14
|
message: () => {
|
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js';
|
|
2
|
+
import sleep from '../../shared/sleep.js';
|
|
2
3
|
export default async function toNotHaveSelector(page, selector, opts) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
// Presence-only contract: this matcher asserts that the selector is
|
|
5
|
+
// absent from the DOM, regardless of visibility. A present-but-hidden
|
|
6
|
+
// element must FAIL this matcher, so we poll for true DOM absence via
|
|
7
|
+
// page.$ (rather than relying on waitForSelector({ hidden: true }),
|
|
8
|
+
// which only asserts invisibility). page.$ (not document.querySelector)
|
|
9
|
+
// is used so Puppeteer's extended selectors like ::-p-text keep working.
|
|
10
|
+
const timeout = applyDefaultWaitForOpts(opts).timeout ?? 5000;
|
|
11
|
+
const interval = 50;
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
async function poll() {
|
|
14
|
+
const element = await page.$(selector);
|
|
15
|
+
if (!element) {
|
|
16
|
+
return {
|
|
17
|
+
pass: true,
|
|
18
|
+
message: () => {
|
|
19
|
+
throw new Error('Cannot negate toNotHaveSelector, use toHaveSelector instead');
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (Date.now() >= startTime + timeout) {
|
|
24
|
+
await element.dispose();
|
|
25
|
+
return {
|
|
26
|
+
pass: false,
|
|
27
|
+
message: () => `Expected page to not have selector, but it did: ${selector}`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
await element.dispose();
|
|
31
|
+
await sleep(interval);
|
|
32
|
+
return await poll();
|
|
17
33
|
}
|
|
34
|
+
return await poll();
|
|
18
35
|
}
|
|
@@ -17,7 +17,6 @@ export { default as providePuppeteerViteMatchers } from './feature/helpers/provi
|
|
|
17
17
|
export { default as resetBrowserState } from './feature/helpers/resetBrowserState.js';
|
|
18
18
|
export { default as visit } from './feature/helpers/visit.js';
|
|
19
19
|
declare global {
|
|
20
|
-
const context: (typeof import('vitest'))['describe'];
|
|
21
20
|
const page: InstanceType<typeof Page>;
|
|
22
21
|
const visit: (typeof import('./feature/helpers/visit.js'))['default'];
|
|
23
22
|
const check: (typeof import('./feature/helpers/matcher-globals/check.js'))['default'];
|
|
@@ -30,9 +29,7 @@ declare global {
|
|
|
30
29
|
const uncheck: (typeof import('./feature/helpers/matcher-globals/uncheck.js'))['default'];
|
|
31
30
|
}
|
|
32
31
|
declare module 'vitest' {
|
|
33
|
-
interface
|
|
34
|
-
}
|
|
35
|
-
interface Assertion extends PuppeteerAssertions {
|
|
32
|
+
interface Matchers<T = any> extends PuppeteerAssertions {
|
|
36
33
|
}
|
|
37
34
|
}
|
|
38
35
|
interface PuppeteerAssertions {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@rvoh/psychic-spec-helpers",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.4.0",
|
|
5
5
|
"description": "psychic framework spec helpers",
|
|
6
6
|
"author": "RVO Health",
|
|
7
7
|
"publishConfig": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"uspec": "vitest --config ./spec/unit/vite.config.ts",
|
|
34
34
|
"fspec": "vitest --config ./spec/features/vite.config.ts",
|
|
35
35
|
"build": "echo \"building psychic-spec-helpers...\" && rm -rf dist && tsc -p ./tsconfig.esm.build.json",
|
|
36
|
-
"build:test-app": "rm -rf dist && echo \"building test app to esm...\" && tsc -p ./tsconfig.esm.build.test-app.json
|
|
36
|
+
"build:test-app": "rm -rf dist && echo \"building test app to esm...\" && tsc -p ./tsconfig.esm.build.test-app.json",
|
|
37
37
|
"lint": "pnpm eslint --no-warn-ignored \"src/**/*.ts\" \"spec/**/*.ts\" && pnpm prettier . --check",
|
|
38
38
|
"format": "pnpm prettier . --write",
|
|
39
39
|
"prepack": "pnpm build"
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"dotenv": "^17.2.3",
|
|
84
84
|
"pluralize-esm": "^9.0.5"
|
|
85
85
|
},
|
|
86
|
-
"packageManager": "pnpm@11.
|
|
86
|
+
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
|
|
87
87
|
}
|
|
@@ -9,7 +9,7 @@ export default async function launchDevServer(
|
|
|
9
9
|
{
|
|
10
10
|
port = 3000,
|
|
11
11
|
cmd = 'pnpm client',
|
|
12
|
-
timeout =
|
|
12
|
+
timeout = 30000,
|
|
13
13
|
}: { port?: number; cmd?: string; timeout?: number } = {}
|
|
14
14
|
) {
|
|
15
15
|
if (devServerProcesses[key]) return
|
|
@@ -28,10 +28,15 @@ export default async function launchDevServer(
|
|
|
28
28
|
|
|
29
29
|
devServerProcesses[key] = proc
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
let spawnError: Error | undefined
|
|
32
|
+
let exited = false
|
|
33
|
+
let exitCode: number | null = null
|
|
32
34
|
|
|
35
|
+
// attached before any await so that an immediate spawn failure (e.g. ENOENT)
|
|
36
|
+
// cannot emit 'error' with no listener and crash the process
|
|
33
37
|
proc.on('error', err => {
|
|
34
|
-
|
|
38
|
+
spawnError = err
|
|
39
|
+
console.error(`Dev server "${key}" (\`${cmd}\`) process error: ${err.message}`)
|
|
35
40
|
})
|
|
36
41
|
|
|
37
42
|
proc.stdout.on('data', data => {
|
|
@@ -42,13 +47,43 @@ export default async function launchDevServer(
|
|
|
42
47
|
if (process.env.DEBUG === '1') console.error(`Server error: ${data}`)
|
|
43
48
|
})
|
|
44
49
|
|
|
45
|
-
proc.on('error', err => {
|
|
46
|
-
console.error(`Server process error: ${err as unknown as string}`)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
50
|
proc.on('close', code => {
|
|
50
|
-
|
|
51
|
+
exited = true
|
|
52
|
+
exitCode = code
|
|
53
|
+
|
|
54
|
+
if (devServerProcesses[key] === proc) {
|
|
55
|
+
// the process died on its own (it was not stopped via stopDevServer); evict it
|
|
56
|
+
// so it is not treated as still running, and log unconditionally so specs that
|
|
57
|
+
// subsequently fail on a dead dev server are comprehensible
|
|
58
|
+
delete devServerProcesses[key]
|
|
59
|
+
if (!spawnError)
|
|
60
|
+
console.error(`Dev server "${key}" (\`${cmd}\`) exited unexpectedly with code ${code}`)
|
|
61
|
+
} else if (process.env.DEBUG === '1') {
|
|
62
|
+
console.log(`Server process exited with code ${code}`)
|
|
63
|
+
}
|
|
51
64
|
})
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await waitForPort(key, cmd, port, timeout, () => {
|
|
68
|
+
if (spawnError)
|
|
69
|
+
return new Error(
|
|
70
|
+
`Dev server "${key}" (\`${cmd}\`) failed to start: ${spawnError.message}`,
|
|
71
|
+
{
|
|
72
|
+
cause: spawnError,
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if (exited)
|
|
77
|
+
return new Error(
|
|
78
|
+
`Dev server "${key}" (\`${cmd}\`) exited with code ${exitCode} before listening on port ${port}`
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return undefined
|
|
82
|
+
})
|
|
83
|
+
} catch (err) {
|
|
84
|
+
if (devServerProcesses[key] === proc) delete devServerProcesses[key]
|
|
85
|
+
throw err
|
|
86
|
+
}
|
|
52
87
|
}
|
|
53
88
|
|
|
54
89
|
export function stopDevServer(key: string) {
|
|
@@ -59,9 +94,11 @@ export function stopDevServer(key: string) {
|
|
|
59
94
|
|
|
60
95
|
if (proc?.pid) {
|
|
61
96
|
if (process.env.DEBUG === '1') console.log('Stopping server...')
|
|
97
|
+
// delete before killing so the 'close' listener can distinguish an intentional
|
|
98
|
+
// stop from the dev server dying on its own
|
|
99
|
+
delete devServerProcesses[key]
|
|
62
100
|
// serverProcess.kill('SIGINT')
|
|
63
101
|
process.kill(-proc.pid, 'SIGKILL')
|
|
64
|
-
delete devServerProcesses[key]
|
|
65
102
|
|
|
66
103
|
if (process.env.DEBUG === '1') console.log('server stopped')
|
|
67
104
|
}
|
|
@@ -91,26 +128,28 @@ async function isPortAvailable(port: number): Promise<boolean> {
|
|
|
91
128
|
})
|
|
92
129
|
}
|
|
93
130
|
|
|
94
|
-
async function waitForPort(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
131
|
+
async function waitForPort(
|
|
132
|
+
key: string,
|
|
133
|
+
cmd: string,
|
|
134
|
+
port: number,
|
|
135
|
+
timeout: number,
|
|
136
|
+
abortError: () => Error | undefined
|
|
137
|
+
) {
|
|
99
138
|
const startTime = Date.now()
|
|
100
139
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
140
|
+
// the port being available means nothing is listening on it yet,
|
|
141
|
+
// so keep waiting until the dev server has actually bound it
|
|
142
|
+
while (await isPortAvailable(port)) {
|
|
143
|
+
const error = abortError()
|
|
144
|
+
if (error) throw error
|
|
105
145
|
|
|
106
146
|
if (Date.now() > startTime + timeout) {
|
|
107
|
-
stopDevServer(key)
|
|
108
|
-
throw new Error(
|
|
147
|
+
if (devServerProcesses[key]) stopDevServer(key)
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Dev server "${key}" (\`${cmd}\`) did not listen on port ${port} within ${timeout}ms`
|
|
150
|
+
)
|
|
109
151
|
}
|
|
110
152
|
|
|
111
153
|
await sleep(50)
|
|
112
|
-
return await recursiveWaitForPort()
|
|
113
154
|
}
|
|
114
|
-
|
|
115
|
-
return await recursiveWaitForPort()
|
|
116
155
|
}
|
|
@@ -6,8 +6,16 @@ export default async function toHaveSelector(
|
|
|
6
6
|
selector: string,
|
|
7
7
|
opts?: WaitForSelectorOptions
|
|
8
8
|
) {
|
|
9
|
+
// Presence-only contract: this matcher asserts that the selector is
|
|
10
|
+
// attached to the DOM, regardless of visibility. Strip visible/hidden so
|
|
11
|
+
// a caller-passed { visible: true } can't narrow the check to also
|
|
12
|
+
// require visibility.
|
|
13
|
+
const waitForOpts = applyDefaultWaitForOpts(opts)
|
|
14
|
+
delete waitForOpts.visible
|
|
15
|
+
delete waitForOpts.hidden
|
|
16
|
+
|
|
9
17
|
try {
|
|
10
|
-
await page.waitForSelector(selector,
|
|
18
|
+
await page.waitForSelector(selector, waitForOpts)
|
|
11
19
|
return {
|
|
12
20
|
pass: true,
|
|
13
21
|
message: () => {
|
|
@@ -1,23 +1,45 @@
|
|
|
1
1
|
import { Page, WaitForSelectorOptions } from 'puppeteer'
|
|
2
2
|
import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js'
|
|
3
|
+
import sleep from '../../shared/sleep.js'
|
|
3
4
|
|
|
4
5
|
export default async function toNotHaveSelector(
|
|
5
6
|
page: Page,
|
|
6
7
|
selector: string,
|
|
7
8
|
opts?: WaitForSelectorOptions
|
|
8
9
|
) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
// Presence-only contract: this matcher asserts that the selector is
|
|
11
|
+
// absent from the DOM, regardless of visibility. A present-but-hidden
|
|
12
|
+
// element must FAIL this matcher, so we poll for true DOM absence via
|
|
13
|
+
// page.$ (rather than relying on waitForSelector({ hidden: true }),
|
|
14
|
+
// which only asserts invisibility). page.$ (not document.querySelector)
|
|
15
|
+
// is used so Puppeteer's extended selectors like ::-p-text keep working.
|
|
16
|
+
const timeout = applyDefaultWaitForOpts(opts).timeout ?? 5000
|
|
17
|
+
const interval = 50
|
|
18
|
+
const startTime = Date.now()
|
|
19
|
+
|
|
20
|
+
async function poll(): Promise<{ pass: boolean; message: () => string }> {
|
|
21
|
+
const element = await page.$(selector)
|
|
22
|
+
if (!element) {
|
|
23
|
+
return {
|
|
24
|
+
pass: true,
|
|
25
|
+
message: () => {
|
|
26
|
+
throw new Error('Cannot negate toNotHaveSelector, use toHaveSelector instead')
|
|
27
|
+
},
|
|
28
|
+
}
|
|
16
29
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
|
|
31
|
+
if (Date.now() >= startTime + timeout) {
|
|
32
|
+
await element.dispose()
|
|
33
|
+
return {
|
|
34
|
+
pass: false,
|
|
35
|
+
message: () => `Expected page to not have selector, but it did: ${selector}`,
|
|
36
|
+
}
|
|
21
37
|
}
|
|
38
|
+
|
|
39
|
+
await element.dispose()
|
|
40
|
+
await sleep(interval)
|
|
41
|
+
return await poll()
|
|
22
42
|
}
|
|
43
|
+
|
|
44
|
+
return await poll()
|
|
23
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -36,7 +36,11 @@ export { default as resetBrowserState } from './feature/helpers/resetBrowserStat
|
|
|
36
36
|
export { default as visit } from './feature/helpers/visit.js'
|
|
37
37
|
|
|
38
38
|
declare global {
|
|
39
|
-
|
|
39
|
+
// `context` is deliberately NOT declared here: @rvoh/dream-spec-helpers (always
|
|
40
|
+
// present alongside this package in a psychic app) already declares it, and a
|
|
41
|
+
// second declaration is a TS2451 redeclare error whenever both declarations are
|
|
42
|
+
// checked in one program (e.g. this repo's own test-app build, where this file
|
|
43
|
+
// is source rather than a skipLibCheck-suppressed .d.ts).
|
|
40
44
|
|
|
41
45
|
const page: InstanceType<typeof Page>
|
|
42
46
|
const visit: (typeof import('./feature/helpers/visit.js'))['default']
|
|
@@ -51,10 +55,10 @@ declare global {
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
declare module 'vitest' {
|
|
58
|
+
// vitest >= 3.2 matcher augmentation point (covers expect(), expect.soft, asymmetric
|
|
59
|
+
// matchers); augmenting Assertion/ExpectStatic no longer merges under vitest 4
|
|
54
60
|
// eslint-disable-next-line
|
|
55
|
-
interface
|
|
56
|
-
// eslint-disable-next-line
|
|
57
|
-
interface Assertion extends PuppeteerAssertions {}
|
|
61
|
+
interface Matchers<T = any> extends PuppeteerAssertions {}
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
interface PuppeteerAssertions {
|