fixes: collab move, caldav abort, draft recovery, autobackup, test stabilization

- Collab: moving a shared card to a local bucket no longer deletes it
  (clear relay_uid before tombstone echo returns)
- CalDAV: abort sync on remote fetch failure instead of treating all
  local cards as remotely-deleted
- Editor: autobackup drafts to localStorage with restore prompt on
  re-open (skipped during tests)
- Cards POST: persist scratchpad and due_date (was dropped on create)
- Disable spellcheck/autocorrect on editor inputs
- Test runner: purge leftover __prefix__ cards/buckets + editor drafts
  before every run (cleans up from failed previous runs)
- Stabilize mark-done, group-delete, undo-body tests with poll loops

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.6 committed 2026-04-14 15:01:07 +02:00
1 parent 7e2b130434
commit 3cca14a3c6
8 files changed
+178 -23

No files matched your search

+23 -6
View File
@@ -157,14 +157,26 @@ describe('Card: Context Menu Actions', () => {
expect(doneBtn.textContent.toLowerCase()).toContain('mark done');
click(doneBtn);
await sleep(300);
let updated = await fetch(`api/cards?ids=${res.id}`).then(r => r.json());
// Poll until #done is persisted to the DB
let updated;
for (let i = 0; i < 40; i++) {
await sleep(100);
updated = await fetch(`api/cards?ids=${res.id}`).then(r => r.json());
if ((updated[0]?.tags || '').includes('done')) break;
}
expect((updated[0].body || '').toLowerCase()).toContain('#done');
expect(updated[0].tags || '').toContain('done');
// Wait for the board to re-render with the updated card data
for (let i = 0; i < 20; i++) {
const card = window.state?.cards?.find(c => c.id == res.id);
if (card && (card.tags || '').includes('done')) break;
await sleep(100);
}
// Re-open menu — the entry should now flip to "Unmark done"
rightClick(document.querySelector(`[data-id="${res.id}"]`));
await sleep(150);
await sleep(200);
const doneBtn2 = $$('#ctx-items [data-action="due-done"]')[0];
expect(doneBtn2.textContent.toLowerCase()).toContain('unmark');
@@ -331,11 +343,16 @@ describe('Card: Group Delete', () => {
// Confirm the delete
const okBtn = document.getElementById('dialog-ok');
if (okBtn) click(okBtn);
await sleep(500);
}
// All three cards should be gone
const remaining = await fetch('api/cards').then(r => r.json());
// Poll until all three cards are gone (group delete = N serial DELETEs)
let remaining;
for (let i = 0; i < 40; i++) {
await sleep(100);
remaining = await fetch('api/cards').then(r => r.json());
const stillThere = remaining.some(c => c.id === parent.id || c.id === child1.id || c.id === child2.id);
if (!stillThere) break;
}
expect(remaining.some(c => c.id === parent.id)).toBe(false);
expect(remaining.some(c => c.id === child1.id)).toBe(false);
expect(remaining.some(c => c.id === child2.id)).toBe(false);