SplitSettingsCreator.test.tsx4.8 KBView on GitHub import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { SplitSettingsCreator, type SplitLabelOption } from '@/modules/threads/components/SplitSettingsCreator';
describe('SplitSettingsCreator', () => {
it('saves form values without manual query inputs', async () => {
const onSave = jest.fn();
render(<SplitSettingsCreator onBack={() => {}} onSave={onSave} />);
fireEvent.change(screen.getByPlaceholderText('Important'), {
target: { value: 'Team' },
});
fireEvent.click(screen.getByText('Save split'));
expect(onSave).toHaveBeenCalledWith(
expect.objectContaining({
name: 'Team',
query: 'label:INBOX',
alsoShowInImportant: false,
}),
);
});
/**
* The label query helpers (labelToken / appendLabelToQuery / removeLabelFromQuery)
* are module-private; we exercise them through the component's "Add label" and
* per-label "Delete" affordances, asserting the resulting query that flows to onSave.
*/
describe('label query surgery', () => {
const LABELS: SplitLabelOption[] = [
{ id: 'l1', name: 'Receipts' },
{ id: 'l2', name: 'My Clients' }, // whitespace → must be quoted
{ id: 'l3', name: 'Newsletters' },
];
function saveAndGetQuery(onSave: jest.Mock): string {
fireEvent.click(screen.getByText('Save split'));
return onSave.mock.calls.at(-1)![0].query as string;
}
it('quotes label names that contain whitespace', () => {
const onSave = jest.fn();
render(
<SplitSettingsCreator onBack={() => {}} onSave={onSave} labels={LABELS} />,
);
fireEvent.change(screen.getByPlaceholderText('Important'), { target: { value: 'X' } });
fireEvent.click(screen.getByText('Add label'));
fireEvent.click(screen.getByText('My Clients'));
// No prior query → the quoted token replaces the throwaway default.
expect(saveAndGetQuery(onSave)).toBe('label:"My Clients"');
});
it('replaces the throwaway DEFAULT_QUERY rather than appending to it', () => {
const onSave = jest.fn();
render(
<SplitSettingsCreator
onBack={() => {}}
onSave={onSave}
labels={LABELS}
draft={{ query: 'label:INBOX' }}
/>,
);
fireEvent.change(screen.getByPlaceholderText('Important'), { target: { value: 'X' } });
fireEvent.click(screen.getByText('Add label'));
fireEvent.click(screen.getByText('Receipts'));
// Starts from the default 'label:INBOX' → replaced wholesale, not AND'd.
expect(saveAndGetQuery(onSave)).toBe('label:Receipts');
});
it('dedupes a label that is already present in the query', () => {
const onSave = jest.fn();
render(
<SplitSettingsCreator
onBack={() => {}}
onSave={onSave}
labels={LABELS}
draft={{ query: 'is:unread label:Receipts' }}
/>,
);
fireEvent.change(screen.getByPlaceholderText('Important'), { target: { value: 'X' } });
fireEvent.click(screen.getByText('Add label'));
fireEvent.click(screen.getByText('Receipts'));
// Already present → no duplicate token appended.
expect(saveAndGetQuery(onSave)).toBe('is:unread label:Receipts');
});
it('appends a new label to an existing non-default query', () => {
const onSave = jest.fn();
render(
<SplitSettingsCreator
onBack={() => {}}
onSave={onSave}
labels={LABELS}
draft={{ query: 'from:<email>' }}
/>,
);
fireEvent.change(screen.getByPlaceholderText('Important'), { target: { value: 'X' } });
fireEvent.click(screen.getByText('Add label'));
fireEvent.click(screen.getByText('Newsletters'));
expect(saveAndGetQuery(onSave)).toBe('from:<email> label:Newsletters');
});
it('removes a label embedded mid-query when the label is deleted', async () => {
const onSave = jest.fn();
const onDeleteLabel = jest.fn().mockResolvedValue(undefined);
render(
<SplitSettingsCreator
onBack={() => {}}
onSave={onSave}
labels={LABELS}
onDeleteLabel={onDeleteLabel}
draft={{ query: 'is:unread label:Receipts from:<email>' }}
/>,
);
fireEvent.change(screen.getByPlaceholderText('Important'), { target: { value: 'X' } });
fireEvent.click(screen.getByText('Add label'));
// Click the per-label delete affordance, then confirm.
fireEvent.click(screen.getByLabelText('Delete Receipts'));
fireEvent.click(screen.getByText('Delete'));
await waitFor(() => expect(onDeleteLabel).toHaveBeenCalled());
// The mid-query `label:Receipts` clause is excised; surrounding terms remain.
expect(saveAndGetQuery(onSave)).toBe('is:unread from:<email>');
});
});
});