Skip to content

Commit 4aa61e3

Browse files
committed
Align AssertionResult and matchers type with expect library
1 parent baf79a1 commit 4aa61e3

4 files changed

Lines changed: 33 additions & 34 deletions

File tree

docs/Framework.md

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@ Expect-WebDriverIO is inspired by [`expect`](https://www.npmjs.com/package/expec
55

66
## Compatibility
77

8-
We can pair `expect-webdriverio` with [Jest](https://jestjs.io/), [Mocha](https://mochajs.org/), and [Jasmine](https://jasmine.github.io/).
9-
- When `expect` is defined globally, we usually overwrite it with the one from `expect-webdriverio` to have our defined assertions work out of the box.
8+
We can pair `expect-webdriverio` with [Jest](https://jestjs.io/), [Mocha](https://mochajs.org/), and even [Jasmine](https://jasmine.github.io/).
9+
10+
It is highly recommended to use it with a [WDIO Testrunner](https://webdriver.io/docs/clioptions) which provides additional auto-configuration for a plug-and-play experience.
11+
12+
When used <u>**outside of [WDIO Testrunner](https://webdriver.io/docs/clioptions)**</u>, types need to be added to your [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
1013

1114
### Jest
12-
We can use `expect-webdriverio` with [Jest](https://jestjs.io/) using either [`@jest/globals`](https://www.npmjs.com/package/@jest/globals) (preferred) or [`@types/jest`](https://www.npmjs.com/package/@types/jest) (which has global imports support).
15+
We can use `expect-webdriverio` with [Jest](https://jestjs.io/) using [`@jest/globals`](https://www.npmjs.com/package/@jest/globals) alone (preferred) and optionally [`@types/jest`](https://www.npmjs.com/package/@types/jest) (which has global ambient support).
1316
- Note: Jest maintainers do not support [`@types/jest`](https://www.npmjs.com/package/@types/jest). If this library gets out of date or has problems, support might be dropped.
14-
15-
In each case, when used <u>**outside of [WDIO Testrunner](https://webdriver.io/docs/clioptions)**</u>, types need to be added to your [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
1617
- Note: With Jest, the matchers `toMatchSnapshot` and `toMatchInlineSnapshot` are overloaded. To resolve the types correctly, `expect-webdriverio/jest` must be last.
1718

1819
#### With `@jest/globals`
19-
When paired with [Jest](https://jestjs.io/) and [`@jest/globals`](https://www.npmjs.com/package/@jest/globals), we should `import` the `expect` function from `expect-webdriverio`.
20+
When paired only with [`@jest/globals`](https://www.npmjs.com/package/@jest/globals), we should `import` the `expect` function from `expect-webdriverio`.
2021

2122
```ts
2223
import { expect } from 'expect-webdriverio'
@@ -40,28 +41,22 @@ Optionally, to avoid needing `import { expect } from 'expect-webdriverio'`, you
4041
}
4142
}
4243
```
43-
##### Augmenting Jest's `expect`
44-
Multiple attempts were made to augment `@jest/globals` to support `expect-webdriverio` matchers directly on Jest's `expect`. However, no namespace is available to augment it; therefore, only module augmentation can be used. This method does not allow adding matchers with the `extends` keyword; instead, they need to be added directly in the interface of the module declaration augmentation, which would create a lot of code duplication.
44+
##### Augmenting `@jest/globals` JestMatchers
45+
Multiple attempts were made to augment `@jest/globals` to support `expect-webdriverio` matchers directly on JestMatchers. However, no namespace is available to augment it; therefore, only module augmentation can be used. This method does not allow adding matchers with the `extends` keyword; instead, they need to be added directly in the interface of the module declaration augmentation, which would create a lot of code duplication.
4546

4647
This [Jest issue](https://github.com/jestjs/jest/issues/12424) seems to target this problem, but it is still in progress.
4748

4849

4950
#### With `@types/jest`
50-
When also paired with [`@types/jest`](https://www.npmjs.com/package/@types/jest), no imports are required. Global types are already defined correctly and you can simply use Jest's `expect` directly.
51+
When also paired with [`@types/jest`](https://www.npmjs.com/package/@types/jest), no imports are required. Global ambient types are already defined correctly and you can simply use Jest's `expect` directly.
5152

52-
Optional: If you are NOT using WDIO Testrunner, it might be required to correctly register the WDIO matchers on Jest's `expect` as shown below:
53+
If you are NOT using WDIO Testrunner, it may be required to correctly register the WDIO matchers on Jest's `expect` as shown below:
5354
```ts
55+
import { expect } from "@jest/globals";
5456
import { matchers } from "expect-webdriverio";
5557

56-
// Import and extend Jest's expect with WebdriverIO matchers
5758
beforeAll(async () => {
58-
// Convert the Map to a plain object and extend Jest's expect
59-
const matchersObject: Record<string, any> = {};
60-
matchers.forEach((matcher, name) => {
61-
matchersObject[name] = matcher;
62-
});
63-
64-
expect.extend(matchersObject);
59+
expect.extend(matchers);
6560
});
6661
```
6762

@@ -178,8 +173,8 @@ Expected in `tsconfig.json`:
178173
}
179174
```
180175

181-
#### Asymmetric matcher
182-
Asymmetric matchers have limited support. Even though `jasmine.stringContaining` has no error, it is potentially not working even with `@wdio/jasmine-framework`, but the below should work:
176+
#### Asymmetric matchers
177+
Asymmetric matchers have limited support. Even though `jasmine.stringContaining` has no error, it potentially does not work even with `@wdio/jasmine-framework`, but the example below should work:
183178

184179
```ts
185180
describe('My tests', async () => {

src/softExpect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const createSoftMatcher = <T>(
8888
}
8989

9090
// TODO ddprevost might need to review this await since not all expect requires to be awaited
91-
return await ((expectChain as unknown) as Record<string, (...args: unknown[]) => Promise<ExpectWebdriverIO.AssertionResult>>)[matcherName](...args)
91+
return await ((expectChain as unknown) as Record<string, (...args: unknown[]) => ExpectWebdriverIO.AsyncAssertionResult>)[matcherName](...args)
9292

9393
} catch (error) {
9494
// Record the failure

src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ async function executeCommandBe(
9191
received: WdioElementMaybePromise,
9292
command: (el: WebdriverIO.Element) => Promise<boolean>,
9393
options: ExpectWebdriverIO.CommandOptions
94-
): Promise<ExpectWebdriverIO.AssertionResult> {
94+
): ExpectWebdriverIO.AsyncAssertionResult {
9595
const { isNot, expectation, verb = 'be' } = this
9696

9797
let el = await received?.getElement()

types/expect-webdriverio.d.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@ type ExpectLibAsymmetricMatcher<T> = import('expect').AsymmetricMatcher<T>
1616
type ExpectLibMatchers<R extends void | Promise<void>, T> = import('expect').Matchers<R, T>
1717
type ExpectLibExpect = import('expect').Expect
1818
type ExpectLibInverse<Matchers> = import('expect').Inverse<Matchers>
19+
type ExpectLibSyncExpectationResult = import('expect').SyncExpectationResult
20+
type ExpectLibAsyncExpectationResult = import('expect').AsyncExpectationResult
21+
type ExpectLibExpectationResult = import('expect').ExpectationResult
22+
type ExpectLibMatcherContext = import('expect').MatcherContext
1923

2024
// TODO dprevost: a suggestion would be to move any code outside of the namespace to separate types.ts file, so that we can import the types.
2125

26+
// Extracted from the expect library, this is the type of the matcher function used in the expect library.
27+
type RawMatcherFn<Context extends ExpectLibMatcherContext = ExpectLibMatcherContext> = {
28+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
29+
(this: Context, actual: any, ...expected: Array<any>): ExpectLibExpectationResult;
30+
}
31+
2232
/**
2333
* Real Promise and wdio chainable promise types.
2434
*/
@@ -556,21 +566,15 @@ declare namespace ExpectWebdriverIO {
556566
afterStep(step: PickleStep, scenario: Scenario, result: { passed: boolean, error?: Error }): void
557567
}
558568

559-
interface AssertionResult {
560-
pass: boolean
561-
message(): string
562-
}
569+
interface AssertionResult extends ExpectLibSyncExpectationResult {}
570+
type AsyncAssertionResult = ExpectLibAsyncExpectationResult
563571

564572
/**
565-
* Used by the wdio main project to configure the matchers in the runner when using Jasmine.
573+
* Used by the wdio main project to configure the matchers in the runner when using Jasmine or Jest.
574+
* Equivalent as `MatchersObject` from the expect library.
575+
* @see https://github.com/jestjs/jest/blob/fd3d6cf9fe416b549a74b6577e5e1ea1130e3659/packages/expect/src/types.ts#L43C13-L43C27
566576
*/
567-
const matchers: Map<
568-
string,
569-
(
570-
actual: unknown,
571-
...expected: unknown[]
572-
) => Promise<AssertionResult>
573-
>
577+
const matchers: Record<string, RawMatcherFn>
574578

575579
interface AssertionHookParams {
576580
/**

0 commit comments

Comments
 (0)