Commit graph

235 commits

Author SHA1 Message Date
Johannes Millan
dbeca43876 test(e2e): fix done-toggle selector to use element instead of class
The done-toggle was refactored from an inline SVG with class="done-toggle"
into a standalone Angular component <done-toggle>. Update all e2e selectors
from '.done-toggle' (class) to 'done-toggle' (element) to match.
2026-03-24 16:25:42 +01:00
Johannes Millan
d316a684bb fix(sync): improve move operation reliability during sync
- moveItemAfterAnchor: preserve current position when anchor is
  concurrently deleted instead of appending to end (which corrupted
  ordering on remote clients). For cross-list moves, append to end
  as fallback to prevent data loss.
- Prevent double-write of deferred actions: track buffered actions in
  a WeakSet and filter them in the effect so they are only written
  once by processDeferredActions().
- Propagate skipDequeue through handleQuotaExceeded retry path to
  prevent queue desync when deferred actions hit storage quota.
- Add e2e tests for concurrent delete + reorder sync scenarios.
2026-03-24 16:25:42 +01:00
Johannes Millan
6d67d31ecb test(e2e): fix failing WebDAV and SuperSync E2E tests
- Suppress onboarding overlay for fresh Client B in WebDAV legacy
  migration test (onboarding-backdrop was blocking sync button clicks)
- Update play indicator selector from .play-icon-indicator to
  .play-indicator after done-toggle circle refactor removed the mat-icon
- Use markTaskDoneByKey in archive conflict tests for reliable done
  state verification (click-based markTaskDone was silently failing)
2026-03-23 11:07:25 +01:00
Johannes Millan
31480a5fab feat(tasks): replace drag handle with done toggle circle and improve mobile UX
- Replace drag handle with SVG done-toggle circle with checkmark draw-on animation
- Improve mobile UX: cdkDragStartDelay for touch, bottom-sheet context menu
- Move issue/repeat indicators from drag handle to tag-list as chips
- Add isFinishDayEnabled config toggle
- Reorder context menu: add sub task before duplicate, deadline as own entry
- Fix context menu not opening on swipe left
- Fix race condition in done toggle animation timeout
2026-03-22 18:14:16 +01:00
Johannes Millan
2f8d871042 feat(shepherd): remove all tours except keyboard navigation
Remove the full welcome tour (Welcome, AddTask, DeleteTask, Projects,
Sync, IssueProviders, ProductivityHelper, FinalCongrats, StartTourAgain)
and keep only the KeyboardNav tour, triggered from the Help menu.

- Delete ShepherdComponent (auto-start on load)
- Strip shepherd-steps.const.ts to KeyboardNav steps only
- Simplify ShepherdService with _initPromise guard and fresh tour on re-open
- Remove 3 tour menu items from Help menu, keep keyboard tour
- Remove waitForEl/waitForElObs$ helpers (unused by KeyboardNav)
- Delete e2e/utils/tour-helpers.ts and all tour dismissal calls
- Clean up stale tour comments across e2e tests
- Fix pre-existing build error: add isFinishDayEnabled to AppFeaturesConfig
  type, defaults, form toggle, component signal, and translations
2026-03-22 17:00:42 +01:00
Johannes Millan
a01a511c25 feat(sync): add backup strategy with accounts-only dump and disaster recovery docs
- Add accounts-only pg_dump (users + passkeys) to backup script for
  lightweight disaster recovery when clients still have data
- Add pipefail to backup script to catch silent dump failures
- Add test endpoint to simulate partial server revert (ops-after/:serverSeq)
- Add 6 e2e tests covering all disaster recovery scenarios:
  complete data loss, partial revert, accounts-only restore (API + SQL),
  full dump restore + reset account, and all-clients-lost recovery
- Add backup-and-recovery.md with setup, recovery procedures, and
  decision tree
