The attach-dialog entry point moved out of the task context menu in
PR #7314 and now lives in the detail panel. Update the WebDAV sync
attachment test to use openTaskDetailPanel() and click the attachment
input-item instead of right-clicking and looking for an "Attach" menu
item that no longer exists.
The exportBackup helper used `file-imex button:has-text("Export")`, which
matched both the backup Export button and the newly-text-labeled
"Export Data (anonymized)" privacy button introduced in #7141. Playwright
strict mode on the subsequent click() threw, causing both non-skipped
tests in this file to fail. Switch to an exact name match so only the
intended button is targeted.
* fix(issue): prevent crash from orphan issueProviderId (#7135)
The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.
Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.
* fix(focus-mode): sync tray countdown with in-app timer during breaks
Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.
Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.
Fixes#7278
* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68
Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.
Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3
* fix: restore i18n, UI, and docs work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.
Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior
* fix(electron): restore electron + e2e work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)
Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
(re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
(setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
(electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
(SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
(this is literally the fix the bad commit's message claimed to add)
Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
checks from d64014d086 (later than c558bcab5e) while restoring the
goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
clearStaleLevelDbLocks from theirs, fs from ours).
* fix(sync): restore sync-core work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.
This PR restores only the pieces that are genuinely still missing:
- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
stale-idb-locks.ts piece was restored in the prior commit)
Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)
Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
- supersync-archive-conflict: add closing B sync to the convergence
rounds (A→B→A→B) so B is not one op behind A's final conflict-
resolution state; bump active-list assertion timeout to 15s to
cover the async reducer pipeline + sync-replay event-loop yield.
- supersync.page: fix race in changeEncryptionPassword where a stale
check icon from the previous syncAndWait() caused the method to
return before the server wipe + re-upload finished, leaving the
next client to race against partially-uploaded server state.
Now captures the check-icon state at entry and waits for it to
clear (same pattern syncAndWait uses).
- Separate JsonParseError and SyncDataCorruptedError handlers in sync wrapper
- Handle corrupted remote data gracefully instead of throwing
- Add getOrGenerateClientId() as unified entry point, eliminating dual injection
of ClientIdService + CLIENT_ID_PROVIDER in snapshot-upload, file-based-encryption,
and sync-hydration services
- Use crypto.getRandomValues() instead of Math.random() for client ID generation
- Warn user when stored client ID is invalid and must be regenerated
- Fix flaky e2e supersync tests (premature waitForURL match on daily-summary URL)
Three separate root causes addressed:
1. markTaskDone/markSubtaskDone race with 200ms animation delay:
toggleDoneWithAnimation uses window.setTimeout(200ms) before
dispatching isDone:true. Tests proceeded before the state settled,
causing subsequent assertions to fail. Fix: wait for isDone CSS class
after clicking done-toggle. Also use .first() on the task locator to
avoid Playwright strict-mode violations during CDK drag animation,
where the same task briefly exists as two DOM elements.
2. Premature waitForURL resolution in supersync.spec.ts test 5.1:
waitForURL(/tag\/TODAY/) matched the current /tag/TODAY/daily-summary
URL immediately. Fix: add negative lookahead (?!\/daily-summary).
3. LWW worklog title nondeterminism in archive-conflict test:
After archive-wins conflict resolution and multiple sync rounds, the
archived task title on both clients depends on sync timing — it may be
the original name or the renamed name. Fix: accept either title using
hasTaskInWorklog OR, rather than asserting a specific title.
The URL pattern /(active\/tasks|tag\/TODAY)/ was matching the current
/daily-summary page URL (which contains "tag/TODAY"), causing waitForURL
to resolve immediately without waiting for navigation back to the work
view. This meant syncAndWait() fired before archive operations were
fully uploaded, so Client B received no archived tasks.
Fix: add negative lookahead (?!\/daily-summary) to all three waitForURL
calls (two inline in supersync-daily-summary.spec.ts, one in the shared
archiveDoneTasks() helper). Also updates the helper to accept tag/TODAY
without /tasks suffix, consistent with the rest of the test suite.
When a full-state op (SYNC_IMPORT/BACKUP_IMPORT) arrived from another
client, mergeRemoteOpClocks reset the local clock to a "minimal" form
but only preserved the current client's counter from the incoming op's
clock. If the current client had issued ops (e.g. GLOBAL_CONFIG) not
reflected in the incoming full-state op's clock, its counter was dropped,
causing subsequent ops to reuse the same counter value. Downstream clients
then saw these ops as EQUAL (duplicate) and skipped them silently.
Fix: take max(mergedClock[clientId], currentClock[clientId]) when
rebuilding the clock after a full-state op reset.
Also add __SP_E2E_BLOCK_WS_DOWNLOAD flag to WsTriggeredDownloadService
to allow E2E tests to block automatic WS-triggered downloads during
concurrent conflict scenarios.
Fix archive conflict test by blocking WS downloads on Client A during
the concurrent edit phase so it doesn't auto-receive B's rename via
WebSocket before archiving (restoring the intended conflict scenario).
Fix LWW singleton test to assert convergence rather than specific winner.
Fix renameTask helper to avoid Playwright/Angular re-render races.
Fix shepherd.js import paths that broke the Angular dev server build.
The WebSocket push feature (7fa8f12132) introduced background downloads
via WsTriggeredDownloadService that race with E2E test assertions.
Tests assume controlled, sequential sync via syncAndWait() but WS push
causes data to arrive before tests expect it, breaking conflict scenarios
and route interception.
Block the /api/sync/ws endpoint by default in setupSuperSync() and
opt-in only for the realtime-push test that specifically verifies WS.
* feat(sync): add Helm chart for SuperSync Kubernetes deployment
Includes Deployment, StatefulSet (PostgreSQL), Service, Ingress,
ConfigMap, Secret, HPA, PDB, NetworkPolicy, and test templates.
Supports both bundled PostgreSQL and external database configurations.
* feat(sync): add WebSocket push notifications for near-realtime sync
Server: Fastify WebSocket plugin with connection manager, app-level
heartbeat (30s), debounced notifications, and per-user routing.
Client: WebSocket service with exponential backoff reconnection,
WS-triggered download service, and reduced polling when connected.
* fix(sync): improve WebSocket error handling and reactivity
Make syncInterval$ reactive to WS connection state, fix Set/Map
mutation during heartbeat iteration, add error handling to WS route
handler, separate JSON parsing from message handling, detect auth
failures in WS-triggered downloads, and add logging to all empty
catch blocks.
* fix(sync): address PR review findings for WebSocket and Helm
Fix race condition in WS-triggered download pipeline by moving
isSyncInProgress filter after debounce and adding guard in
_downloadOps. Add logging to remaining empty catch blocks, fix
missing $NODE_IP in Helm NOTES.txt, and correct inaccurate
comments in values.yaml and ws-triggered-download.service.ts.
* fix(sync): add rate limiting to WebSocket upgrade endpoint
Limit WS connection attempts to 10 per minute per IP to prevent
connection flooding, matching the rate-limit pattern used by other
sync endpoints.
* fix(sync): extract shared constants, fix debounce correctness, replace deprecated toPromise
Extract CLIENT_ID_REGEX and MAX_CLIENT_ID_LENGTH into sync.const.ts
to prevent drift between sync.routes.ts and websocket.routes.ts.
Fix debounce in notifyNewOps to accumulate excluded client IDs across
rapid calls from different clients, preventing self-notifications.
Replace deprecated toPromise() with firstValueFrom in connectWebSocket
and _sync methods. Add .catch() to reconnect path and logging to
silent early returns in _downloadOps.
* fix(build): restore correct package-lock.json and fix upstream lint errors
* revert: restore upstream HTML formatting to match CI prettier config
* fix(sync): use DownloadOutcome discriminated union in WsTriggeredDownloadService
* fix(sync): narrow TokenVerificationResult before accessing userId
* fix(sync): await async getProviderById in connectWebSocket
Missing await caused the Promise object to be cast to
SuperSyncProvider, making getWebSocketParams undefined.
* feat(sync): add SEED_USERS env var to create verified users on startup
For self-hosted single-user setups: set SEED_USERS=email1,email2 to
create verified users on boot and log their access tokens. Skips
existing users. Removes need for SMTP/magic link registration.
Also fix Dockerfile to use npm install instead of npm ci for lockfile
compatibility.
* fix(sync): address PR review feedback for Helm chart and server
Security:
- Remove seed-users.ts (logged full JWT tokens to stdout)
- Add fail assertions for missing jwtSecret and postgresql.password
- Add smtp.user/smtp.password values fields
- Add from: selector to NetworkPolicy ingress
- Add egress rule for external database when postgresql.enabled=false
Correctness:
- Fix PostgreSQL StatefulSet indentation for non-persistent mode
- Fix NOTES.txt panic on empty tls list (use len instead of index)
- Restore npm ci in Dockerfile by using node:24-alpine (ships npm 11)
- Add Recreate deployment strategy when using RWO PVC
Operational:
- Add fail guard preventing HPA maxReplicas > 1 (in-memory WS state)
- Fix PDB to use maxUnavailable instead of minAvailable
- Add WebSocket ingress timeout annotation examples
- Add Prisma db push init container for schema migrations
- Default serviceAccount.automount to false
- Add Chart.yaml maintainers, home, sources metadata
* feat(sync): add ALLOWED_EMAILS env var to restrict registration
Supports fully qualified emails (user@example.com) and domain
wildcards (*@example.com). When unset, all emails are allowed.
Applied to all three registration endpoints (passkey options,
passkey verify, magic link).
* fix(sync): exempt health endpoint from rate limiting
Kubernetes liveness/readiness probes hit /health every 5-15s,
exhausting the global rate limit (100 req/15min) and causing
429 responses that trigger container restarts.
* fix(sync): harden WebSocket, Helm chart, and sync services from multi-agent review
- Pin prisma@5.22.0 in init container and use migrate deploy instead of db push
- Add per-user WebSocket connection limit (max 10) to prevent resource exhaustion
- Add replicaCount > 1 fail guard in deployment template (in-memory WS state)
- Set maxPayload: 1024 on WebSocket plugin (only pong messages expected)
- Remove premature IN_SYNC status from download-only WS-triggered sync
- Fix double removeConnection on error+close events (let close handle cleanup)
- DRY debounce logic in notifyNewOps and store latestSeq on pending entry
- Remove dead fromClientId from NewOpsNotification and WsMessage interfaces
- Add takeUntilDestroyed to _wsProviderCleanup subscription
- Simplify email-allowlist.ts to eager init (eliminate mutable state)
- Guard connectWebSocket at call site for non-SuperSync providers
- Add distinctUntilChanged to syncInterval$ to prevent unnecessary resets
- Add container-level securityContext to PostgreSQL StatefulSet
- Fix HPA maxReplicas default to 1 (matches single-replica constraint)
* fix(sync): restore migration in Dockerfile CMD for non-Helm Docker users
Helm users get migration via init container (runs first, CMD becomes no-op).
Docker-compose/plain Docker users get automatic migration back on startup.
* fix(boards): add missing drag delay for touch and extract shared signal
Add cdkDragStartDelay to board-panel drag items, which was missing
entirely. Extract the repeated `isTouchActive() ? DRAG_DELAY_FOR_TOUCH : 0`
expression into a shared `dragDelayForTouch` computed signal and refactor
all 8 components to use it.
* test(sync): add comprehensive WebSocket test coverage
Add unit tests for the new WebSocket real-time sync notification pipeline:
- WebSocketConnectionService (server): connection lifecycle, max-per-user
limits, notification debouncing, heartbeat, graceful shutdown (13 tests)
- WebSocket routes validation: token/clientId validation, close codes,
error handling, validation ordering (18 tests)
- SuperSyncWebSocketService (frontend): reconnection with exponential
backoff, heartbeat, disconnect cleanup, URL conversion (6 tests)
- WsTriggeredDownloadService: auth error handling, pipeline resilience,
start() idempotency (3 tests)
- SyncWrapperService: connectWebSocket guards for non-SuperSync, null
params, and already-connected cases (3 tests)
- E2E realtime push: verifies WS-triggered download between two clients
Quality fixes:
- Replace waitForTimeout with syncAndWait in E2E
- Add explanatory comment for microtask flushing in sync-wrapper spec
- Use getter for isSyncInProgress mock in ws-triggered-download spec
---------
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
Replace HTTP-header-based conflict detection (If-Unmodified-Since, If-Match,
Last-Modified, ETag) with application-level content hashing (MD5), reducing
WebDAV server requirements to just GET, PUT, and MKCOL.
This mirrors the proven pattern from the LocalFile sync provider and eliminates
compatibility issues with WebDAV servers that don't properly support conditional
headers, strip response headers via reverse proxies, or return inconsistent
PROPFIND XML.
Changes:
- download() now computes MD5 hash of response body as rev
- upload() uses GET-compare-PUT instead of conditional PUT headers
- Remove testConditionalHeaders(), _cleanRev(), _getFileMetaViaHead()
- Remove WebdavServerCapabilities, WebdavServerType, basicCompatibilityMode
- Remove conditional header warning dialog from config page
- Remove legacyRev from provider interface
- Simplify webdav.const.ts (remove unused headers/methods/statuses)
- Add E2E test for near-simultaneous two-client sync
- Add unit test for rev stability across downloads
- Fix SuperSync add-button click interception by hovering the group
header first (activates pointer-events) and targeting the button
element instead of mat-icon, with force-click fallback
- Fix WebDAV sync-expansion project lookup by using the proven
navigateToProjectByName helper instead of a fragile manual
sidebar locator
- Reduce flakiness in rapid-sync test by adding explicit timeouts
to post-loop verification assertions
Multiple error paths in the sync pipeline silently swallowed errors,
allowing sync to report IN_SYNC while clients had diverged state.
This caused permanent data loss — tasks missing on one device but
present on another, with no indication anything was wrong.
Fixes:
1. Propagate download failure: when OperationLogDownloadService
returns success=false, throw instead of treating as "no new ops".
Prevents lastServerSeq from advancing past failed downloads.
2. Throw on LWW conflict apply failure: autoResolveConflictsLWW
now throws when applyOperations fails, matching the behavior of
applyNonConflictingOps. Prevents lastServerSeq from advancing
past failed conflict resolutions.
3. Propagate rejected ops handler errors: rethrow instead of
swallowing in the uploadPendingOps catch block, so the
sync-wrapper can set ERROR status.
4. Surface validation failure: validateAfterSync now checks the
return value from validateAndRepairCurrentState and shows an
error snackbar when validation fails.
5. Set ERROR status in sync-wrapper catch-all: the generic error
handler now calls setSyncStatus('ERROR') so the sync icon shows
the red error state.
Fixes#6571
The stale client reconnection test was flaky because syncAndWait()
was called immediately after clicking done-toggle, racing with the
200ms animation delay before isDone is dispatched to the store.
Use markTaskDone helper and wait for state to persist before syncing.
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.
- 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.
- 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)
- 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
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
- 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
- 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
- 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
- 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
- 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
- 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
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.
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.
* fixes to the profile system storage which worked unreliable across multiple storage backends
* fix tests
* fix issues with tests
* address copilot reviews
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.
- 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
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.
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.
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.
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.
- 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
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.
- 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
- 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