mirror of
https://github.com/pezkuwichain/pezkuwi-dev.git
synced 2026-10-21 19:26:18 +00:00
expect: fix toBeNull, add not.toHaveLength and toHaveProperty, toThrow matches as jest does
- toBeNull and not.toBeNull took a parameter named value, which shadowed the value under test: .toBeNull() always failed and .not.toBeNull() always passed. - not.toHaveLength and toHaveProperty (dotted or array path, optional expected value with asymmetric matchers) were unimplemented stubs. - toThrow and rejects.toThrow with a string required the message to be equal; as in jest, the message now has to contain it. A RegExp matches the message and an Error compares messages.
This commit is contained in:
1 parent
c83f0adb5b
commit
709c071494
2 files changed
+116
-4
No files matched your search
+65
@@ -1,6 +1,8 @@
|
||||
// Copyright 2017-2026 @pezkuwi/dev-test authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { strict as assert } from 'node:assert';
|
||||
|
||||
describe('expect', () => {
|
||||
it('has been decorated', () => {
|
||||
expect(expect(true).not).toBeDefined();
|
||||
@@ -24,6 +26,58 @@ describe('expect', () => {
|
||||
Promise.reject(new Error('this is a rejection message'))
|
||||
).rejects.toThrow(/rejection/);
|
||||
});
|
||||
|
||||
it('matches a rejection containing the message', async () => {
|
||||
await expect(
|
||||
Promise.reject(new Error('Unable to find an element by: [data-testid="x"]\n\n<div />'))
|
||||
).rejects.toThrow('Unable to find an element by: [data-testid="x"]');
|
||||
});
|
||||
|
||||
it('fails on a rejection without the message', async () => {
|
||||
// typed as void, it returns the promise of assert.rejects
|
||||
await assert.rejects(() =>
|
||||
Promise.resolve(expect(Promise.reject(new Error('another reason'))).rejects.toThrow('rejection'))
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.toBeNull', () => {
|
||||
it('passes on null, and fails on anything else', () => {
|
||||
expect(null).toBeNull();
|
||||
expect(() => expect(undefined).toBeNull()).toThrow();
|
||||
expect(() => expect(0).toBeNull()).toThrow();
|
||||
});
|
||||
|
||||
it('supports .not', () => {
|
||||
expect(undefined).not.toBeNull();
|
||||
expect(() => expect(null).not.toBeNull()).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.toHaveLength', () => {
|
||||
it('supports .not', () => {
|
||||
expect([1]).not.toHaveLength(0);
|
||||
expect(() => expect([]).not.toHaveLength(0)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.toHaveProperty', () => {
|
||||
const value = { a: { b: [1, 2] }, c: undefined };
|
||||
|
||||
it('finds a property by key, dotted path or array path', () => {
|
||||
expect(value).toHaveProperty('c');
|
||||
expect(value).toHaveProperty('a.b');
|
||||
expect(value).toHaveProperty(['a', 'b', '1']);
|
||||
expect(() => expect(value).toHaveProperty('a.d')).toThrow();
|
||||
expect(() => expect(null).toHaveProperty('a')).toThrow();
|
||||
});
|
||||
|
||||
it('compares the value when one is given', () => {
|
||||
expect(value).toHaveProperty('a.b', [1, 2]);
|
||||
expect(value).toHaveProperty('a', { b: expect.arrayContaining([2]) });
|
||||
expect(() => expect(value).toHaveProperty('a.b', [1])).toThrow();
|
||||
expect(() => expect(value).toHaveProperty('c', null)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.toBeDefined', () => {
|
||||
@@ -59,6 +113,17 @@ describe('expect', () => {
|
||||
expect(thrower).toThrow(/me er/);
|
||||
});
|
||||
|
||||
it('matches error containing the message', () => {
|
||||
expect(thrower).toThrow('some');
|
||||
expect(thrower).toThrow(new Error('some error'));
|
||||
});
|
||||
|
||||
it('fails on an error without the message', () => {
|
||||
expect(() => expect(thrower).toThrow('other')).toThrow('Expected an error containing "other", found "some error"');
|
||||
expect(() => expect(thrower).toThrow(/other/)).toThrow();
|
||||
expect(() => expect(thrower).toThrow(new Error('some'))).toThrow();
|
||||
});
|
||||
|
||||
it('handles .not correctly (no throw, empty message)', () => {
|
||||
expect(() => undefined).not.toThrow();
|
||||
});
|
||||
|
||||
Vendored
+51
-4
@@ -279,6 +279,51 @@ function assertIncludes (value: string | unknown[], [check, Clazz]: [string | un
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* As .toThrow(check) has it: a string is contained in the message, a RegExp
|
||||
* matches it, an Error has the same message. Returns true, as the validation
|
||||
* functions of assert.throws and assert.rejects must
|
||||
*/
|
||||
function assertErrorMatches (error: unknown, check?: RegExp | Error | string): true {
|
||||
const message = error instanceof Error
|
||||
? error.message
|
||||
: String(error);
|
||||
|
||||
if (typeof check === 'string') {
|
||||
assert.ok(message.includes(check), `Expected an error containing "${check}", found "${message}"`);
|
||||
} else if (check instanceof RegExp) {
|
||||
assert.match(message, check);
|
||||
} else if (check) {
|
||||
assert.strictEqual(message, check.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* As .toHaveProperty(path, expected?) has it: the (dotted or array) path
|
||||
* exists, and when an expected value is given, equals it
|
||||
*/
|
||||
function assertHasProperty (value: unknown, path: string | string[], expected: unknown[]): void {
|
||||
const keys = Array.isArray(path)
|
||||
? path
|
||||
: path.split('.');
|
||||
let current = value;
|
||||
|
||||
for (const key of keys) {
|
||||
assert.ok(current !== null && current !== undefined && key in Object(current), `Expected a property at "${keys.join('.')}"`);
|
||||
current = (current as Record<string, unknown>)[key];
|
||||
}
|
||||
|
||||
if (expected.length) {
|
||||
assertEqualMatch(current, expected[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the shimmed expect(...) function, including all .to* and .not.to*
|
||||
* functions. This is not comprehensive, rather is contains what we need to
|
||||
@@ -301,17 +346,18 @@ export function expect () {
|
||||
not: enhanceObj({
|
||||
toBe: (other: unknown) => assert.notStrictEqual(value, other),
|
||||
toBeDefined: () => assert.ok(value === undefined),
|
||||
toBeNull: (value: unknown) => assert.ok(value !== null),
|
||||
toBeNull: () => assert.ok(value !== null, 'Expected a value other than null'),
|
||||
toBeUndefined: () => assert.ok(value !== undefined),
|
||||
toContain: (item: unknown) => assertNotContains(value, item),
|
||||
toEqual: (other: unknown) => containsMatcher(other)
|
||||
? assert.throws(() => assertEqualMatch(value, other))
|
||||
: assert.notDeepEqual(value, other),
|
||||
toHaveBeenCalled: () => assert.ok(!(value as Mocked | undefined)?.mock?.calls.length),
|
||||
toHaveLength: (length: number) => assert.notEqual((value as unknown[] | undefined)?.length, length),
|
||||
toThrow: (message?: RegExp | Error | string) => assert.doesNotThrow(value as () => unknown, message && { message } as Error)
|
||||
}, stubExpectFnNot),
|
||||
rejects: enhanceObj({
|
||||
toThrow: (message?: RegExp | Error | string) => assert.rejects(value as Promise<unknown>, message && { message } as Error)
|
||||
toThrow: (check?: RegExp | Error | string) => assert.rejects(value as Promise<unknown>, (error) => assertErrorMatches(error, check))
|
||||
}, stubExpectFnRejects),
|
||||
resolves: enhanceObj({}, stubExpectFnResolves),
|
||||
toBe: (other: unknown) => assert.strictEqual(value, other),
|
||||
@@ -319,7 +365,7 @@ export function expect () {
|
||||
toBeFalsy: () => assert.ok(!value),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
toBeInstanceOf: (Clazz: Function) => assertInstanceOf(value, Clazz),
|
||||
toBeNull: (value: unknown) => assert.ok(value === null),
|
||||
toBeNull: () => assert.ok(value === null, `Expected null, found ${String(value)}`),
|
||||
toBeTruthy: () => assert.ok(value),
|
||||
toBeUndefined: () => assert.ok(value === undefined),
|
||||
toContain: (item: unknown) => assertContains(value, item),
|
||||
@@ -331,9 +377,10 @@ export function expect () {
|
||||
toHaveBeenCalledWith: (...args: unknown[]) => assertSomeCallHasArgs((value as Mocked | undefined), args),
|
||||
toHaveBeenLastCalledWith: (...args: unknown[]) => assertCallHasArgs((value as Mocked | undefined)?.mock?.calls.at(-1), args),
|
||||
toHaveLength: (length: number) => assert.equal((value as unknown[] | undefined)?.length, length),
|
||||
toHaveProperty: (path: string | string[], ...expected: unknown[]) => assertHasProperty(value, path, expected),
|
||||
toMatch: (check: string | RegExp) => assertMatchStr(value, check),
|
||||
toMatchObject: (check: object) => assertMatchObj(value, check),
|
||||
toThrow: (message?: RegExp | Error | string) => assert.throws(value as () => unknown, message && { message } as Error)
|
||||
toThrow: (check?: RegExp | Error | string) => assert.throws(value as () => unknown, (error) => assertErrorMatches(error, check))
|
||||
}, stubExpectFn), rootMatchers), stubExpect)
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user