Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 4 additions & 21 deletions packages/bupkis/src/assertion/impl/async-parametric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import { inspect } from 'node:util';
import { type z } from 'zod';

import { InvalidObjectSchemaError } from '../../error.js';
import { isA, isNonNullObject, isString } from '../../guards.js';
import {
ConstructibleSchema,
Expand Down Expand Up @@ -287,22 +286,14 @@ export const functionRejectWithErrorSatisfyingAssertion = createAsyncAssertion(
};
}

let schema: undefined | z.ZodType;
// TODO: can valueToSchema handle the first two conditional branches?
let schema: z.ZodType;
if (isString(param)) {
schema = createErrorMessageSchema(param);
} else if (isA(param, RegExp)) {
schema = createErrorMessageRegexSchema(param);
} else if (isNonNullObject(param)) {
} else {
schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
}
/* c8 ignore next 5 */
if (!schema) {
throw new InvalidObjectSchemaError(
`Invalid parameter schema: ${inspect(param, { depth: 2 })}`,
{ schema: param },
);
}

return {
schema,
Expand Down Expand Up @@ -350,22 +341,14 @@ export const promiseRejectWithErrorSatisfyingAssertion = createAsyncAssertion(
message: `Expected Promise to reject, but it fulfilled with ${inspect(result)}`,
};
}
let schema: undefined | z.ZodType;
// TODO: can valueToSchema handle the first two conditional branches?
let schema: z.ZodType;
if (isString(param)) {
schema = createErrorMessageSchema(param);
} else if (isA(param, RegExp)) {
schema = createErrorMessageRegexSchema(param);
} else if (isNonNullObject(param)) {
} else {
schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
}
/* c8 ignore next 5 */
if (!schema) {
throw new InvalidObjectSchemaError(
`Invalid parameter schema: ${inspect(param, { depth: 2 })}`,
{ schema: param },
);
}

return {
schema,
Expand Down
20 changes: 4 additions & 16 deletions packages/bupkis/src/assertion/impl/sync-parametric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import { inspect } from 'node:util';
import { z } from 'zod';

import { BupkisError, InvalidObjectSchemaError } from '../../error.js';
import { BupkisError } from '../../error.js';
import { isA, isError, isNonNullObject, isString } from '../../guards.js';
import {
AnyObjectSchema,
Expand Down Expand Up @@ -752,14 +752,9 @@ export const functionThrowsSatisfyingAssertion = createAssertion(
schema: createErrorMessageRegexSchema(param),
subject: error,
};
} else if (isNonNullObject(param)) {
} else {
const schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
return { schema, subject: error };
} else {
throw new InvalidObjectSchemaError(
`Invalid parameter schema: ${inspect(param, { depth: 2 })}`,
{ schema: param },
);
}
},
);
Expand Down Expand Up @@ -815,21 +810,14 @@ export const functionThrowsTypeSatisfyingAssertion = createAssertion(
: `Expected function to throw an instance of ${ctor.name}, but it threw a non-object value: ${inspect(error)}`,
};
}
let schema: undefined | z.ZodType;
// TODO: can valueToSchema handle the first two conditional branches?
let schema: z.ZodType;
if (isString(param)) {
schema = createErrorMessageSchema(param);
} else if (isA(param, RegExp)) {
schema = createErrorMessageRegexSchema(param);
} else if (isNonNullObject(param)) {
} else {
schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
}
if (!schema) {
throw new InvalidObjectSchemaError(
`Invalid parameter schema: ${inspect(param)}`,
{ schema: param },
);
}

