Skip to content

Commit aea600a

Browse files
committed
feat: use custom toIncludeSameMembers matcher
1 parent 52ccf24 commit aea600a

File tree

8 files changed

+107
-5
lines changed

8 files changed

+107
-5
lines changed

tests/integration/tools/mongodb/create/createCollection.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describeWithMongoDB("createCollection tool", (integration) => {
5555
expect(content).toEqual(`Collection "collection2" created in database "${integration.randomDbName()}".`);
5656
collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
5757
expect(collections).toHaveLength(2);
58-
expect(collections.map((c) => c.name)).toEqual(expect.arrayContaining(["collection1", "collection2"]));
58+
expect(collections.map((c) => c.name)).toIncludeSameMembers(["collection1", "collection2"]);
5959
});
6060

6161
it("does nothing if collection already exists", async () => {

tests/integration/tools/mongodb/metadata/listCollections.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ describeWithMongoDB("listCollections tool", (integration) => {
5656
});
5757
const items2 = getResponseElements(response2.content);
5858
expect(items2).toHaveLength(2);
59-
expect(items2.map((item) => item.text)).toEqual(
60-
expect.arrayContaining(['Name: "collection-1"', 'Name: "collection-2"'])
61-
);
59+
expect(items2.map((item) => item.text)).toIncludeSameMembers([
60+
'Name: "collection-1"',
61+
'Name: "collection-2"',
62+
]);
6263
});
6364
});
6465

tests/integration/tools/mongodb/metadata/listDatabases.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describeWithMongoDB("listDatabases tool", (integration) => {
3535

3636
const response = await integration.mcpClient().callTool({ name: "list-databases", arguments: {} });
3737
const dbNames = getDbNames(response.content);
38-
expect(dbNames).toEqual(expect.arrayContaining([...defaultDatabases, "foo", "baz"]));
38+
expect(dbNames).toIncludeSameMembers([...defaultDatabases, "foo", "baz"]);
3939
});
4040
});
4141

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
describe("toIncludeSameMembers matcher", () => {
4+
it("should pass when arrays contain the same elements in different order", () => {
5+
const array1 = [1, 2, 3];
6+
const array2 = [3, 1, 2];
7+
8+
expect(array1).toIncludeSameMembers(array2);
9+
});
10+
11+
it("should pass when arrays contain the same elements in same order", () => {
12+
const array1 = [1, 2, 3];
13+
const array2 = [1, 2, 3];
14+
15+
expect(array1).toIncludeSameMembers(array2);
16+
});
17+
18+
it("should fail when arrays have different lengths", () => {
19+
const array1 = [1, 2, 3];
20+
const array2 = [1, 2];
21+
22+
expect(() => expect(array1).toIncludeSameMembers(array2)).toThrow();
23+
});
24+
25+
it("should fail when arrays contain different elements", () => {
26+
const array1 = [1, 2, 3];
27+
const array2 = [4, 5, 6];
28+
29+
expect(() => expect(array1).toIncludeSameMembers(array2)).toThrow();
30+
});
31+
32+
it("should work with string arrays", () => {
33+
const array1 = ["apple", "banana", "cherry"];
34+
const array2 = ["cherry", "apple", "banana"];
35+
36+
expect(array1).toIncludeSameMembers(array2);
37+
});
38+
39+
it("should work with object arrays", () => {
40+
const array1 = [{ name: "Alice" }, { name: "Bob" }];
41+
const array2 = [{ name: "Bob" }, { name: "Alice" }];
42+
43+
expect(array1).toIncludeSameMembers(array2);
44+
});
45+
46+
it("should work with mixed type arrays", () => {
47+
const array1 = [1, "hello", { key: "value" }];
48+
const array2 = [{ key: "value" }, 1, "hello"];
49+
50+
expect(array1).toIncludeSameMembers(array2);
51+
});
52+
53+
it("should work with empty arrays", () => {
54+
const array1: unknown[] = [];
55+
const array2: unknown[] = [];
56+
57+
expect(array1).toIncludeSameMembers(array2);
58+
});
59+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { expect } from "vitest";
2+
3+
export function toIncludeSameMembers<T>(actual: T[], expected: T[]): { pass: boolean; message: () => string } {
4+
let pass = false;
5+
try {
6+
expect(actual).toEqual(expect.arrayContaining(expected as unknown[]));
7+
expect(expected).toEqual(expect.arrayContaining(actual as unknown[]));
8+
pass = true;
9+
} catch {
10+
pass = false;
11+
}
12+
13+
return {
14+
pass,
15+
message: () => {
16+
if (pass) {
17+
return `Expected arrays not to include the same members`;
18+
} else {
19+
return `Expected arrays to include the same members.\nExpected: ${JSON.stringify(expected)}\nReceived: ${JSON.stringify(actual)}`;
20+
}
21+
},
22+
};
23+
}

tests/setup.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { expect } from "vitest";
2+
import { toIncludeSameMembers } from "./matchers/toIncludeSameMembers.js";
3+
4+
// Extend vitest's expect with custom matchers
5+
expect.extend({
6+
toIncludeSameMembers,
7+
});

tests/vitest.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import "vitest";
2+
3+
declare module "vitest" {
4+
interface Assertion<T = unknown> {
5+
toIncludeSameMembers<U>(expected: U[]): T;
6+
}
7+
8+
interface AsymmetricMatchersContaining {
9+
toIncludeSameMembers<T>(expected: T[]): unknown;
10+
}
11+
}

vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default defineConfig({
66
testTimeout: 3600000,
77
hookTimeout: 3600000,
88
include: ["**/*.test.ts"],
9+
setupFiles: ["./tests/setup.ts"],
910
coverage: {
1011
exclude: ["node_modules", "tests", "dist"],
1112
reporter: ["lcov"],

0 commit comments

Comments
 (0)