2026-03-18 20:15:45 +01:00
Johannes Millan
f3585b5dc1 test(e2e): fix recurring future-start-date and supersync encryption tests
- Rewrite recurring-future-start-date test to set startDate via the
  repeat dialog's model signal instead of the broken schedule-dialog
  approach (mat-datepicker signals don't work from page.evaluate)
- Fix supersync encryption password-change test by clicking syncBtn
  directly instead of triggerSync() which throws on error state before
  the async decrypt dialog renders
2026-03-18 13:14:10 +01:00
Johannes Millan
a276ccb3f4 fix: resolve pre-existing lint and prettier errors blocking commits (#6840) 2026-03-15 16:47:52 +01:00
Johannes Millan
cf7fa73e5b test(e2e): fix flaky delete-vs-update test and remove obsolete encryption tests
- Fix flaky "Concurrent Delete vs. Update" test with deterministic
  delete-wins assertion instead of permissive either-outcome check
- Delete supersync-encryption-enable-disable.spec.ts (4 tests) and
  supersync-encryption-prompt-loop.spec.ts (2 tests) — these tested
  optional encryption flows that no longer exist since encryption
  became mandatory for SuperSync
2026-03-07 22:32:03 +01:00
Johannes Millan
a54de5f90d test(e2e): fix failing supersync E2E tests
- Wrong-password overwrite: remove Client A sync blocked by decrypt dialog
- Complex chain (4.1): rewrite without unreliable renameTask helper
- Delete vs Update race: fix assertions to expect delete wins
- Deleted task dueDay=today: same delete-wins fix, rename test
- Subtask conflicts: restructure so A marks done subtask from B
- Import tests: add syncImportChoice 'local' to preserve imported data
- Various other test fixes for reliability
2026-03-07 22:32:03 +01:00
Johannes Millan
5401bb7f1d test(e2e): fix 11 failing supersync import tests
- Fix importBackupFile() to wait for actual import completion via console
  event instead of unreliable URL polling
- Fix setupSuperSync to respect syncImportChoice config in all code paths
- Add .first() to task locators that resolve to multiple elements on
  project pages (backlog + active sections)
- Add decryptionFailedPassword config to handle "Decryption Failed" dialog
  when server has old encrypted ops from before encryption changes
- Skip 1 test (unencrypted→encrypted) that needs server-side isCleanSlate fix
2026-03-07 13:28:20 +01:00
Johannes Millan
069873126d test(e2e): fix supersync error scenario tests and skip failing import tests
- Update error scenario mocks to use new OpUploadResponse format
  ({ results: OpUploadResult[] } instead of { piggybacked, rejectedOps })
- Use route.fetch() to get real op IDs from server for mock responses
- Add syncImportChoice option to SuperSyncConfig for import test control
- Skip 3 import-clean-server-state tests pending server-side isCleanSlate fix
2026-03-06 23:08:37 +01:00
Johannes Millan
e8f5f1d404 test(e2e): fix supersync encryption tests for mandatory encryption
- Skip "disable encryption" test: button is hidden for SuperSync
- Skip "encryption prompt loop" tests: unencrypted SuperSync no longer possible
- Fix "wrong password" test: assert decrypt error dialog directly instead
  of checking for post-dialog snackbar/error indicators
- Fix "password change recovery" test: handle sync-import-conflict dialog
  that appears after retry decrypt due to clean slate from password change
2026-03-06 18:19:26 +01:00
Johannes Millan
cce9576946 test(e2e): fix supersync E2E tests for mandatory encryption flow
- Extract shared encryption warning helper and fix race conditions
- Restructure concurrent-import tests to import before enabling sync
- Fix encryption password preservation and change tests
- Add "Change Password" button detection in supersync page object
- Mark backup-import ID mismatch test as fixme (known limitation)
2026-03-06 16:36:38 +01:00
Johannes Millan
755cd705f5
Fix/run 22683814946 (#6740)
test(e2e): fix e2e tests
2026-03-05 19:37:48 +01:00
Johannes Millan
7164a5e44b test(e2e): fix encryption button timing and drag-handle strict mode violation
Two root causes for failing E2E tests in CI:

1. Encryption button not visible after provider selection: The async
   provider change listener loads config from IndexedDB and updates the
   Formly model, but the 1000ms waitForTimeout was insufficient in CI.
   Replace with polling (200ms intervals, 10s timeout) that waits for
   either enable or disable encryption button to become visible.
   Also removes ~130 lines of leftover debug logging from disableEncryption().

2. Drag-handle strict mode violation on parent tasks with subtasks:
   task.locator('.drag-handle') matched both the parent's drag handle
   and nested subtask drag handles. Add .first() to all drag-handle
   locator usages to target only the parent task's own handle.
2026-03-04 19:38:37 +01:00
Florian Bachmann
729d35501f
Fix immutable caldav task (#6705)
* Fix immutable CalDAV task after completion

* Linting

* Resolves Claude suggestions
2026-03-03 15:05:57 +01:00
Johannes Millan
8a6df3634d test(e2e): fix strict mode violation in encryption password dialog locator
The dialog-enter-encryption-password now has two mat-flat-button buttons
when SuperSync is active (Save & Sync + Force Overwrite). The e2e locator
'button[mat-flat-button]' resolved to 2 elements causing Playwright strict
mode failures across all encryption-related SuperSync tests. Added
[color="primary"] to uniquely target the Save & Sync button.
2026-03-03 11:27:19 +01:00
Johannes Millan
ff444c1b77 test(sync): add E2E tests for max retry rejection and import encryption preservation 2026-03-01 21:27:08 +01:00
Johannes Millan
baa9a46935 test(sync): add encryption and supersync e2e test coverage
Add comprehensive test coverage for the encryption and SuperSync features:

- Add e2e tests for encryption prompt loop, error scenarios, no-op sync
- Add e2e tests for provider switch, re-enable, account reset, server migration
- Add e2e tests for import clean-slate scenario
- Add unit tests for encryption dialogs, toggle, enable/disable services
- Add unit tests for sync-config, sync-wrapper, file-based-encryption
- Add unit tests for operation-log-sync, sync-import-filter, clean-slate
- Update supersync page object for mandatory encryption flows
2026-03-01 21:27:08 +01:00
overcuriousity
dc7742d354
fix: profile system bugs (#6614)
* fixes to the profile system storage which worked unreliable across multiple storage backends

* fix tests

* fix issues with tests

* address copilot reviews
2026-02-23 17:43:04 +01:00
Johannes Millan
5ab773fa26 test(e2e): fix flaky LWW delete-vs-update conflict tests
Fix deleteTask helper clicking on task-title, which entered edit mode
and caused Backspace to delete text instead of triggering the delete
shortcut. Use drag-handle click target instead.

Also add delete verification, resilient title assertions for known LWW
race, increased persistence waits, and extra sync rounds.
2026-02-16 11:07:52 +01:00
Johannes Millan
efd307ab84 test(sync): add encryption and gap detection tests, fix E2E navigation race
- Add E2E test for encryption + USE_LOCAL conflict resolution, validating
  the double-encryption bug fix (b70a0e5634) end-to-end
- Add integration tests for partial trimming gap detection with
  syncVersion-based check across multi-client scenarios
- Add integration tests for encrypted sync round-trips (ops, snapshots,
  concurrent uploads)
- Fix systemic "waiting for navigation to finish" failures in WebDAV E2E
  tests by adding noWaitAfter and waitForURL to SyncPage methods,
  matching the pattern already used in SuperSyncPage
2026-02-15 11:19:22 +01:00
Johannes Millan
08e8329f97 refactor(sync): consolidate LegacySyncProvider into SyncProviderId
Remove duplicate LegacySyncProvider enum and use SyncProviderId everywhere.
The two enums had identical values but were separate types for historical
reasons, making the "Legacy" name misleading since providers are actively used.
2026-02-15 11:19:22 +01:00
Johannes Millan
0a947a5d62 test(e2e): expand completed tasks section in three-client convergence test
The "Completed Tasks" collapsible uses @if which removes done task
elements from the DOM when collapsed. After syncing a done task to
other clients, the section may be collapsed, causing waitForTask and
isDone assertions to fail intermittently.
2026-02-15 11:19:22 +01:00
Johannes Millan
03b53f118d test(e2e): expand completed tasks section before checking in delete vs update test
The "Completed Tasks" collapsible uses @if which removes task elements
from the DOM when collapsed. After sync convergence, Client A may have
the section collapsed while Client B has it expanded, causing the
consistency assertion to fail despite identical NgRx state.
2026-02-13 17:54:06 +01:00
Johannes Millan
8cba78c103 test(e2e): fix simple counter tests and flaky sync tests
Update createSimpleCounter() to use the new habits page flow after
the config page removal in 41027f3. Replace fragile waitForTimeout
calls with explicit sync completion checks in enableEncryption() and
increase timeouts in changeEncryptionPassword() and LWW conflict tests.
2026-02-13 17:54:06 +01:00
Johannes Millan
93cbcd7e44 test(e2e): fix reliability issues in supersync E2E tests
- Replace page.reload() with close-and-reopen pattern after backup
  imports to prevent hangs with active sync connections
- Add proper sync-import-conflict dialog handling via Promise.race
- Fix browser context leak in provider-switch test
- Replace baseURL! non-null assertions with fallback pattern
- Replace fixed timeouts with element-based waits in cascade-delete
- Extract duplicate dialog-dismissal loop into dismissBlockingDialogs
  helper in waits.ts
- Add missing WorkViewPage/SuperSyncPage imports in same-client test
- Replace any type with proper VectorClockEntry interface
2026-02-13 14:06:43 +01:00
Johannes Millan
fda5bca5b3 test(e2e): fix flaky import-sync tests with proper app readiness waits
Replace waitForLoadState('networkidle') with waitForAppReady() after
post-import page reloads and before task count verification. networkidle
only waits for network silence but not Angular bootstrap, loading
screens, or blocking dialogs - causing intermittent failures under load.
2026-02-13 14:06:43 +01:00
Johannes Millan
e5aca24663 test(e2e): fix cascade delete test and dialog handler conflicts
- Rewrite cascade delete test to avoid concurrent modification race
  condition that the sync protocol correctly resolves by keeping both
  sides' data (not a bug, just wrong test expectation)
- Add try/catch to dialog handlers in supersync.page.ts to prevent
  "Cannot accept dialog which is already handled" errors when multiple
  listeners compete
- Fix closeClient unhandled promise rejection when timeout wins
  Promise.race
2026-02-12 21:18:14 +01:00
Johannes Millan
23cb535c3d test(e2e): fix navigation hang in supersync import test
Replace page.reload() with close-and-reopen pattern to avoid
hangs caused by active sync connections blocking navigation
lifecycle events.
2026-02-12 19:44:58 +01:00
Johannes Millan
aea9c769c8 test(e2e): fix failing supersync E2E tests
- cascade-delete: use context menu for project deletion instead of
  non-existent settings page button
- multi-tab: set PLAYWRIGHT user agent on shared context and navigate
  inactive tabs to about:blank to avoid multi-instance blocker
- token-expiry: fix route pattern to match actual API endpoint and
  re-enter credentials after 401 clears them
2026-02-12 18:02:14 +01:00
Johannes Millan
fa083e7da4 chore(e2e): remove unused eslint-disable directive 2026-02-12 16:43:11 +01:00
Johannes Millan
346eb50bf3 test(e2e): strengthen assertions in supersync E2E tests
- Assert 401 route interception fired in token expiry tests
- Assert expected LWW winner in three-client config convergence test
- Navigate to root view instead of TODAY for cascade delete orphan check
2026-02-12 16:42:07 +01:00
Johannes Millan
43b5808a3f fix(sync): harden vector clock sanitization and improve E2E test robustness
- Cap sanitizeVectorClock values at 100M instead of MAX_SAFE_INTEGER (prevents adversarial clock inflation)
- Add early-exit optimization in compareVectorClocks for CONCURRENT detection
- Add test.setTimeout(180000) to token-expiry and lastseq-preservation E2E tests
- Remove dead code (unreachable guard) in compaction E2E test
- Extract shared config helpers (navigateToMiscSettings, toggleSetting, etc.) into e2e/utils/config-helpers.ts
- Fix Client B cleanup in provider-switch test (move to finally block)
- Add clarifying comments for raw SQL schema coupling, CORS header, and mergeRemoteOpClocks caller contract
- Extract enforceStorageQuota helper in sync routes to reduce duplication
- Add conflictType to conflict detection for structured error handling
- Add MAX_CLEANUP_ITERATIONS guard to freeStorageForUpload
2026-02-12 16:27:56 +01:00
Johannes Millan
5b7c9bdfb7 test(sync): add comprehensive SuperSync test coverage
Add 7 E2E tests (lastSeq preservation, token expiry recovery, cascade
delete, compaction resilience, global config edge cases, provider switch,
multi-tab), 2 server unit tests (replaceToken expiry regression,
middleware auth), 2 client integration tests (IndexedDB error recovery,
schema version sync), and locale-independent sort edge cases for vector
clocks.
2026-02-12 16:27:56 +01:00
Johannes Millan
c70ced204e fix(sync): fix stale comments, doc refs, and preserve lastSeq on clean-slate
Remove references to deleted docs/ai/ files, update stale comments about
protectedClientIds and pruning-aware comparison to reflect current REPLACE
semantics, fix MAX=30→20 heading, client_0..29→19 comment, use ?? over ||,
preserve lastSeq in server clean-slate path to prevent sequence reuse, and
add clarifying comments for mock limitations and validation guards.
2026-02-12 16:27:56 +01:00
Johannes Millan
3f99e3773c fix(sync): fix replaceToken expiry, locale-independent sort, and stale comments
- Use JWT_EXPIRY_PASSKEY (7d) for replaceToken instead of 365d magic link
  expiry — token replacement is a security action, shorter lifetime is safer
- Replace localeCompare with locale-independent comparator in vector clock
  tie-breaking to ensure deterministic behavior across environments
- Fix 5 additional stale MAX=30 references in docs and tests (now 20)
- Update authentication.md to reflect dual JWT expiry tiers
- Clean up isLikelyPruningArtifact references in docs and LEGACY_MAX in tests
2026-02-12 16:27:55 +01:00
Johannes Millan
cfc6749197 refactor(sync): remove isLikelyPruningArtifact heuristic
With MAX_VECTOR_CLOCK_SIZE=20, server-side pruning requires 21+ unique
client IDs — extremely unlikely for a personal productivity app. The
isLikelyPruningArtifact heuristic was a backward-compat workaround for
the old MAX=10 era with acknowledged false positives. Remove it in favor
of strict clean-slate semantics: all CONCURRENT ops are filtered.

The same-client check remains — it's mathematically definitive (not a
heuristic) and correctly keeps post-import ops from the import's client.
2026-02-12 16:27:55 +01:00
Johannes Millan
f37280e082 refactor(sync): reduce MAX_VECTOR_CLOCK_SIZE from 30 to 20
Lower the cap to leave headroom for future increases and surface
size-related edge cases earlier. All pruning logic is MAX-agnostic
so this is a safe constant change with documentation updates.
2026-02-12 16:27:55 +01:00
Johannes Millan
29541951a3 refactor(sync): increase MAX_VECTOR_CLOCK_SIZE from 10 to 30 and remove defense layers
At MAX=10, pruning triggered frequently enough (11+ unique client IDs from
reinstalls/new browsers) to require 4 defense layers compensating for
information loss: pruning-aware comparison, protected client IDs with
migration, isLikelyPruningArtifact heuristic, and same-client check.

At MAX=30, pruning almost never triggers (needs 31+ unique client IDs).
A 30-entry clock is ~500 bytes — negligible bandwidth. This allows removing
most defense layers while keeping two cheap backward-compat checks for old
10-entry pruned data still on servers.

Removed:
- Pruning-aware mode in compareVectorClocks (standard comparison now)
- Protected client IDs mechanism (storage, migration, preservation)
- selectProtectedClientIds function
- Clock normalization in SyncImportFilterService

Kept temporarily (backward compat with old 10-entry data):
- isLikelyPruningArtifact with LEGACY_MAX=10
- Same-client check (always mathematically correct)
2026-02-12 16:27:55 +01:00
Johannes Millan
4a779a0218 fix(sync): replace vector clock on remote SYNC_IMPORT instead of merging
When a client with an established vector clock (10+ entries) received a
remote SYNC_IMPORT/BACKUP_IMPORT with a fresh clock, mergeRemoteOpClocks()
merged the import's clock into the old clock instead of replacing it.
This caused clock bloat (11+ entries), which led to server-side pruning
dropping the import's entry (lowest counter). Other clients then saw
these ops as CONCURRENT with the import and discarded them.

Fix: In mergeRemoteOpClocks(), when a full-state op is present, use its
clock as the base instead of the existing local clock. Regular ops
continue to merge normally.
2026-02-12 16:27:55 +01:00
Johannes Millan
cfa12f83e3 fix(sync): keep import client's own post-import ops despite pruned vector clocks
When vector clocks are at MAX_VECTOR_CLOCK_SIZE and have diverged due to
asymmetric pruning, ops from the same client that created the SYNC_IMPORT
were incorrectly classified as CONCURRENT and filtered. Same-client counter
comparison is mathematically definitive (monotonically increasing), so
CONCURRENT can only arise from pruning artifacts for the import's own client.
2026-02-11 20:37:40 +01:00
Johannes Millan
d290673034 fix(e2e): fix failing and flaky supersync tests
Replace fixed timeouts with deterministic waits and use correct methods
for expected-failure scenarios in supersync E2E tests.
2026-02-08 14:16:06 +01:00
Johannes Millan
985e839747 fix(tests): improve synchronization tests and increase timeout for IndexedDB operations 2026-02-06 20:10:21 +01:00
Johannes Millan
a977fa2fbe refactor(sync): extract shared utils, add response validators, and improve error handling
- Extract isTransientNetworkError into shared sync-error-utils.ts
- Add runtime response validators for all SuperSync API responses
- Add migration failure notification with session dedup guard (ERROR severity)
- Fix Dropbox token logging to avoid leaking credentials
- Add tests: clock corruption, response validators, transient errors, concurrent SYNC_IMPORT e2e
2026-02-05 21:02:30 +01:00
Johannes Millan
108b72ff1a fix(sync): handle MultiEntityPayload in LWW Delete vs Update conflict resolution
_convertToLWWUpdatesIfNeeded() now extracts the full entity from the
local DELETE payload, merges remote UPDATE changes on top, and produces
a flat entity payload that lwwUpdateMetaReducer expects. Previously it
only changed the actionType but kept the original MultiEntityPayload,
causing the meta-reducer to bail because entityData['id'] was undefined.

Also fixes _extractEntityFromDeleteOperation() to use
extractActionPayload() for consistent MultiEntityPayload unwrapping,
and increases E2E timing constants to ensure DELETE operations flush
to IndexedDB before sync triggers.
2026-02-04 19:53:44 +01:00
Johannes Millan
6c0a6b9348 fix(e2e): stabilize undo task delete sync test
Two flakiness sources fixed:
- Click on task element could activate title inline editor, causing
  Backspace to edit text instead of triggering delete. Now clicks the
  drag handle which calls focusSelf() without entering edit mode.
- Replaced deleteTask helper with inline sequence to avoid wasting
  2s of the 5s undo snackbar window on dialog-detection timeout.
2026-02-04 17:15:13 +01:00
Johannes Millan
b8e448e707 fix(e2e): use desktop selectors for simple counter sync tests
Counter test helpers assumed mobile layout (.mobile-dropdown-wrapper)
but simulated clients use 1920x1080 viewport where counters render
inline in .counters-action-group.
2026-02-03 18:26:54 +01:00
Johannes Millan
350e07e696 fix(e2e): reduce flakiness and speed up WebDAV sync tests
- Remove serial mode and networkidle waits in legacy migration tests
- Reduce optional conflict dialog timeout from 5s to 2s
- Add dialog handler for Client B in first-sync-conflict USE_REMOTE test
- Add delay before sync check and lower threshold in burst sync test
- Add waitForArchivePersistence in concurrent same-task archive test
- Use closeContextsSafely, increase keyboard timing, and add
  waitForStatePersistence in today-tag reorder tests
2026-02-03 17:53:07 +01:00