@@ -53,6 +53,54 @@ export interface ExtendedExpected<IsAsync = false> extends Expected<IsAsync> {
5353 * ```
5454 */
5555 toBeType : ( type : string , options ?: { nullable ?: boolean } ) => unknown
56+ /**
57+ * Asserts that the value has the specified size (variant of `.toHaveLength()` for Maps and Sets).
58+ *
59+ * ```ts
60+ * import { expect } from "./expect.ts"
61+ * expect(new Set()).toHaveSize(0)
62+ * expect(new Map([["foo", 1]])).toHaveSize(1)
63+ * ```
64+ */
65+ toHaveSize : ( size : number ) => unknown
66+ /**
67+ * Asserts a promise is resolved.
68+ *
69+ * ```ts
70+ * import { expect } from "./expect.ts"
71+ * const { promise, resolve } = Promise.withResolvers<void>()
72+ * await expect(promise).not.toBeResolvedPromise()
73+ *
74+ * ```
75+ */
76+ toBeResolvedPromise : ( ) => Promise < unknown >
77+ /**
78+ * Asserts that the function was called with the specified arguments, and exactly once.
79+ *
80+ * This is a shorthand for `.toHaveBeenCalledTimes(1)` and `.toHaveBeenCalledWith()`.
81+ *
82+ * Note that this matcher does not support the `.not` modifier.
83+ * If you expect one of the assertions to fail, you should use the adequate matchers instead.
84+ *
85+ * ```ts
86+ * import { expect, fn } from "./expect.ts"
87+ * const mock = fn()
88+ * mock("foo", 42)
89+ * expect(mock).toHaveBeenCalledOnceWith("foo", 42)
90+ * ```
91+ * @param expected The expected arguments.
92+ */
93+ toHaveBeenCalledOnceWith ( ...expected : unknown [ ] ) : unknown
94+ /**
95+ * Asserts a value is structured clonable (using `structuredClone`).
96+ *
97+ * ```ts
98+ * import { expect } from "./expect.ts"
99+ * expect({ foo: "bar" }).toBeStructuredClonable()
100+ * expect({ foo: () => {} }).not.toBeStructuredClonable()
101+ * ```
102+ */
103+ toBeStructuredClonable : ( ) => unknown
56104 /**
57105 * Asserts a property matches a given descriptor (using `Object.getOwnPropertyDescriptor`).
58106 *
@@ -393,6 +441,47 @@ _expect.extend({
393441 }
394442 } , `Expected value to {!NOT} be of type "${ type } "${ ! nullable ? " and not null but " : "" } ` )
395443 } ,
444+ toHaveSize ( context , size ) {
445+ const actual = ( context . value as { size : number } ) ?. size
446+ return process ( context . isNot , ( ) => {
447+ assert ( actual === size )
448+ } , `Expected value to {!NOT} have size ${ size } ${ size !== actual ? `: the value has size ${ actual } ` : "" } ` )
449+ } ,
450+ async toBeResolvedPromise ( context ) {
451+ if ( ! ( context . value instanceof Promise ) ) {
452+ throw new TypeError ( "Expected value to be a promise" )
453+ }
454+ const test = new Promise < void > ( ( resolve ) => setTimeout ( resolve , 0 ) )
455+ const status = await Promise . race ( [
456+ context . value . then ( ( ) => "resolved" ) ,
457+ test . then ( ( ) => "pending" ) ,
458+ ] )
459+ await test
460+ return process ( context . isNot , ( ) => {
461+ assert ( status === "resolved" )
462+ } , "Expected value to {!NOT} be resolved" )
463+ } ,
464+ toHaveBeenCalledOnceWith ( context , ...args ) {
465+ if ( context . isNot ) {
466+ throw new TypeError ( "`.not` modifier is not supported for this matcher" )
467+ }
468+ try {
469+ _expect ( context . value ) . toHaveBeenCalledTimes ( 1 )
470+ _expect ( context . value ) . toHaveBeenCalledWith ( ...args )
471+ return { message : ( ) => "" , pass : true }
472+ } catch ( error ) {
473+ return { message : ( ) => error . message , pass : false }
474+ }
475+ } ,
476+ toBeStructuredClonable ( context ) {
477+ return process ( context . isNot , ( ) => {
478+ try {
479+ structuredClone ( context . value )
480+ } catch ( error ) {
481+ throw new AssertionError ( error . message )
482+ }
483+ } , "Expected value to {!NOT} be structured clonable" )
484+ } ,
396485 toHaveDescribedProperty ( context , key , expected ) {
397486 return process ( context . isNot , ( ) => {
398487 isType ( context . value , "object" , { nullable : false } )
@@ -626,6 +715,16 @@ _expect.extend({
626715 } ,
627716} )
628717
718+ /** Reset call history of a mock or spy function. */
719+ // deno-lint-ignore no-explicit-any
720+ export function reset ( fn : any ) {
721+ const info = fn ?. [ Symbol . for ( "@MOCK" ) ]
722+ if ( ! info ) {
723+ throw new Error ( "Received function must be a mock or spy function" )
724+ }
725+ info . calls . length = 0
726+ }
727+
629728/** https://jsr.io/@std/expect/doc/~/expect. */
630729const expect = _expect as unknown as ( ( ...args : Parameters < typeof _expect > ) => ExtendedExpected ) & { [ K in keyof typeof _expect ] : typeof _expect [ K ] }
631730
0 commit comments