return {
schema,
Expand Down
149 changes: 127 additions & 22 deletions packages/bupkis/test-data/async-parametric-generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import escapeStringRegexp from 'escape-string-regexp';
import fc from 'fast-check';

import * as assertions from '../src/assertion/impl/async-parametric.js';
import { expect } from '../src/index.js';
import { type AnyAssertion } from '../src/types.js';

export const AsyncParametricGenerators = new Map<AnyAssertion, GeneratorParams>(
Expand Down Expand Up @@ -44,30 +45,56 @@ export const AsyncParametricGenerators = new Map<AnyAssertion, GeneratorParams>(
],
[
assertions.functionRejectWithErrorSatisfyingAssertion,
fc
.string({ maxLength: 5, minLength: 1 })
.map(safeRegexStringFilter)
.filter((actual) => !!actual.length)
.chain((expected) =>
fc.tuple(
fc.constant(async () => {
throw new Error(expected);
}),
fc.constantFrom(
...extractPhrases(
assertions.functionRejectWithErrorSatisfyingAssertion,
fc.oneof(
// Standard cases: string, RegExp, object
fc
.string({ maxLength: 5, minLength: 1 })
.map(safeRegexStringFilter)
.filter((actual) => !!actual.length)
.chain((expected) =>
fc.tuple(
fc.constant(async () => {
throw new Error(expected);
}),
fc.constantFrom(
...extractPhrases(
assertions.functionRejectWithErrorSatisfyingAssertion,
),
),
fc.oneof(
fc.constant(expected),
fc.constant(new RegExp(escapeStringRegexp(expected))),
fc.constant({ message: expected }),
fc.constant({
message: new RegExp(escapeStringRegexp(expected)),
}),
),
),
fc.oneof(
fc.constant(expected),
fc.constant(new RegExp(escapeStringRegexp(expected))),
fc.constant({ message: expected }),
fc.constant({
message: new RegExp(escapeStringRegexp(expected)),
}),
),
// expect.it() cases - nested in object to match error properties
fc.tuple(
fc.constant(async () => {
throw new Error('test error');
}),
fc.constantFrom(
...extractPhrases(
assertions.functionRejectWithErrorSatisfyingAssertion,
),
),
fc.constant({ message: expect.it('to be a string') }),
),
fc.tuple(
fc.constant(async () => {
throw new Error('test error');
}),
fc.constantFrom(
...extractPhrases(
assertions.functionRejectWithErrorSatisfyingAssertion,
),
),
fc.constant({ message: expect.it('to match', /test/) }),
),
),
],
[
assertions.functionRejectWithTypeAssertion,
Expand Down Expand Up @@ -105,22 +132,100 @@ export const AsyncParametricGenerators = new Map<AnyAssertion, GeneratorParams>(
],
[
assertions.promiseRejectWithErrorSatisfyingAssertion,
fc.string().chain((message) =>
fc.oneof(
// String match
fc.string().chain((message) =>
fc.tuple(
fc.constant({
then(
_resolve: (value: any) => void,
reject: (reason: any) => void,
) {
reject(new Error(message));
},
}),
fc.constantFrom(
...extractPhrases(
assertions.promiseRejectWithErrorSatisfyingAssertion,
),
),
fc.constant(message),
),
),
// RegExp match
fc
.string({ maxLength: 5, minLength: 1 })
.map(safeRegexStringFilter)
.filter((msg) => !!msg.length)
.chain((message) =>
fc.tuple(
fc.constant({
then(
_resolve: (value: any) => void,
reject: (reason: any) => void,
) {
reject(new Error(message));
},
}),
fc.constantFrom(
...extractPhrases(
assertions.promiseRejectWithErrorSatisfyingAssertion,
),
),
fc.constant(new RegExp(escapeStringRegexp(message))),
),
),
// Object match
fc.string().chain((message) =>
fc.tuple(
fc.constant({
then(
_resolve: (value: any) => void,
reject: (reason: any) => void,
) {
reject(new Error(message));
},
}),
fc.constantFrom(
...extractPhrases(
assertions.promiseRejectWithErrorSatisfyingAssertion,
),
),
fc.constant({ message }),
),
),
// expect.it() cases - nested in object to match error properties
fc.tuple(
fc.constant({
then(
_resolve: (value: any) => void,
reject: (reason: any) => void,
) {
reject(new Error('test error'));
},
}),
fc.constantFrom(
...extractPhrases(
assertions.promiseRejectWithErrorSatisfyingAssertion,
),
),
fc.constant({ message: expect.it('to be a string') }),
),
fc.tuple(
fc.constant({
then(
_resolve: (value: any) => void,
reject: (reason: any) => void,
) {
reject(new Error(message));
reject(new Error('test error'));
},
}),
fc.constantFrom(
...extractPhrases(
assertions.promiseRejectWithErrorSatisfyingAssertion,
),
),
fc.constant(message),
fc.constant({ message: expect.it('to match', /test/) }),
),
),
],
Expand Down
Loading
Loading