feat(ext/settings): Display section with color pickers + swatch + reset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,22 @@ vi.mock('../../../shared/state', () => ({
|
||||
}));
|
||||
|
||||
import { sendMessage } from '../../../shared/state';
|
||||
import { DEFAULT_DIGIT_COLOR, DEFAULT_SYMBOL_COLOR } from '../../../shared/color-scheme';
|
||||
|
||||
function mockChromeStorage(initial: Record<string, unknown> = {}) {
|
||||
const store: Record<string, unknown> = { ...initial };
|
||||
(global as any).chrome = {
|
||||
storage: {
|
||||
sync: {
|
||||
get: vi.fn((key: string) => Promise.resolve(
|
||||
key in store ? { [key]: store[key] } : {})),
|
||||
set: vi.fn((kv: Record<string, unknown>) => { Object.assign(store, kv); return Promise.resolve(); }),
|
||||
remove: vi.fn((key: string) => { delete store[key]; return Promise.resolve(); }),
|
||||
},
|
||||
},
|
||||
};
|
||||
return store;
|
||||
}
|
||||
|
||||
function settingsResponses() {
|
||||
// Two parallel calls in renderSettings: get_settings + get_blacklist.
|
||||
@@ -30,6 +46,7 @@ describe('settings view', () => {
|
||||
});
|
||||
|
||||
it('renders a Sync now button', async () => {
|
||||
mockChromeStorage();
|
||||
settingsResponses();
|
||||
|
||||
await renderSettings(app);
|
||||
@@ -38,6 +55,7 @@ describe('settings view', () => {
|
||||
});
|
||||
|
||||
it('clicking Sync now sends a sync message and shows feedback on success', async () => {
|
||||
mockChromeStorage();
|
||||
settingsResponses();
|
||||
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ ok: true });
|
||||
|
||||
@@ -52,6 +70,7 @@ describe('settings view', () => {
|
||||
});
|
||||
|
||||
it('shows the error when sync fails', async () => {
|
||||
mockChromeStorage();
|
||||
settingsResponses();
|
||||
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ ok: false, error: 'remote_unreachable' });
|
||||
|
||||
@@ -64,3 +83,109 @@ describe('settings view', () => {
|
||||
expect(status.textContent).toMatch(/remote_unreachable/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('settings Display section', () => {
|
||||
let app: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = '<div id="app"></div>';
|
||||
app = document.getElementById('app')!;
|
||||
(sendMessage as ReturnType<typeof vi.fn>).mockReset();
|
||||
});
|
||||
|
||||
it('renders digit and symbol color pickers with default values when storage is empty', async () => {
|
||||
mockChromeStorage();
|
||||
settingsResponses();
|
||||
|
||||
await renderSettings(app);
|
||||
|
||||
const digitInput = app.querySelector<HTMLInputElement>('#display-digit-color');
|
||||
const symbolInput = app.querySelector<HTMLInputElement>('#display-symbol-color');
|
||||
expect(digitInput).not.toBeNull();
|
||||
expect(symbolInput).not.toBeNull();
|
||||
expect(digitInput!.value).toBe(DEFAULT_DIGIT_COLOR);
|
||||
expect(symbolInput!.value).toBe(DEFAULT_SYMBOL_COLOR);
|
||||
});
|
||||
|
||||
it('renders pickers with stored values when storage has a scheme', async () => {
|
||||
mockChromeStorage({
|
||||
password_display_scheme: { digit_color: '#112233', symbol_color: '#aabbcc' },
|
||||
});
|
||||
settingsResponses();
|
||||
|
||||
await renderSettings(app);
|
||||
|
||||
const digitInput = app.querySelector<HTMLInputElement>('#display-digit-color');
|
||||
const symbolInput = app.querySelector<HTMLInputElement>('#display-symbol-color');
|
||||
expect(digitInput!.value).toBe('#112233');
|
||||
expect(symbolInput!.value).toBe('#aabbcc');
|
||||
});
|
||||
|
||||
it('renders a color-preview-swatch element', async () => {
|
||||
mockChromeStorage();
|
||||
settingsResponses();
|
||||
|
||||
await renderSettings(app);
|
||||
|
||||
expect(app.querySelector('#display-swatch')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('changing digit color calls saveColorScheme with updated scheme', async () => {
|
||||
mockChromeStorage();
|
||||
settingsResponses();
|
||||
|
||||
await renderSettings(app);
|
||||
|
||||
const digitInput = app.querySelector<HTMLInputElement>('#display-digit-color')!;
|
||||
digitInput.value = '#ff0000';
|
||||
digitInput.dispatchEvent(new Event('change'));
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
|
||||
const syncSet = (global as any).chrome.storage.sync.set as ReturnType<typeof vi.fn>;
|
||||
expect(syncSet).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
password_display_scheme: expect.objectContaining({ digit_color: '#ff0000' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('changing symbol color calls saveColorScheme with updated scheme', async () => {
|
||||
mockChromeStorage();
|
||||
settingsResponses();
|
||||
|
||||
await renderSettings(app);
|
||||
|
||||
const symbolInput = app.querySelector<HTMLInputElement>('#display-symbol-color')!;
|
||||
symbolInput.value = '#00ff00';
|
||||
symbolInput.dispatchEvent(new Event('change'));
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
|
||||
const syncSet = (global as any).chrome.storage.sync.set as ReturnType<typeof vi.fn>;
|
||||
expect(syncSet).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
password_display_scheme: expect.objectContaining({ symbol_color: '#00ff00' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('clicking reset calls chrome.storage.sync.remove and restores defaults', async () => {
|
||||
mockChromeStorage({
|
||||
password_display_scheme: { digit_color: '#112233', symbol_color: '#aabbcc' },
|
||||
});
|
||||
settingsResponses();
|
||||
|
||||
await renderSettings(app);
|
||||
|
||||
const resetBtn = app.querySelector<HTMLButtonElement>('#display-reset')!;
|
||||
resetBtn.click();
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
|
||||
const syncRemove = (global as any).chrome.storage.sync.remove as ReturnType<typeof vi.fn>;
|
||||
expect(syncRemove).toHaveBeenCalledWith('password_display_scheme');
|
||||
|
||||
const digitInput = app.querySelector<HTMLInputElement>('#display-digit-color')!;
|
||||
const symbolInput = app.querySelector<HTMLInputElement>('#display-symbol-color')!;
|
||||
expect(digitInput.value).toBe(DEFAULT_DIGIT_COLOR);
|
||||
expect(symbolInput.value).toBe(DEFAULT_SYMBOL_COLOR);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user