forked from mirror/crumbs-original
- Add docs/TESTING.md with 10 BDD testing best practices - Add createNote() helper to fixtures to DRY up state setup - Add createChecklistNote() helper in checklist tests - Rename scenario titles to describe outcomes, not actions - Replace imperative Given steps with createNote() helper - Fix fragile conditional in tag filtering test (silent pass) - Remove implementation details from Gherkin comments - Improve Given/When/Then rhythm consistency across all tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
162 lines
5.5 KiB
TypeScript
162 lines
5.5 KiB
TypeScript
import { test, expect } from './helpers/fixtures.js';
|
|
import type { Page } from '@playwright/test';
|
|
|
|
/** Reset all preferences to defaults via API so each test starts clean */
|
|
async function resetPreferences(page: Page) {
|
|
await page.request.put('/api/preferences', {
|
|
data: {
|
|
defaultNoteMode: 'richtext',
|
|
defaultNoteColor: 'default',
|
|
hideFooter: 'false',
|
|
sidebarDefaultState: 'open'
|
|
}
|
|
});
|
|
}
|
|
|
|
test.describe('Settings — Preferences', () => {
|
|
test('Scenario: Preferences tab is visible in settings nav', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
// When the user navigates to settings
|
|
await page.goto('/settings');
|
|
|
|
// Then the Preferences nav link is visible
|
|
await expect(page.getByTestId('settings-nav-preferences')).toBeVisible();
|
|
});
|
|
|
|
test('Scenario: Default note mode change applies to new notes', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
await resetPreferences(page);
|
|
|
|
// Given the user sets default note mode to Markdown
|
|
await page.goto('/settings/preferences');
|
|
const putResponse = page.waitForResponse((res) => res.url().includes('/api/preferences') && res.request().method() === 'PUT');
|
|
await page.getByTestId('pref-mode-markdown').click();
|
|
await putResponse;
|
|
|
|
// When the user creates a new note
|
|
await page.goto('/');
|
|
await page.getByTestId('new-note-btn').click();
|
|
|
|
// Then the note editor opens in markdown mode (textarea visible)
|
|
await expect(page.getByTestId('note-content-input')).toBeVisible();
|
|
});
|
|
|
|
test('Scenario: Footer toggle hides and shows footer', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
await resetPreferences(page);
|
|
|
|
// Given the footer is visible
|
|
await page.goto('/');
|
|
await expect(page.getByTestId('app-footer')).toBeVisible();
|
|
|
|
// When the user enables the hide footer preference
|
|
await page.goto('/settings/preferences');
|
|
await page.getByTestId('pref-hide-footer').check();
|
|
|
|
// Then the footer is hidden
|
|
await page.goto('/');
|
|
await expect(page.getByTestId('app-footer')).not.toBeVisible();
|
|
|
|
// When the user disables the hide footer preference
|
|
await page.goto('/settings/preferences');
|
|
await page.getByTestId('pref-hide-footer').uncheck();
|
|
|
|
// Then the footer is visible again
|
|
await page.goto('/');
|
|
await expect(page.getByTestId('app-footer')).toBeVisible();
|
|
});
|
|
|
|
test('Scenario: Preferences persist across page reload', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
await resetPreferences(page);
|
|
|
|
// Given the user changes default note mode to Markdown
|
|
await page.goto('/settings/preferences');
|
|
const putResponse = page.waitForResponse((res) => res.url().includes('/api/preferences') && res.request().method() === 'PUT');
|
|
await page.getByTestId('pref-mode-markdown').click();
|
|
await putResponse;
|
|
|
|
// When the page is reloaded
|
|
await page.reload();
|
|
|
|
// Then the Markdown button is still selected (has primary styling)
|
|
const mdBtn = page.getByTestId('pref-mode-markdown');
|
|
await expect(mdBtn).toHaveClass(/font-medium/);
|
|
});
|
|
});
|
|
|
|
test.describe('Settings — API Key Management', () => {
|
|
test('Scenario: Created API key appears in the keys list', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
// When the user navigates to the MCP settings and creates an API key
|
|
await page.goto('/settings/mcp');
|
|
await page.getByTestId('api-key-name-input').fill('Settings Test Key');
|
|
await page.getByTestId('create-api-key-btn').click();
|
|
|
|
// Then the key is shown once for copying
|
|
await expect(page.getByTestId('created-key-display')).toBeVisible();
|
|
const keyValue = await page.getByTestId('created-key-value').textContent();
|
|
expect(keyValue).toMatch(/^crumbs_/);
|
|
|
|
// And it appears in the keys list
|
|
const keyItem = page.getByTestId('api-key-item').filter({ hasText: 'Settings Test Key' });
|
|
await expect(keyItem).toBeVisible();
|
|
});
|
|
|
|
test('Scenario: Revoked API key disappears from the keys list', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
// Given an API key exists
|
|
await page.goto('/settings/mcp');
|
|
await page.getByTestId('api-key-name-input').fill('Revoke Me');
|
|
await page.getByTestId('create-api-key-btn').click();
|
|
await expect(page.getByTestId('created-key-display')).toBeVisible();
|
|
|
|
const keyItem = page.getByTestId('api-key-item').filter({ hasText: 'Revoke Me' });
|
|
await expect(keyItem).toBeVisible();
|
|
|
|
// When the user revokes the key
|
|
await keyItem.getByTestId('delete-api-key-btn').click();
|
|
await keyItem.getByTestId('confirm-delete-btn').click();
|
|
|
|
// Then the key is removed from the list
|
|
await expect(keyItem).not.toBeVisible();
|
|
});
|
|
|
|
test('Scenario: Sidebar settings link opens the preferences page', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
// When the user navigates to settings from the sidebar
|
|
await page.getByTestId('settings-link').click();
|
|
|
|
// Then the preferences page is displayed
|
|
await expect(page).toHaveURL('/settings/preferences');
|
|
await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible();
|
|
});
|
|
|
|
test('Scenario: Settings tabs navigate between subpages', async ({
|
|
authenticatedPage: page
|
|
}) => {
|
|
// Given the user is on the settings page
|
|
await page.goto('/settings');
|
|
|
|
// When the user selects the API tab
|
|
await page.getByRole('link', { name: 'API' }).click();
|
|
|
|
// Then the API settings are displayed
|
|
await expect(page).toHaveURL('/settings/mcp');
|
|
await expect(page.getByRole('heading', { name: 'API Keys' })).toBeVisible();
|
|
|
|
// When the user selects the Profile tab
|
|
await page.getByRole('link', { name: 'Profile' }).click();
|
|
|
|
// Then the profile settings are displayed
|
|
await expect(page).toHaveURL('/settings/profile');
|
|
});
|
|
});
|