Skip to content

Commit

Permalink
reinstate diff between 10.1.2 and 10.1.3 minus @jest/globals support …
Browse files Browse the repository at this point in the history
…+ misc
  • Loading branch information
mulekick committed Oct 26, 2024
1 parent 3d0e828 commit 276bff2
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 408 deletions.
514 changes: 179 additions & 335 deletions README.md

Large diffs are not rendered by default.

37 changes: 19 additions & 18 deletions packages/expect-puppeteer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Modify your Jest configuration:

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.

[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.
[Puppeteer API](https://pptr.dev/api) is great, but it is low level and not designed for integration testing.

This API is designed for integration testing:

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

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

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

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

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

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

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

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

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

- `instance` <[Page]|[ElementHandle]> Context
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
- `matcher` <[string]|[RegExp]> A text or a RegExp to match in page
- `options` <[Object]> Optional parameters
- `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:
Expand All @@ -162,8 +162,8 @@ await expect(page).toMatchTextContent(/lo.*/);

Expect an element be present in the page or element.

- `instance` <[Page]|[ElementHandle]> Context
- `selector` <[string]> A [selector] to match element
- `instance` <[Page]|[Frame]|[ElementHandle]> Context
- `selector` <[string]|[MatchSelector](#MatchSelector)> A [selector] or a [MatchSelector](#MatchSelector) to match element
- `options` <[Object]> Optional parameters
- `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:
- `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
Expand All @@ -183,8 +183,8 @@ await expect(row).toClick("td:nth-child(3) a");

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

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

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

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

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

```js
import { join } from "node:path";
Expand All @@ -208,7 +208,7 @@ await expect(page).toUploadFile(
);
```

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

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

Expand Down Expand Up @@ -242,6 +242,7 @@ setDefaultOptions({ timeout: 1000 });
[element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[page]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page "Page"
[elementhandle]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-elementhandle "ElementHandle"
[page]: https://pptr.dev/api/puppeteer.page "Page"
[frame]: https://pptr.dev/api/puppeteer.frame "Frame"
[elementhandle]: https://pptr.dev/api/puppeteer.elementhandle/ "ElementHandle"
[uievent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
17 changes: 17 additions & 0 deletions packages/expect-puppeteer/src/globals.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// import jest globals
import { xdescribe, beforeAll, it, expect } from "@jest/globals";

// import jest-puppeteer globals
import "jest-puppeteer";
import "expect-puppeteer";

// test explicit imports from @jest/globals (incompatible with matchers implementation)
xdescribe("Google", (): void => {
beforeAll(async (): Promise<void> => {
await page.goto("https://google.com");
});

it('should display "google" text on page', async (): Promise<void> => {
await expect(page).not.toMatchTextContent("google", {});
});
});
1 change: 0 additions & 1 deletion packages/expect-puppeteer/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { getDefaultOptions, setDefaultOptions } from "expect-puppeteer";

// import globals
import "jest-puppeteer";
import "expect-puppeteer";

expect.addSnapshotSerializer({
print: () => "hello",
Expand Down
85 changes: 43 additions & 42 deletions packages/expect-puppeteer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ type Wrapper<T> = T extends (
? (...args: A) => R
: never;

// declare matchers list
type PuppeteerMatchers<T> = T extends PuppeteerInstance
// declare common matchers list
type InstanceMatchers<T> = T extends PuppeteerInstance
? {
// common
toClick: Wrapper<typeof toClick>;
Expand All @@ -64,24 +64,24 @@ type PuppeteerMatchers<T> = T extends PuppeteerInstance
: never;

// declare page matchers list
interface PageMatchers extends PuppeteerMatchers<Page> {
interface PageMatchers extends InstanceMatchers<Page> {
// instance specific
toDisplayDialog: Wrapper<typeof toDisplayDialog>;
// inverse matchers
not: PuppeteerMatchers<Page>[`not`] & {};
not: InstanceMatchers<Page>[`not`] & {};
}

// declare frame matchers list
interface FrameMatchers extends PuppeteerMatchers<Frame> {
interface FrameMatchers extends InstanceMatchers<Frame> {
// inverse matchers
not: PuppeteerMatchers<Frame>[`not`] & {};
not: InstanceMatchers<Frame>[`not`] & {};
}

// declare element matchers list
interface ElementHandleMatchers
extends PuppeteerMatchers<ElementHandle<Element>> {
extends InstanceMatchers<ElementHandle<Element>> {
// inverse matchers
not: PuppeteerMatchers<ElementHandle<Element>>[`not`] & {};
not: InstanceMatchers<ElementHandle<Element>>[`not`] & {};
}

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

// ---------------------------

// extend global jest object
// not possible to use PMatchersPerType directly ...
interface PuppeteerMatchers<T> {
// common
toClick: T extends PuppeteerInstance ? Wrapper<typeof toClick> : never;
toFill: T extends PuppeteerInstance ? Wrapper<typeof toFill> : never;
toFillForm: T extends PuppeteerInstance ? Wrapper<typeof toFillForm> : never;
toMatchTextContent: T extends PuppeteerInstance
? Wrapper<typeof toMatchTextContent>
: never;
toMatchElement: T extends PuppeteerInstance
? Wrapper<typeof toMatchElement>
: never;
toSelect: T extends PuppeteerInstance ? Wrapper<typeof toSelect> : never;
toUploadFile: T extends PuppeteerInstance
? Wrapper<typeof toUploadFile>
: never;
// page
toDisplayDialog: T extends Page ? Wrapper<typeof toDisplayDialog> : never;
// inverse matchers
not: {
toMatchTextContent: T extends PuppeteerInstance
? Wrapper<typeof notToMatchTextContent>
: never;
toMatchElement: T extends PuppeteerInstance
? Wrapper<typeof notToMatchElement>
: never;
};
}

// support for @types/jest
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace jest {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Matchers<R, T> {
// common
toClick: T extends PuppeteerInstance ? Wrapper<typeof toClick> : never;
toFill: T extends PuppeteerInstance ? Wrapper<typeof toFill> : never;
toFillForm: T extends PuppeteerInstance
? Wrapper<typeof toFillForm>
: never;
toMatchTextContent: T extends PuppeteerInstance
? Wrapper<typeof toMatchTextContent>
: never;
toMatchElement: T extends PuppeteerInstance
? Wrapper<typeof toMatchElement>
: never;
toSelect: T extends PuppeteerInstance ? Wrapper<typeof toSelect> : never;
toUploadFile: T extends PuppeteerInstance
? Wrapper<typeof toUploadFile>
: never;
// page
toDisplayDialog: T extends Page ? Wrapper<typeof toDisplayDialog> : never;
// inverse matchers
not: {
toMatchTextContent: T extends PuppeteerInstance
? Wrapper<typeof notToMatchTextContent>
: never;
toMatchElement: T extends PuppeteerInstance
? Wrapper<typeof notToMatchElement>
: never;
};
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unused-vars
interface Matchers<R, T> extends PuppeteerMatchers<T> {}
}
}

Expand All @@ -151,7 +152,7 @@ const wrapMatcher = <T extends PuppeteerInstance>(
instance: T,
) =>
async function throwingMatcher(...args: unknown[]): Promise<unknown> {
// ???
// update the assertions counter
jestExpect.getState().assertionCalls += 1;
try {
// run async matcher
Expand All @@ -176,7 +177,7 @@ const puppeteerExpect = <T extends PuppeteerInstance>(instance: T) => {
];

if (!isPage && !isFrame && !isHandle)
throw new Error(`${instance} is not supported`);
throw new Error(`${instance.constructor.name} is not supported`);

// retrieve matchers
const expectation = {
Expand Down Expand Up @@ -237,7 +238,7 @@ const expectPuppeteer = (<T>(actual: T) => {

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

export { expectPuppeteer as expect };
Expand Down
4 changes: 2 additions & 2 deletions packages/expect-puppeteer/src/matchers/toClick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function toClick(
selector: Selector | string,
options: ToClickOptions = {},
) {
const { delay, button, clickCount, offset, ...otherOptions } = options;
const { delay, button, count, offset, ...otherOptions } = options;
const element = await toMatchElement(instance, selector, otherOptions);
await element.click({ delay, button, clickCount, offset });
await element.click({ delay, button, count, offset });
}
28 changes: 25 additions & 3 deletions packages/jest-environment-puppeteer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,33 @@ describe("Google", () => {
});
```

## TypeScript Setup

If you’re using TypeScript, `jest-puppeteer` natively supports it from version `8.0.0`. To get started with TypeScript, follow these steps:

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:

```bash
npm uninstall --save-dev @types/jest-environment-puppeteer @types/expect-puppeteer
```

2. Install `@types/jest` (`jest-puppeteer` does not support `@jest/globals`) :

```bash
npm install --save-dev @types/jest
```

3. Import the `jest-puppeteer` module to expose the global API :

```ts
import "jest-puppeteer";
```

## API

### `global.browser`

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

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

### `global.page`

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).
Give access to a [Puppeteer Page](https://pptr.dev/api/puppeteer.page) opened at start (you will use it most of time).

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

### `global.context`

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.
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.

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

Expand Down
1 change: 0 additions & 1 deletion packages/jest-environment-puppeteer/tests/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// import globals
import "jest-puppeteer";
import "expect-puppeteer";

describe("Basic", () => {
beforeAll(async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// import globals
import "jest-puppeteer";
import "expect-puppeteer";

describe("browserContext", () => {
const test = process.env.INCOGNITO ? it : it.skip;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// import globals
import "jest-puppeteer";
import "expect-puppeteer";

describe("browserContext", () => {
const test = process.env.INCOGNITO ? it : it.skip;
Expand Down
1 change: 0 additions & 1 deletion packages/jest-environment-puppeteer/tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { readConfig } from "../src/config";

// import globals
import "jest-puppeteer";
import "expect-puppeteer";

// This test does not run on Node.js < v20 (segfault)
xdescribe("readConfig", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// import globals
import "jest-puppeteer";
import "expect-puppeteer";

describe("resetBrowser", () => {
test("should reset browser", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// import globals
import "jest-puppeteer";
import "expect-puppeteer";

describe("resetPage", () => {
test("should reset page", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// import globals
import "jest-puppeteer";
import "expect-puppeteer";

describe("runBeforeUnloadOnClose", () => {
it("shouldn’t call page.close with runBeforeUnload by default", async () => {
Expand Down

0 comments on commit 276bff2

Please sign in to comment.