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 `===`. 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** **Usage**
```js ```js
@ -144,6 +146,8 @@ The value to compare to.
Ensures that value is an instance of a class. Uses `instanceof` operator. 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** **Usage**
```js ```js
@ -280,6 +284,8 @@ Expected substring.
Ensures that value is an `Array` or `Set` and contains an expected item. 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** **Usage**
```js ```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`]. 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** **Usage**
```js ```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`]. 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** **Usage**
```js ```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`]. 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** **Usage**
```js ```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. 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** **Usage**
```js ```js
@ -494,6 +508,8 @@ Differences from [`method: GenericAssertions.toEqual`]:
* Array sparseness is checked. For example, `[, 1]` does not match `[undefined, 1]`. * 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`. * 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** **Usage**
```js ```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 * calling `Object.is`. This method compares objects by reference instead of their contents, similarly to the strict
* equality operator `===`. * 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** * **Usage**
* *
* ```js * ```js
@ -6099,7 +6102,7 @@ interface GenericAssertions<R> {
* *
* @param expected Expected value. * @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 * 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) * [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. * 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** * **Usage**
* *
* ```js * ```js
@ -6181,7 +6187,7 @@ interface GenericAssertions<R> {
* *
* @param expected The class or constructor function. * @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. * 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. * 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** * **Usage**
* *
* ```js * ```js
@ -6284,7 +6293,7 @@ interface GenericAssertions<R> {
* *
* @param expected Expected value in the collection. * @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. * 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 * For primitive values, this method is equivalent to
* [expect(value).toContain(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-contain-2). * [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** * **Usage**
* *
* ```js * ```js
@ -6309,7 +6321,7 @@ interface GenericAssertions<R> {
* *
* @param expected Expected value in the collection. * @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 * Compares contents of the value with contents of
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal-option-expected), * [`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 * For primitive values, this method is equivalent to
* [expect(value).toBe(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-be). * [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** * **Usage**
* *
* ```js * ```js
@ -6370,7 +6385,7 @@ interface GenericAssertions<R> {
* *
* @param expected Expected value. * @param expected Expected value.
*/ */
toEqual(expected: unknown): R; toEqual<E = unknown>(expected: E): R;
/** /**
* Ensures that value has a `.length` property equal to * Ensures that value has a `.length` property equal to
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-have-length-option-expected). * [`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 * Equality is checked recursively, similarly to
* [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal). * [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** * **Usage**
* *
* ```js * ```js
@ -6413,7 +6431,7 @@ interface GenericAssertions<R> {
* array items. * array items.
* @param expected Optional expected value to compare the property to. * @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. * 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. * When comparing arrays, the number of items must match, and each item is checked recursively.
* *
* **Usage** * 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.
* ```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.
* *
* **Usage** * **Usage**
* *
@ -6478,7 +6473,7 @@ interface GenericAssertions<R> {
* *
* @param expected The expected object value to match against. * @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 * Compares contents of the value with contents of
* [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-strict-equal-option-expected) * [`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 * - 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`. * literal object with fields `a` and `b`.
* *
* **Usage** * 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.
* ```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`.
* *
* **Usage** * **Usage**
* *
@ -6522,7 +6498,7 @@ interface GenericAssertions<R> {
* *
* @param expected Expected value. * @param expected Expected value.
*/ */
toStrictEqual(expected: unknown): R; toStrictEqual<E = unknown>(expected: E): R;
/** /**
* Calls the function and ensures it throws an error. * 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'); 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({ const result = await runTSC({
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';
export default defineConfig({
});
`,
'a.spec.ts': ` 'a.spec.ts': `
import { test, expect } from '@playwright/test'; import { 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' } }]);
expect(42).toBe<number>(42);
// @ts-expect-error // @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 // @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 // @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 // @ts-expect-error
expect.soft([value]).toMatchObject<typeof value>([{ c: { e: 2 } }]); expect([42]).toContainEqual<number>('forty-two');
});
` expect(42).toEqual<number>(42);
}); // @ts-expect-error
expect(result.exitCode).toBe(0); expect(42).toEqual<number>('forty-two');
});
expect({ a: 42 }).toHaveProperty<number>('a', 42);
test('should check types of overloaded toStrictEqual with generic parameter', async ({ runTSC }) => { // @ts-expect-error
const result = await runTSC({ expect({ a: 42 }).toHaveProperty<number>('a', 'forty-two');
'playwright.config.ts': `
import { defineConfig } from '@playwright/test'; expect({ 'a': 42 }).toMatchObject<{ a: number }>({ 'a': 42 });
// @ts-expect-error
export default defineConfig({ expect({ 'a': 42 }).toMatchObject<{ a: number }>({ 'b': 42 });
});
`, expect([{ a: 42 }]).toMatchObject<Array<{ a: number }>>([{ 'a': 42 }]);
'a.spec.ts': ` // @ts-expect-error
import { test, expect } from '@playwright/test'; expect([{ a: 42 }]).toMatchObject<{ a: number }>([{ 'a': 42 }]);
test('my test', async () => {
const value = { a: 1, b: 'foo', c: { d: 2, e: 'bar' } }; expect(42).toStrictEqual<number>(42);
// @ts-expect-error
expect.soft(value).toStrictEqual<typeof value>({ a: 1, b: 'foo', c: { d: 2, e: 'bar' } }); expect(42).toStrictEqual<number>('forty-two');
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(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });

View file

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