test: fix broken suites

This commit is contained in:
Linus Rath
2026-07-22 18:56:09 +02:00
parent 3f22a3323a
commit 813185e58d
6 changed files with 30 additions and 10 deletions
@@ -136,6 +136,7 @@ vi.mock('@/lib/plugin-hooks', () => ({
getRecipientSuggestions: { call: async () => [] },
onSend: { call: async () => [] },
beforeSend: { call: async () => [] },
onRecipientChipsChange: { transform: async (chips: unknown) => chips },
},
contactHooks: {
search: { call: async () => [] },
@@ -229,8 +230,10 @@ describe('RecipientChipInput drag and drop', () => {
const dt = new MockDataTransfer();
fireEvent.dragStart(chipSpan, { dataTransfer: dt });
// The enrichment pass may have stamped extra display metadata on the
// chip by drag time, so match the essential fields rather than deep-equal.
const payload = JSON.parse(dt.getData('application/x-recipient-chip'));
expect(payload).toEqual({ recipient: { email: 'alice@example.com' }, fromField: 'to', fromIndex: 0 });
expect(payload).toMatchObject({ recipient: { email: 'alice@example.com' }, fromField: 'to', fromIndex: 0 });
});
it('keeps a display name with a comma in a single chip (array model)', async () => {
@@ -244,7 +247,7 @@ describe('RecipientChipInput drag and drop', () => {
const dt = new MockDataTransfer();
fireEvent.dragStart(chipSpan, { dataTransfer: dt });
const payload = JSON.parse(dt.getData('application/x-recipient-chip'));
expect(payload).toEqual({ recipient: { name: 'Doo, John', email: 'john@doo.org' }, fromField: 'to', fromIndex: 0 });
expect(payload).toMatchObject({ recipient: { name: 'Doo, John', email: 'john@doo.org' }, fromField: 'to', fromIndex: 0 });
});
it('onDragEnd clears the opacity class on the chip', async () => {
@@ -135,6 +135,7 @@ vi.mock('@/lib/plugin-hooks', () => ({
getRecipientSuggestions: { call: async () => [] },
onSend: { call: async () => [] },
beforeSend: { call: async () => [] },
onRecipientChipsChange: { transform: async (chips: unknown) => chips },
},
contactHooks: {
search: { call: async () => [] },
+10 -1
View File
@@ -1,6 +1,15 @@
import { unlink, writeFileSync } from "fs";
import { mkdtempSync, unlink, writeFileSync } from "fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
// The route consults admin-dashboard overrides (ADMIN_CONFIG_DIR, default
// data/admin) before env vars. Point it at an empty temp dir so local admin
// state on the developer's machine can't leak into these env-driven
// assertions. Must happen before the first GET, because the config manager
// singleton loads the directory once and caches it.
process.env.ADMIN_CONFIG_DIR = mkdtempSync(path.join(tmpdir(), 'bw-config-route-'));
// Mock NextResponse before importing the route
vi.mock('next/server', () => ({
NextResponse: {
@@ -302,6 +302,11 @@ describe('JMAPClient resilience', () => {
});
it('does not mark the connection lost or reconnect repeatedly while rate limited', async () => {
// The file-level shouldAdvanceTime lets fake time creep forward with
// the wall clock, which can fire the 30s keep-alive an extra time on a
// slow or loaded machine. This test counts pings, so pin the clock and
// advance it explicitly.
vi.useFakeTimers({ shouldAdvanceTime: false });
const client = await createConnectedClient();
const callback = vi.fn();
client.onConnectionChange(callback);
+4 -4
View File
@@ -247,18 +247,18 @@ describe('contact-store', () => {
expect(results.length).toBeLessThanOrEqual(10);
});
it('should expand group members when group name matches', () => {
it('should suggest a matching group as a single entry with member count', () => {
const member1 = makeContact({ id: 'm1', name: { components: [{ kind: 'given', value: 'Alice' }], isOrdered: true }, emails: { e0: { address: 'alice@test.com' } } });
const member2 = makeContact({ id: 'm2', name: { components: [{ kind: 'given', value: 'Bob' }], isOrdered: true }, emails: { e0: { address: 'bob@test.com' } } });
const group = makeGroup({ id: 'g1', members: { m1: true, m2: true } });
useContactStore.setState({ contacts: [member1, member2, group] });
const results = useContactStore.getState().getAutocomplete('Team');
expect(results).toHaveLength(2);
expect(results.map(r => r.email).sort()).toEqual(['alice@test.com', 'bob@test.com']);
expect(results).toHaveLength(1);
expect(results[0]).toEqual({ name: 'Team', email: '', group: { id: 'g1', memberCount: 2 } });
});
it('should not include group itself in results', () => {
it('should not suggest a group with no addressable members', () => {
const group = makeGroup({ id: 'g1' });
useContactStore.setState({ contacts: [group] });
const results = useContactStore.getState().getAutocomplete('Team');
+5 -3
View File
@@ -9,9 +9,11 @@ export default defineConfig({
globals: true,
setupFiles: ['./vitest.setup.ts'],
// integration/** is the dockerized Playwright suite (run via
// `npm run test:integration`); examples/** is untracked sample code. Both
// use their own runners and must not be collected by vitest.
exclude: ['e2e/**', 'integration/**', 'examples/**', 'node_modules/**', '.next/**'],
// `npm run test:integration`); examples/** is untracked sample code; and
// repos/** holds sibling checkouts with their own runners. node_modules
// must be globbed at any depth or the nested repos/*/node_modules trees
// get collected too.
exclude: ['e2e/**', 'integration/**', 'examples/**', 'repos/**', '**/node_modules/**', '.next/**'],
},
resolve: {
alias: {