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

+41 -2
View File
@@ -201,6 +201,37 @@ async function createBackup() {
backupBlob = await res.blob();
}
// Purge leftover test artifacts from previous failed runs.
// Tests use `__prefix__` naming; anything matching gets deleted before
// the baseline backup so it doesn't get restored after the run either.
async function purgeTestLeftovers() {
const token = () => document.cookie.match(/csrf=([^;]+)/)?.[1] || getCsrf();
let purged = { cards: 0, buckets: 0 };
try {
const cards = await fetch('api/cards').then(r => r.json());
const testCards = cards.filter(c => /^__.*__/.test(c.title || ''));
for (const c of testCards) {
await fetch(`api/cards/${c.id}`, { method: 'DELETE', headers: { 'X-CSRF-Token': token() } });
purged.cards++;
}
} catch {}
try {
const buckets = await fetch('api/buckets').then(r => r.json());
const testBuckets = buckets.filter(b => /^__.*__/.test(b.name || ''));
for (const b of testBuckets) {
await fetch(`api/buckets/${b.id}`, { method: 'DELETE', headers: { 'X-CSRF-Token': token() } });
purged.buckets++;
}
} catch {}
// Also clear any leftover editor drafts so the restore prompt doesn't fire
try {
for (const k of Object.keys(localStorage)) {
if (k.startsWith('hyprcrd-editor-draft')) localStorage.removeItem(k);
}
} catch {}
return purged;
}
async function restoreBackup() {
if (!backupBlob) return;
const form = new FormData();
@@ -398,10 +429,18 @@ async function run() {
const output = panel.querySelector('#test-output');
const summary = panel.querySelector('#test-summary');
// 1. Create backup
output.innerHTML = '<div style="color:#64748b;">creating backup...</div>';
// 1. Purge leftover test artifacts from any previous failed run
output.innerHTML = '<div style="color:#64748b;">cleaning up previous test artifacts...</div>';
summary.textContent = '';
await sleep(50);
const purged = await purgeTestLeftovers();
if (purged.cards || purged.buckets) {
output.innerHTML += `<div style="color:#fbbf24;">purged ${purged.cards} leftover card${purged.cards !== 1 ? 's' : ''}, ${purged.buckets} bucket${purged.buckets !== 1 ? 's' : ''}</div>`;
}
// 2. Create backup (clean baseline)
output.innerHTML += '<div style="color:#64748b;">creating backup...</div>';
await sleep(50);
try {
await createBackup();
} catch (e) {