Files
webmail/hooks/__tests__/use-keyword-format.test.tsx
T
lucamzanon 42b6cc08b0 feat: colour unified rows by account and give every tag a hue of its own
In the unified view each row can be shaded with its account colour
instead of carrying an account dot, behind a layout setting. Tags keep
their own generated hue on their chips.
2026-09-19 05:44:44 +02:00

116 lines
4.5 KiB
TypeScript

import { renderHook } from '@testing-library/react';
import { describe, it, expect, beforeEach } from 'vitest';
import { useKeywordFormat } from '../use-keyword-format';
import { useSettingsStore, KEYWORD_PALETTE, type KeywordDefinition } from '@/stores/settings-store';
import { suggestKeywordColor } from '@/lib/keyword-discovery';
const TAGS: KeywordDefinition[] = [
{ id: 'work', label: 'Work', color: 'blue' },
{ id: 'work/clients', label: 'Clients', color: 'green' },
{ id: 'archive', label: 'Archive', color: 'red-dark' },
];
describe('useKeywordFormat', () => {
beforeEach(() => {
useSettingsStore.setState({ emailKeywords: TAGS, nestedTags: true });
});
describe('tagColor', () => {
it('resolves a tag to its palette entry, including the new shades', () => {
const { result } = renderHook(() => useKeywordFormat());
expect(result.current.tagColor('work')).toBe(KEYWORD_PALETTE.blue);
expect(result.current.tagColor('archive')).toBe(KEYWORD_PALETTE['red-dark']);
});
it('gives a keyword it has no definition for a hue of its own', () => {
// Set on the message by another client, applied by a server-side filter,
// or its tag was deleted here. One shared grey made every such tag look
// alike, which is when telling them apart matters most.
const { result } = renderHook(() => useKeywordFormat());
expect(result.current.tagColor('never-heard-of-it'))
.toBe(KEYWORD_PALETTE[suggestKeywordColor('never-heard-of-it')]);
});
it('matches the colour the settings screen would propose for that tag', () => {
// Adopting a discovered tag must not make its colour jump.
const { result } = renderHook(() => useKeywordFormat());
for (const id of ['netflix', 'humb', 'ebay']) {
expect(result.current.tagColor(id)).toBe(KEYWORD_PALETTE[suggestKeywordColor(id)]);
}
});
it('keeps an undefined tag on the same hue across renders', () => {
const a = renderHook(() => useKeywordFormat());
const b = renderHook(() => useKeywordFormat());
expect(a.result.current.tagColor('humb')).toBe(b.result.current.tagColor('humb'));
});
it('spreads undefined tags over several hues', () => {
const { result } = renderHook(() => useKeywordFormat());
const ids = ['humb', 'alie', 'ebay', 'zoop', 'fire', 'nexu', 'ubis', 'tlsreports'];
const dots = new Set(ids.map((id) => result.current.tagColor(id).dot));
expect(dots.size).toBeGreaterThan(1);
});
it('derives a hue for a definition whose colour is not in the palette', () => {
useSettingsStore.setState({ emailKeywords: [{ id: 'odd', label: 'Odd', color: 'chartreuse' }] });
const { result } = renderHook(() => useKeywordFormat());
expect(result.current.tagColor('odd')).toBe(KEYWORD_PALETTE[suggestKeywordColor('odd')]);
});
});
describe('sortTagIds', () => {
it('follows the order the user arranged in settings', () => {
// Settings order is work, work/clients, archive - drag-reorderable, and
// deliberately not alphabetical.
const { result } = renderHook(() => useKeywordFormat());
expect(result.current.sortTagIds(['archive', 'work/clients', 'work'])).toEqual([
'work',
'work/clients',
'archive',
]);
});
it('is stable however the keywords happen to arrive', () => {
const { result } = renderHook(() => useKeywordFormat());
const expected = ['work', 'work/clients', 'archive'];
expect(result.current.sortTagIds(['work', 'archive', 'work/clients'])).toEqual(expected);
expect(result.current.sortTagIds(['archive', 'work', 'work/clients'])).toEqual(expected);
});
it('follows a reordering of the settings list', () => {
useSettingsStore.setState({ emailKeywords: [TAGS[2], TAGS[0], TAGS[1]] });
const { result } = renderHook(() => useKeywordFormat());
expect(result.current.sortTagIds(['work', 'archive'])).toEqual(['archive', 'work']);
});
it('puts a tag with no local definition last, ordered by name', () => {
const { result } = renderHook(() => useKeywordFormat());
expect(result.current.sortTagIds(['zz-unknown', 'work', 'aa-unknown'])).toEqual([
'work',
'aa-unknown',
'zz-unknown',
]);
});
it("leaves the caller's array alone", () => {
const { result } = renderHook(() => useKeywordFormat());
const input = ['archive', 'work'];
result.current.sortTagIds(input);
expect(input).toEqual(['archive', 'work']);
});
});
});