Skip to content

Commit 911b243

Browse files
authored
feat: @jest/globals support (#606)
1 parent 3d0e828 commit 911b243

File tree

14 files changed

+285
-408
lines changed

14 files changed

+285
-408
lines changed

README.md

Lines changed: 179 additions & 335 deletions
Large diffs are not rendered by default.

packages/expect-puppeteer/README.md

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Modify your Jest configuration:
2424

2525
Writing integration test is very hard, especially when you are testing a Single Page Applications. Data are loaded asynchronously and it is difficult to know exactly when an element will be displayed in the page.
2626

27-
[Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md) is great, but it is low level and not designed for integration testing.
27+
[Puppeteer API](https://pptr.dev/api) is great, but it is low level and not designed for integration testing.
2828

2929
This API is designed for integration testing:
3030

@@ -81,11 +81,11 @@ await expect(page).toMatchElement("div.inner", { text: "some text" });
8181

8282
Expect an element to be in the page or element, then click on it.
8383

84-
- `instance` <[Page]|[ElementHandle]> Context
84+
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
8585
- `selector` <[string]|[MatchSelector](#MatchSelector)> A [selector] or a [MatchSelector](#MatchSelector) to click on.
8686
- `options` <[Object]> Optional parameters
8787
- `button` <"left"|"right"|"middle"> Defaults to `left`.
88-
- `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
88+
- `count` <[number]> defaults to 1. See [UIEvent.detail].
8989
- `delay` <[number]> Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.
9090
- `text` <[string]|[RegExp]> A text or a RegExp to match in element `textContent`.
9191

@@ -111,8 +111,8 @@ const dialog = await expect(page).toDisplayDialog(async () => {
111111

112112
Expect a control to be in the page or element, then fill it with text.
113113

114-
- `instance` <[Page]|[ElementHandle]> Context
115-
- `selector` <[string]> A [selector] to match field
114+
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
115+
- `selector` <[string]|[MatchSelector](#MatchSelector)> A [selector] or a [MatchSelector](#MatchSelector) to match field
116116
- `value` <[string]> Value to fill
117117
- `options` <[Object]> Optional parameters
118118
- `delay` <[number]> delay to pass to [the puppeteer `element.type` API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandletypetext-options)
@@ -125,8 +125,8 @@ await expect(page).toFill('input[name="firstName"]', "James");
125125

126126
Expect a form to be in the page or element, then fill its controls.
127127

128-
- `instance` <[Page]|[ElementHandle]> Context
129-
- `selector` <[string]> A [selector] to match form
128+
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
129+
- `selector` <[string]|[MatchSelector](#MatchSelector)> A [selector] or a [MatchSelector](#MatchSelector) to match form
130130
- `values` <[Object]> Values to fill
131131
- `options` <[Object]> Optional parameters
132132
- `delay` <[number]> delay to pass to [the puppeteer `element.type` API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandletypetext-options)
@@ -142,7 +142,7 @@ await expect(page).toFillForm('form[name="myForm"]', {
142142

143143
Expect a text or a string RegExp to be present in the page or element.
144144

145-
- `instance` <[Page]|[ElementHandle]> Context
145+
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
146146
- `matcher` <[string]|[RegExp]> A text or a RegExp to match in page
147147
- `options` <[Object]> Optional parameters
148148
- `polling` <[string]|[number]> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
@@ -162,8 +162,8 @@ await expect(page).toMatchTextContent(/lo.*/);
162162

163163
Expect an element be present in the page or element.
164164

165-
- `instance` <[Page]|[ElementHandle]> Context
166-
- `selector` <[string]> A [selector] to match element
165+
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
166+
- `selector` <[string]|[MatchSelector](#MatchSelector)> A [selector] or a [MatchSelector](#MatchSelector) to match element
167167
- `options` <[Object]> Optional parameters
168168
- `polling` <[string]|[number]> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
169169
- `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
@@ -183,8 +183,8 @@ await expect(row).toClick("td:nth-child(3) a");
183183

184184
Expect a select control to be present in the page or element, then select the specified option.
185185

186-
- `instance` <[Page]|[ElementHandle]> Context
187-
- `selector` <[string]> A [selector] to match select [element]
186+
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
187+
- `selector` <[string]|[MatchSelector](#MatchSelector)> A [selector] or a [MatchSelector](#MatchSelector) to match select [element]
188188
- `valueOrText` <[string]> Value or text matching option
189189

190190
```js
@@ -195,9 +195,9 @@ await expect(page).toSelect('select[name="choices"]', "Choice 1");
195195

196196
Expect a input file control to be present in the page or element, then fill it with a local file.
197197

198-
- `instance` <[Page]|[ElementHandle]> Context
199-
- `selector` <[string]> A [selector] to match input [element]
200-
- `filePath` <[string]> A file path
198+
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
199+
- `selector` <[string]|[MatchSelector](#MatchSelector)> A [selector] or a [MatchSelector](#MatchSelector) to match input [element]
200+
- `filePath` <[string]|[Array]<[string]>> A file path or array of file paths
201201

202202
```js
203203
import { join } from "node:path";
@@ -208,7 +208,7 @@ await expect(page).toUploadFile(
208208
);
209209
```
210210

211-
### <a name="MatchSelector"></a>{type: [string], value: [string]}
211+
### <a name="MatchSelector"></a>Match Selector
212212

213213
An object used as parameter in order to select an element.
214214

@@ -242,6 +242,7 @@ setDefaultOptions({ timeout: 1000 });
242242
[element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
243243
[map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
244244
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
245-
[page]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page "Page"
246-
[elementhandle]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-elementhandle "ElementHandle"
245+
[page]: https://pptr.dev/api/puppeteer.page "Page"
246+
[frame]: https://pptr.dev/api/puppeteer.frame "Frame"
247+
[elementhandle]: https://pptr.dev/api/puppeteer.elementhandle/ "ElementHandle"
247248
[uievent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// import jest globals
2+
import { xdescribe, beforeAll, it, expect } from "@jest/globals";
3+
4+
// import jest-puppeteer globals
5+
import "jest-puppeteer";
6+
import "expect-puppeteer";
7+
8+
// test explicit imports from @jest/globals (incompatible with matchers implementation)
9+
xdescribe("Google", (): void => {
10+
beforeAll(async (): Promise<void> => {
11+
await page.goto("https://google.com");
12+
});
13+
14+
it('should display "google" text on page', async (): Promise<void> => {
15+
await expect(page).not.toMatchTextContent("google", {});
16+
});
17+
});

packages/expect-puppeteer/src/index.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { getDefaultOptions, setDefaultOptions } from "expect-puppeteer";
22

33
// import globals
44
import "jest-puppeteer";
5-
import "expect-puppeteer";
65

76
expect.addSnapshotSerializer({
87
print: () => "hello",

packages/expect-puppeteer/src/index.ts

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ type Wrapper<T> = T extends (
4444
? (...args: A) => R
4545
: never;
4646

47-
// declare matchers list
48-
type PuppeteerMatchers<T> = T extends PuppeteerInstance
47+
// declare common matchers list
48+
type InstanceMatchers<T> = T extends PuppeteerInstance
4949
? {
5050
// common
5151
toClick: Wrapper<typeof toClick>;
@@ -64,24 +64,24 @@ type PuppeteerMatchers<T> = T extends PuppeteerInstance
6464
: never;
6565

6666
// declare page matchers list
67-
interface PageMatchers extends PuppeteerMatchers<Page> {
67+
interface PageMatchers extends InstanceMatchers<Page> {
6868
// instance specific
6969
toDisplayDialog: Wrapper<typeof toDisplayDialog>;
7070
// inverse matchers
71-
not: PuppeteerMatchers<Page>[`not`] & {};
71+
not: InstanceMatchers<Page>[`not`] & {};
7272
}
7373

7474
// declare frame matchers list
75-
interface FrameMatchers extends PuppeteerMatchers<Frame> {
75+
interface FrameMatchers extends InstanceMatchers<Frame> {
7676
// inverse matchers
77-
not: PuppeteerMatchers<Frame>[`not`] & {};
77+
not: InstanceMatchers<Frame>[`not`] & {};
7878
}
7979

8080
// declare element matchers list
8181
interface ElementHandleMatchers
82-
extends PuppeteerMatchers<ElementHandle<Element>> {
82+
extends InstanceMatchers<ElementHandle<Element>> {
8383
// inverse matchers
84-
not: PuppeteerMatchers<ElementHandle<Element>>[`not`] & {};
84+
not: InstanceMatchers<ElementHandle<Element>>[`not`] & {};
8585
}
8686

8787
// declare matchers per instance type
@@ -103,40 +103,41 @@ type GlobalWithExpect = typeof globalThis & { expect: PuppeteerExpect };
103103

104104
// ---------------------------
105105

106-
// extend global jest object
106+
// not possible to use PMatchersPerType directly ...
107+
interface PuppeteerMatchers<T> {
108+
// common
109+
toClick: T extends PuppeteerInstance ? Wrapper<typeof toClick> : never;
110+
toFill: T extends PuppeteerInstance ? Wrapper<typeof toFill> : never;
111+
toFillForm: T extends PuppeteerInstance ? Wrapper<typeof toFillForm> : never;
112+
toMatchTextContent: T extends PuppeteerInstance
113+
? Wrapper<typeof toMatchTextContent>
114+
: never;
115+
toMatchElement: T extends PuppeteerInstance
116+
? Wrapper<typeof toMatchElement>
117+
: never;
118+
toSelect: T extends PuppeteerInstance ? Wrapper<typeof toSelect> : never;
119+
toUploadFile: T extends PuppeteerInstance
120+
? Wrapper<typeof toUploadFile>
121+
: never;
122+
// page
123+
toDisplayDialog: T extends Page ? Wrapper<typeof toDisplayDialog> : never;
124+
// inverse matchers
125+
not: {
126+
toMatchTextContent: T extends PuppeteerInstance
127+
? Wrapper<typeof notToMatchTextContent>
128+
: never;
129+
toMatchElement: T extends PuppeteerInstance
130+
? Wrapper<typeof notToMatchElement>
131+
: never;
132+
};
133+
}
134+
135+
// support for @types/jest
107136
declare global {
108137
// eslint-disable-next-line @typescript-eslint/no-namespace
109138
namespace jest {
110-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
111-
interface Matchers<R, T> {
112-
// common
113-
toClick: T extends PuppeteerInstance ? Wrapper<typeof toClick> : never;
114-
toFill: T extends PuppeteerInstance ? Wrapper<typeof toFill> : never;
115-
toFillForm: T extends PuppeteerInstance
116-
? Wrapper<typeof toFillForm>
117-
: never;
118-
toMatchTextContent: T extends PuppeteerInstance
119-
? Wrapper<typeof toMatchTextContent>
120-
: never;
121-
toMatchElement: T extends PuppeteerInstance
122-
? Wrapper<typeof toMatchElement>
123-
: never;
124-
toSelect: T extends PuppeteerInstance ? Wrapper<typeof toSelect> : never;
125-
toUploadFile: T extends PuppeteerInstance
126-
? Wrapper<typeof toUploadFile>
127-
: never;
128-
// page
129-
toDisplayDialog: T extends Page ? Wrapper<typeof toDisplayDialog> : never;
130-
// inverse matchers
131-
not: {
132-
toMatchTextContent: T extends PuppeteerInstance
133-
? Wrapper<typeof notToMatchTextContent>
134-
: never;
135-
toMatchElement: T extends PuppeteerInstance
136-
? Wrapper<typeof notToMatchElement>
137-
: never;
138-
};
139-
}
139+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unused-vars
140+
interface Matchers<R, T> extends PuppeteerMatchers<T> {}
140141
}
141142
}
142143

@@ -151,7 +152,7 @@ const wrapMatcher = <T extends PuppeteerInstance>(
151152
instance: T,
152153
) =>
153154
async function throwingMatcher(...args: unknown[]): Promise<unknown> {
154-
// ???
155+
// update the assertions counter
155156
jestExpect.getState().assertionCalls += 1;
156157
try {
157158
// run async matcher
@@ -176,7 +177,7 @@ const puppeteerExpect = <T extends PuppeteerInstance>(instance: T) => {
176177
];
177178

178179
if (!isPage && !isFrame && !isHandle)
179-
throw new Error(`${instance} is not supported`);
180+
throw new Error(`${instance.constructor.name} is not supported`);
180181

181182
// retrieve matchers
182183
const expectation = {
@@ -237,7 +238,7 @@ const expectPuppeteer = (<T>(actual: T) => {
237238

238239
Object.keys(jestExpect).forEach((prop) => {
239240
// @ts-expect-error add jest expect properties to expect-puppeteer implementation
240-
expectPuppeteer[prop] = jestExpect[prop];
241+
expectPuppeteer[prop] = jestExpect[prop] as unknown;
241242
});
242243

243244
export { expectPuppeteer as expect };

packages/expect-puppeteer/src/matchers/toClick.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export async function toClick(
99
selector: Selector | string,
1010
options: ToClickOptions = {},
1111
) {
12-
const { delay, button, clickCount, offset, ...otherOptions } = options;
12+
const { delay, button, count, offset, ...otherOptions } = options;
1313
const element = await toMatchElement(instance, selector, otherOptions);
14-
await element.click({ delay, button, clickCount, offset });
14+
await element.click({ delay, button, count, offset });
1515
}

packages/jest-environment-puppeteer/README.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,33 @@ describe("Google", () => {
3737
});
3838
```
3939

40+
## TypeScript Setup
41+
42+
If you’re using TypeScript, `jest-puppeteer` natively supports it from version `8.0.0`. To get started with TypeScript, follow these steps:
43+
44+
1. Make sure your project is using the correct type definitions. If you’ve upgraded to version `10.1.2` or above, uninstall old types:
45+
46+
```bash
47+
npm uninstall --save-dev @types/jest-environment-puppeteer @types/expect-puppeteer
48+
```
49+
50+
2. Install `@types/jest` (`jest-puppeteer` does not support `@jest/globals`) :
51+
52+
```bash
53+
npm install --save-dev @types/jest
54+
```
55+
56+
3. Import the `jest-puppeteer` module to expose the global API :
57+
58+
```ts
59+
import "jest-puppeteer";
60+
```
61+
4062
## API
4163

4264
### `global.browser`
4365

44-
Give access to the [Puppeteer Browser](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-browser).
66+
Give access to the [Puppeteer Browser](https://pptr.dev/api/puppeteer.browser).
4567

4668
```js
4769
it("should open a new page", async () => {
@@ -52,7 +74,7 @@ it("should open a new page", async () => {
5274

5375
### `global.page`
5476

55-
Give access to a [Puppeteer Page](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page) opened at start (you will use it most of time).
77+
Give access to a [Puppeteer Page](https://pptr.dev/api/puppeteer.page) opened at start (you will use it most of time).
5678

5779
```js
5880
it("should fill an input", async () => {
@@ -62,7 +84,7 @@ it("should fill an input", async () => {
6284

6385
### `global.context`
6486

65-
Give access to a [browser context](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-browsercontext) that is instantiated when the browser is launched. You can control whether each test has its own isolated browser context using the `browserContext` option in config.
87+
Give access to a [browser context](https://pptr.dev/api/puppeteer.browsercontext) that is instantiated when the browser is launched. You can control whether each test has its own isolated browser context using the `browserContext` option in config.
6688

6789
### `global.jestPuppeteer.debug()`
6890

packages/jest-environment-puppeteer/tests/basic.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// import globals
22
import "jest-puppeteer";
3-
import "expect-puppeteer";
43

54
describe("Basic", () => {
65
beforeAll(async () => {

packages/jest-environment-puppeteer/tests/browserContext-1.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// import globals
22
import "jest-puppeteer";
3-
import "expect-puppeteer";
43

54
describe("browserContext", () => {
65
const test = process.env.INCOGNITO ? it : it.skip;

packages/jest-environment-puppeteer/tests/browserContext-2.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// import globals
22
import "jest-puppeteer";
3-
import "expect-puppeteer";
43

54
describe("browserContext", () => {
65
const test = process.env.INCOGNITO ? it : it.skip;

0 commit comments

Comments
 (0)