Send an email to collaborators when they are added to a shared note.
Uses nodemailer with optional SMTP configuration (auto-enabled when
SMTP_HOST is set). Users can opt out via the notifyOnShare preference.
https://claude.ai/code/session_01P4HSMr7QvKw9TMNikUJD9e
Add more vertical padding (py-1 → py-1.5) to checklist items for better
readability. Add min-w-0 to the text input so long text is constrained
within the flex container instead of overflowing out of the note.
https://claude.ai/code/session_0163L1u9MfL1C6Rv3yAw8RKs
- Replace `truncate` with `break-words min-w-0` so long checklist text
wraps visibly instead of being clipped
- Increase vertical spacing between items (space-y-1 → space-y-2)
- Add bottom margin (mb-6) to prevent action buttons overlapping content
- Align checkboxes to top of multi-line items (items-start + mt-0.5)
https://claude.ai/code/session_01UVNA5wucVEjRQzwRxMhTZ9
On mobile, tapping a NoteCard to open the editor produces synthetic mouse
events (mousedown → mouseup → click) at the original touch coordinates.
If those coordinates land on the overlay backdrop rather than the editor
card, the mousedown sets mousedownOnOverlay = true and the click
immediately closes the editor — causing a brief flicker.
Guard against this by deferring overlay readiness to the next animation
frame after mount, so any synthetic events from the originating tap are
ignored.
https://claude.ai/code/session_011C8bfgShjDSmDr3rUCb2gw
Run E2E tests inside mcr.microsoft.com/playwright container which has
Chromium pre-installed, eliminating the ~30s browser download on each run.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three sources of flakiness fixed:
1. **Preferences sync race** — page.goto() triggers a full reload which
re-runs initPreferences() + syncPreferencesFromServer(). On slow CI,
the async server sync can return stale data from parallel test workers
that share the same user's server-side preferences. Fixed with
gotoWithStablePrefs() which blocks the one GET /api/preferences sync
request, so initPreferences() falls back to localStorage (always correct).
2. **localStorage not reset** — resetPreferences() only reset the server.
After a full page reload, loadFromLocalStorage() could return stale
values from previous test runs. Now resets both server and localStorage.
3. **API key name collision** — parallel test runs created keys with the
same name ("Settings Test Key"), causing Playwright strict-mode
violations when multiple elements matched. Fixed with Date.now()
unique suffixes.
Verified: 167/167 settings tests pass (20x repeat), 173/173 full suite (5x).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
Each preference test now resets to default preferences via API at the
start, ensuring parallel tests don't pollute each other's state.
Also await PUT response before reload in persist test to prevent
race condition.
Add /settings/about page showing version, project description,
Bretzel ecosystem info, open source details, and tech stack.
Move version display from sidebar to About page and align
Settings nav item styling with other sidebar items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Settings tests set defaultNoteMode to 'markdown' without resetting,
causing parallel sharing test to timeout when TiptapEditor wasn't
rendered (textarea shown instead). Two fixes:
- Reset preferences in settings tests after changing them
- Create note via API in sharing test (tests sharing, not note creation)
Move Profile page to /settings/profile and add a 302 redirect from
/settings to /settings/preferences so the first settings tab is
Preferences, not Profile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Track mousedown origin so click-outside-to-close only fires when both
mousedown and mouseup occur on the overlay backdrop, not when dragging a
text selection from inside the editor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove duplicate bold formatting test from notes-crud.spec.ts
- Trim formatting.spec.ts from 9 to 5 tests (remove italic, inline code,
toolbar bold, toolbar visibility — same mechanism as bold shortcut)
- Remove hardcoded waitForTimeout(500) from search.spec.ts (Playwright
assertions already retry with built-in timeouts)
- Restructure sharing.spec.ts: 9 serial tests → 5 parallel self-contained
tests using collabPage fixture and API-based setup
- Add collabPage fixture to helpers/fixtures.ts for sharing tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make db a required first parameter on all service functions (notes-service,
api-utils, tags, collaborators, attachments, rate-limit, sync/server).
This enables unit testing with in-memory databases via createTestDb().
Also removes the NODE_ENV=test bypass from rate-limit and adds optional
now parameter for time control in tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Write a markdown coverage table to $GITHUB_STEP_SUMMARY for both unit
and E2E test jobs. Uses a small bash script that parses json-summary
output from the coverage reporters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add V8 coverage for unit tests via @vitest/coverage-v8 and Istanbul
instrumentation for E2E tests via vite-plugin-istanbul. Coverage data
is collected from browser pages after each Playwright test and merged
with nyc. CI uploads both coverage reports as GitHub Actions artifacts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pre-build before Playwright instead of building inside webServer
- Reduce retries from 2 to 1 (fix flaky tests instead of masking them)
- Set CI workers to 4 for more parallelism
- Reduce artifact retention from 30 to 7 days
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bearer token authentication now works on all /api/* routes, not just
/api/mcp. If a Bearer token is present it's validated first; otherwise
session cookie auth is used as fallback. Updates settings UI to reflect
the broader scope (tab renamed to "API", REST API curl example added)
and updates the landing page feature card.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>