// src/vitest.setup.ts or where you define your custom matchers import {expect} from 'vitest'; expect.extend({ toEqualUnordered(received, expected) { const {isNot, promise, utils} = this; if (!Array.isArray(received)) { return { pass: false, message: () => `Received should be array, but received ${utils.printReceived(received)}`, } } if (!Array.isArray(expected)) { return { pass: false, message: () => `Expected should be array, but received ${utils.printReceived(expected)}`, } } // Create a clone of received array to check against const remainingReceived = [...received]; // Check if each element in expected exists in received for (const expectedItem of expected) { const index = remainingReceived.findIndex(receivedItem => this.equals(receivedItem, expectedItem) ); if (index === -1) { const similarItemWithSameid = ('id' in expectedItem) && remainingReceived.find(item => { if (!('id' in item)) { return false; } return item.id === expectedItem.id }) const receivedToPrint = similarItemWithSameid return { pass: false, message: () => `Expected array to contain: ${utils.printExpected(expectedItem)}\n` + `Number of items of both arrays: Received ${utils.printReceived(received.length)} | Expected ${utils.printExpected(expected.length)}` + `Received (${similarItemWithSameid ? 'similar item with id' : 'array'}): ${similarItemWithSameid ? utils.printDiffOrStringify(similarItemWithSameid, expectedItem) : utils.printReceived(received)}`, }; } // Remove the matched item to prevent duplicate matches remainingReceived.splice(index, 1); } // First check if arrays have the same length if (received.length > expected.length) { return { pass: false, message: () => `Received array has more elements than expected` + `\nExpected: ${utils.printExpected(expected.length)} elements, Received: ${utils.printExpected(received.length)}` + `\nExtra received not in expected: ${utils.printReceived(remainingReceived)}`, } return { pass: false, message: () => `Arrays have different lengths. Expected ${expected.length}, received ${received.length}`, }; } else if (received.length < expected.length) { return { pass: false, message: () => `Expected array has more elements than received` + `\nExpected: ${utils.printExpected(expected.length)} elements, Received: ${utils.printReceived(received.length)}` + `\nExtra expected not in received: ${utils.printReceived(expected.filter(item => !remainingReceived.includes(item)))}`, } return { pass: false, message: () => `Arrays have different lengths. Expected ${expected.length}, received ${received.length}`, }; } return { pass: true, message: () => `Expected arrays not to match regardless of order:\n` + `Expected: ${utils.printExpected(expected)}\n` + `Received: ${utils.printReceived(received)}`, }; }, }); // TypeScript declarations declare module 'vitest' { interface Assertion { toEqualUnordered(expected: any[]): void; } interface AsymmetricMatchersContaining { toEqualUnordered(expected: any[]): void; } }