chore(test): add optional generic parameters to matchers

This commit is contained in:
Rui Figueira 2024-10-14 22:25:33 +01:00
parent ddd1f0ce33
commit 68103850e3
4 changed files with 89 additions and 126 deletions

View file

@ -30,6 +30,8 @@ expect(value).not.toBe(2);
Compares value with [`param: expected`] by calling `Object.is`. This method compares objects by reference instead of their contents, similarly to the strict equality operator `===`.
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js
@ -144,6 +146,8 @@ The value to compare to.
Ensures that value is an instance of a class. Uses `instanceof` operator.
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js
@ -280,6 +284,8 @@ Expected substring.
Ensures that value is an `Array` or `Set` and contains an expected item.
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js
@ -305,6 +311,8 @@ For objects, this method recursively checks equality of all fields, rather than
For primitive values, this method is equivalent to [`method: GenericAssertions.toContain#2`].
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js
@ -334,6 +342,8 @@ For objects, this method recursively checks equality of all fields, rather than
For primitive values, this method is equivalent to [`method: GenericAssertions.toBe`].
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js
@ -404,6 +414,8 @@ Expected length.
Ensures that property at provided `keyPath` exists on the object and optionally checks that property is equal to the [`param: expected`]. Equality is checked recursively, similarly to [`method: GenericAssertions.toEqual`].
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js
@ -461,6 +473,8 @@ Compares contents of the value with contents of [`param: expected`], performing
When comparing arrays, the number of items must match, and each item is checked recursively.
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js
@ -494,6 +508,8 @@ Differences from [`method: GenericAssertions.toEqual`]:
* Array sparseness is checked. For example, `[, 1]` does not match `[undefined, 1]`.
* Object types are checked to be equal. For example, a class instance with fields `a` and `b` will not equal a literal object with fields `a` and `b`.
Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring expected objects have the right structure.
**Usage**
```js

View file

@ -6088,6 +6088,9 @@ interface GenericAssertions<R> {
* calling `Object.is`. This method compares objects by reference instead of their contents, similarly to the strict
* equality operator `===`.
*
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
* ```js
@ -6099,7 +6102,7 @@ interface GenericAssertions<R> {
*
* @param expected Expected value.
*/
toBe(expected: unknown): R;
toBe<E = unknown>(expected: E): R;
/**
* Compares floating point numbers for approximate equality. Use this method instead of
* [expect(value).toBe(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-be)
@ -6170,6 +6173,9 @@ interface GenericAssertions<R> {
/**
* Ensures that value is an instance of a class. Uses `instanceof` operator.
*
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
* ```js
@ -6181,7 +6187,7 @@ interface GenericAssertions<R> {
*
* @param expected The class or constructor function.
*/
toBeInstanceOf(expected: Function): R;
toBeInstanceOf<E extends Function = Function>(expected: E): R;
/**
* Ensures that `value < expected` for number or big integer values.
*
@ -6274,6 +6280,9 @@ interface GenericAssertions<R> {
/**
* Ensures that value is an `Array` or `Set` and contains an expected item.
*
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
* ```js
@ -6284,7 +6293,7 @@ interface GenericAssertions<R> {
*
* @param expected Expected value in the collection.
*/
toContain(expected: unknown): R;
toContain<E = unknown>(expected: E): R;
/**
* Ensures that value is an `Array` or `Set` and contains an item equal to the expected.
*
@ -6295,6 +6304,9 @@ interface GenericAssertions<R> {
* For primitive values, this method is equivalent to
* [expect(value).toContain(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-contain-2).
*
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
* ```js
@ -6309,7 +6321,7 @@ interface GenericAssertions<R> {
*
* @param expected Expected value in the collection.
*/
toContainEqual(expected: unknown): R;
toContainEqual<E = unknown>(expected: E): R;
/**
* Compares contents of the value with contents of
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal-option-expected),
@ -6322,6 +6334,9 @@ interface GenericAssertions<R> {
* For primitive values, this method is equivalent to
* [expect(value).toBe(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-be).
*
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
* ```js
@ -6370,7 +6385,7 @@ interface GenericAssertions<R> {
*
* @param expected Expected value.
*/
toEqual(expected: unknown): R;
toEqual<E = unknown>(expected: E): R;
/**
* Ensures that value has a `.length` property equal to
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-have-length-option-expected).
@ -6393,6 +6408,9 @@ interface GenericAssertions<R> {
* Equality is checked recursively, similarly to
* [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal).
*
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
* ```js
@ -6413,7 +6431,7 @@ interface GenericAssertions<R> {
* array items.
* @param expected Optional expected value to compare the property to.
*/
toHaveProperty(keyPath: string | Array<string>, value?: unknown): R;
toHaveProperty<E = unknown>(propertyPath: string | readonly any[], value?: E): R;
/**
* Ensures that string value matches a regular expression.
*
@ -6436,31 +6454,8 @@ interface GenericAssertions<R> {
*
* When comparing arrays, the number of items must match, and each item is checked recursively.
*
* **Usage**
*
* ```js
* const value = {
* a: 1,
* b: 2,
* c: true,
* };
* expect(value).toMatchObject({ a: 1, c: true });
* expect(value).toMatchObject({ b: 2, c: true });
*
* expect([{ a: 1, b: 2 }]).toMatchObject([{ a: 1 }]);
* ```
*
* @param expected The expected object value to match against.
*/
toMatchObject<K extends Record<string, unknown>>(expected: DeepPartial<K> | Array<DeepPartial<K>>): R;
/**
* Compares contents of the value with contents of
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-match-object-option-expected),
* performing "deep equality" check. Allows extra properties to be present in the value, unlike
* [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal),
* so you can check just a subset of object properties.
*
* When comparing arrays, the number of items must match, and each item is checked recursively.
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
@ -6478,7 +6473,7 @@ interface GenericAssertions<R> {
*
* @param expected The expected object value to match against.
*/
toMatchObject(expected: Record<string, unknown> | Array<unknown>): R;
toMatchObject<E extends {} | Array<any> = Record<string, unknown> | Array<unknown>>(expected: E): R;
/**
* Compares contents of the value with contents of
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-strict-equal-option-expected)
@ -6491,27 +6486,8 @@ interface GenericAssertions<R> {
* - Object types are checked to be equal. For example, a class instance with fields `a` and `b` will not equal a
* literal object with fields `a` and `b`.
*
* **Usage**
*
* ```js
* const value = { prop: 1 };
* expect(value).toStrictEqual({ prop: 1 });
* ```
*
* @param expected Expected value.
*/
toStrictEqual<K>(expected: K): R;
/**
* Compares contents of the value with contents of
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-strict-equal-option-expected)
* **and** their types.
*
* Differences from
* [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal):
* - Keys with undefined properties are checked. For example, `{ a: undefined, b: 2 }` does not match `{ b: 2 }`.
* - Array sparseness is checked. For example, `[, 1]` does not match `[undefined, 1]`.
* - Object types are checked to be equal. For example, a class instance with fields `a` and `b` will not equal a
* literal object with fields `a` and `b`.
* Optionally, you can provide a type for the expected value via a generic. This is particularly useful for ensuring
* expected objects have the right structure.
*
* **Usage**
*
@ -6522,7 +6498,7 @@ interface GenericAssertions<R> {
*
* @param expected Expected value.
*/
toStrictEqual(expected: unknown): R;
toStrictEqual<E = unknown>(expected: E): R;
/**
* Calls the function and ensures it throws an error.
*

View file

@ -1140,76 +1140,49 @@ test('custom asymmetric matchers should work with expect.extend', async ({ runIn
expect(result.output).not.toContain('should not run');
});
test('should check types of overloaded toMatchObject with generic parameter', async ({ runTSC }) => {
test('should compile matchers with generic parameters', async ({ runTSC }) => {
const result = await runTSC({
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';
export default defineConfig({
});
`,
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('my test', async () => {
const value = { a: 1, b: 'foo', c: { d: 2, e: 'bar' } };
expect.soft(value).toMatchObject<typeof value>({});
expect.soft(value).toMatchObject<typeof value>({ a: 1 });
expect.soft(value).toMatchObject<typeof value>({ a: 1, c: { e: 'bar' } });
expect.soft([value]).toMatchObject<typeof value>([{ a: 1 }]);
expect.soft([value]).toMatchObject<typeof value>([{ a: 1, c: { e: 'bar' } }]);
import { expect } from '@playwright/test';
expect(42).toBe<number>(42);
// @ts-expect-error
expect.soft(value).toMatchObject<typeof value>({ a: 'a' });
expect(42).toBe<number>('forty-two');
class A {}
expect(new A()).toBeInstanceOf<typeof A>(A);
// @ts-expect-error
expect.soft(value).toMatchObject<typeof value>({ c: { e: 2 } });
expect(new A()).toBeInstanceOf<typeof Object>(A);
expect([42]).toContain<number>(42);
// @ts-expect-error
expect.soft([value]).toMatchObject<typeof value>([{ a: 'a' }]);
expect([42]).toContain<number>('forty-two');
expect(42).toContainEqual<number>(42);
// @ts-expect-error
expect.soft([value]).toMatchObject<typeof value>([{ c: { e: 2 } }]);
});
`
});
expect(result.exitCode).toBe(0);
});
test('should check types of overloaded toStrictEqual with generic parameter', async ({ runTSC }) => {
const result = await runTSC({
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';
export default defineConfig({
});
`,
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('my test', async () => {
const value = { a: 1, b: 'foo', c: { d: 2, e: 'bar' } };
expect.soft(value).toStrictEqual<typeof value>({ a: 1, b: 'foo', c: { d: 2, e: 'bar' } });
expect.soft([value]).toStrictEqual<Array<typeof value>>([{ a: 1, b: 'foo', c: { d: 2, e: 'bar' } }]);
expect.soft(new Date()).toStrictEqual<Date>(new Date());
// @ts-expect-error
expect.soft(value).toStrictEqual<typeof value>({ a: 1 });
// @ts-expect-error
expect.soft(value).toStrictEqual<typeof value>({ a: 'a' });
// @ts-expect-error
expect.soft(value).toStrictEqual<typeof value>({ c: { e: 2 } });
// @ts-expect-error
expect.soft([value]).toStrictEqual<typeof value>([{ a: 'a' }]);
// @ts-expect-error
expect.soft([value]).toStrictEqual<typeof value>([{ c: { e: 2 } }]);
});
expect([42]).toContainEqual<number>('forty-two');
expect(42).toEqual<number>(42);
// @ts-expect-error
expect(42).toEqual<number>('forty-two');
expect({ a: 42 }).toHaveProperty<number>('a', 42);
// @ts-expect-error
expect({ a: 42 }).toHaveProperty<number>('a', 'forty-two');
expect({ 'a': 42 }).toMatchObject<{ a: number }>({ 'a': 42 });
// @ts-expect-error
expect({ 'a': 42 }).toMatchObject<{ a: number }>({ 'b': 42 });
expect([{ a: 42 }]).toMatchObject<Array<{ a: number }>>([{ 'a': 42 }]);
// @ts-expect-error
expect([{ a: 42 }]).toMatchObject<{ a: number }>([{ 'a': 42 }]);
expect(42).toStrictEqual<number>(42);
// @ts-expect-error
expect(42).toStrictEqual<number>('forty-two');
`
});
expect(result.exitCode).toBe(0);
});

View file

@ -273,13 +273,13 @@ interface AsymmetricMatchers {
interface GenericAssertions<R> {
not: GenericAssertions<R>;
toBe(expected: unknown): R;
toBe<E = unknown>(expected: E): R;
toBeCloseTo(expected: number, numDigits?: number): R;
toBeDefined(): R;
toBeFalsy(): R;
toBeGreaterThan(expected: number | bigint): R;
toBeGreaterThanOrEqual(expected: number | bigint): R;
toBeInstanceOf(expected: Function): R;
toBeInstanceOf<E extends Function = Function>(expected: E): R;
toBeLessThan(expected: number | bigint): R;
toBeLessThanOrEqual(expected: number | bigint): R;
toBeNaN(): R;
@ -287,16 +287,14 @@ interface GenericAssertions<R> {
toBeTruthy(): R;
toBeUndefined(): R;
toContain(expected: string): R;
toContain(expected: unknown): R;
toContainEqual(expected: unknown): R;
toEqual(expected: unknown): R;
toContain<E = unknown>(expected: E): R;
toContainEqual<E = unknown>(expected: E): R;
toEqual<E = unknown>(expected: E): R;
toHaveLength(expected: number): R;
toHaveProperty(keyPath: string | Array<string>, value?: unknown): R;
toHaveProperty<E = unknown>(propertyPath: string | readonly any[], value?: E): R;
toMatch(expected: RegExp | string): R;
toMatchObject<K extends Record<string, unknown>>(expected: DeepPartial<K> | Array<DeepPartial<K>>): R;
toMatchObject(expected: Record<string, unknown> | Array<unknown>): R;
toStrictEqual<K>(expected: K): R;
toStrictEqual(expected: unknown): R;
toMatchObject<E extends {} | Array<any> = Record<string, unknown> | Array<unknown>>(expected: E): R;
toStrictEqual<E = unknown>(expected: E): R;
toThrow(error?: unknown): R;
toThrowError(error?: unknown): R;
}