- E2E: Use specific CSS selectors instead of fragile role/text matchers
- E2E: Add visibility assertions before clicking elements
- E2E: Refactor plugin tests to use shared helper functions
- E2E: Add 30s timeout for global-search test suite
- E2E: Increase parallel workers to 4 for local dev speed
- Interface: Add optional snapshotOpType param to uploadSnapshot
- Tests: Fix hydrator spec assertions to match implementation logic
Add documentation explaining why all vectorClock keys must be protected
during SYNC_IMPORT/BACKUP_IMPORT operations to prevent incorrect
CONCURRENT comparisons after vector clock pruning.
Update tests to verify multi-key vectorClock protection.
Add "Remove Encryption" functionality for file-based sync providers
(WebDAV, Dropbox, LocalFile). Users can now disable encryption,
converting synced data to unencrypted format.
Changes:
- Add disableEncryptionForFileBased() method to EncryptionDisableService
- Add "Remove Encryption" button to file-based provider forms
- Add disable-only mode to change password dialog for file-based
- Add unit tests for the new disable flow
- Add E2E test (webdav-encryption-disable.spec.ts) validating full flow
- Extend sync.page.ts with encryption enable/disable helpers
The flow: user clicks Remove Encryption → confirmation dialog →
upload unencrypted snapshot → clear local encryption config.
Make fields that were added after initial provider creation optional so
Typia validation passes with persisted data that has undefined values.
Fixes#6215 - Data Damage Detected dialog on startup
- Test getter returns correct values before, during, and after operations
- Test runWithSyncBlocked executes operations and propagates results/errors
- Test sync is blocked during encryption operations
- Test sync is allowed after encryption operations complete
- Test runWithSyncBlocked waits for ongoing sync before starting
forceUpload() blocks sync() via _isEncryptionOperationInProgress$, but
ImmediateUploadService only checked isSyncInProgress. This allowed it to
trigger during password changes and run server migration checks that
download/decrypt old data - causing DecryptError after password change.
- Expose isEncryptionOperationInProgress getter in SyncWrapperService
- Check the flag in ImmediateUploadService._canUpload()
- Add unit test for the new guard
Fix date-dependent tests using fixed dates relative to mocked today instead
of real current time. Update validation test expectation to match
implementation behavior allowing empty entityChanges without warning.
- Add logging to empty catch block in validation (is-related-model-data-valid.ts)
- Notify users when ANY ops are rejected, not just >= 10 (rejected-ops-handler.service.ts)
- Change encryption auto-disable snackbar from SUCCESS to WARNING
- Add logging when sync-config-default-override.json fails to load
- Add DIALOG_RESULT_ERROR translation key for sync dialog errors
- Add test for single op rejection notification
When Dropbox access token expires and refresh token is missing/invalid,
show user-friendly re-authentication prompt instead of cryptic error.
Fixes#6212
- Use discriminated union for DerivedKeyInfo (type-safe: webcrypto | fallback)
- Extract CryptoStrategy interface with WebCrypto and @noble/ciphers implementations
- Add fallback path tests with mocked crypto.subtle unavailability
- Add cross-compatibility tests (WebCrypto ↔ fallback interoperability)
- Total test count increased from 32 to 43
WebCrypto (crypto.subtle) is unavailable on Android/iOS Capacitor apps
due to insecure contexts. This adds @noble/ciphers as a pure JavaScript
fallback for AES-GCM encryption when WebCrypto is not available.
- Add @noble/ciphers dependency (Cure53 audited, ~4-6KB tree-shaken)
- Update DerivedKeyInfo to support both CryptoKey and raw bytes
- Add fallback encrypt/decrypt functions using gcm from @noble/ciphers
- Legacy PBKDF2 data still requires WebCrypto with clear error message
WebCrypto (crypto.subtle) is unavailable in insecure contexts like
Android Capacitor (serves from http://localhost). Previously, encryption
operations would fail silently, making sync appear successful when data
wasn't actually syncing.
Changes:
- Add isCryptoSubtleAvailable() check function in encryption.ts
- Add assertCryptoSubtleAvailable() guard to encrypt/decrypt functions
- Add WebCryptoNotAvailableError handler in sync-wrapper.service.ts
- Check crypto availability BEFORE deleting server data in
encryption-enable.service.ts and import-encryption-handler.service.ts
- Add WEB_CRYPTO_NOT_AVAILABLE translation key with clear error message
This ensures users see a clear error message instead of silent failure
when trying to use encryption on unsupported platforms.
When concurrent sync triggers occur (especially on Android), a race condition
between filterNewOps() and appendBatch() can cause ConstraintError due to
duplicate operation IDs in the byId unique index.
Instead of failing sync, catch ConstraintError and return empty array - the
ops were already inserted by a concurrent sync.
Fixes#6213
Regular menu items (like tag toggle buttons) were having their clicks
blocked by the touch fix directive. This caused tag assignment from
context menu to not work on mobile devices.
The click blocking is now limited to elements with matMenuTriggerFor
attribute, which are the actual submenu triggers that need protection
against accidental selection when submenus open under the finger.
Empty entityChanges is intentional for most actions since the
actionPayload is sufficient for replay and sync. The warning
was triggering constantly for normal operations like updateTask.
On Android, Capacitor serves the app from http://localhost which is not
a secure context. WebCrypto's crypto.subtle API is unavailable in
insecure contexts, causing Dropbox OAuth PKCE code generation to fail.
Use hash-wasm (already a dependency for Argon2id) as fallback when
crypto.subtle is unavailable.
Move unsynced operations check inside runWithSyncBlocked() to prevent
race condition where background operations could add unsynced ops
between the check and acquiring the lock.
Also add verification step after createCleanSlate() to ensure
SYNC_IMPORT was stored before proceeding, and keep new password
config on upload failure (reverting would encrypt with wrong password).
Fixes flaky E2E test in CI.
- Wait for mat-dialog-container to be visible before clicking
- Scope button selectors within the dialog container
- Wait for dialog to be detached after dismissing
- Remove flaky waitForTimeout in favor of proper state waits
Move the max-height constraint from global mat-dialog-content to the
dialog-schedule-task component specifically, since it has no header
and needs different sizing.
Fields progressOnDone, transitionConfig, and availableTransitions were added
after some users already had OpenProject configs, causing Typia validation
errors. Making them optional allows legacy data to load without errors.
When the app goes to background on Android, accumulated time tracking
data was not being flushed to IndexedDB. If Android terminated the app
while backgrounded, tracked time would be lost.
This fix adds a flushOnPause$ effect that:
- Syncs elapsed time from the native foreground service
- Flushes accumulated time from the BatchedTimeSyncAccumulator
- Writes pending operations to IndexedDB
This follows the same pattern already used for notification button
clicks (handlePauseAction$, handleDoneAction$).
Mock validateAndRepair directly instead of relying on global state
(environment.production, window.confirm spy) that could be affected
by test pollution when running the full test suite.
When isManualBreakStart was enabled, the detectSessionCompletion$ effect
was blocking the completeFocusSession action entirely. This prevented:
- Screen transition from Main to SessionDone
- Completion sound from playing
- Window focus on Electron
- "Start break" button from appearing
The isManualBreakStart check was incorrectly placed in detectSessionCompletion$.
It belongs only in autoStartBreakOnSessionComplete$ (which already has it)
to prevent breaks from auto-starting while still allowing session completion.
Re-enable the data repair system with user confirmation before execution.
When invalid data is detected, show a confirmation dialog asking if the
user wants to repair, giving users control and visibility.
Changes:
- Add translated confirmation dialog before repair executes
- Add isRemote flag to loadAllData dispatch in sync contexts to prevent
LOCAL_ACTIONS effects from firing
- Add user notification when repair fails after confirmation
- Add comprehensive tests for validateAndRepairCurrentState()
- Add documentation for implicit contracts and limitations
- Improve type safety (replace `as any` with proper casts)
- Create composite action for shared E2E setup (Node, npm, Playwright)
- Add build job that builds frontend once and uploads artifact
- Test jobs now download artifact instead of rebuilding
- Cache Playwright browsers to avoid fresh install each run
- Reduces frontend builds from 3x to 1x per workflow run
Split e2e-scheduled.yml into 3 parallel jobs (regular, webdav, supersync)
for ~3x faster nightly runs. Each job only starts the Docker services it
needs. Also reduced fixed wait times in sync-helpers.ts and waits.ts to
improve test execution speed while maintaining reliability.
Add optional chaining for cfg()?.tasks to prevent "Cannot read
properties of undefined (reading 'defaultProjectId')" error during
Dropbox sync when config is not yet loaded.
Fixes#6199
Angular Material dropdowns can fail to open under load when multiple
test workers compete for resources. Add selectSuperSyncProviderWithRetry()
helper that retries the click up to 3 times with a 200ms initial delay.
Applied to setupSuperSync, enableEncryption, and disableEncryption methods.
Update link renderer tests to provide tokens array and mock parser
context as required by marked v17. Remove URL auto-linking tests
since gfm:true handles this automatically in the lexer now.
Use CapacitorHttp instead of fetch() on iOS/Android to avoid
issues with WebKit and WebView handling of HTTP requests. Native
platform tests are skipped as CapacitorHttp cannot be properly
mocked in Jasmine.
Subtasks appearing as top-level items in Today/Tag views (when their
parent is not in the view) were snapping back to original position
when reordered via drag-drop.
Root cause: selectTodayTagRepair selector was excluding ALL subtasks
from valid taskIds, even those that should appear as top-level items.
After reordering, the repair effect immediately "fixed" the order by
removing the subtask.
Changes:
- Update selectTodayTagRepair to include subtasks whose parent is NOT
in the Today list (these appear as top-level items)
- Update enterPredicate to check if subtask's parent is in target
list's tasks for more reliable detection
- Add guard for OVERDUE to BACKLOG edge case in _move method
- Add comprehensive tests for enterPredicate (26 tests)
- Add edge case tests for selectTodayTagRepair (38 tests)
Change DEFAULT_GITLAB_CFG.project from '' to null for semantic
correctness. The type GitlabCfg.project is string | null, so null
properly represents "not configured" rather than an empty string.
Fixes#6138