Recover stale plan drafts and handle local save failures
Test and deploy / test-and-deploy (push) Successful in 1m20s

This commit is contained in:
snowspeeder committed 2026-09-05 16:01:39 -04:00
1 parent 5f3a015ea2
commit 393978195c
2 files changed
+71 -43

No files matched your search

+29
View File
@@ -257,3 +257,32 @@ test(
assert.deepEqual(errors, []);
},
);
test('browser: Your Plans recovers a deleted remote draft without losing edits', browserOptions, async t => {
const {page,base,create,db,errors}=await session(t);
const saved=await create(warningPlan(),'Recovered fruit');
await page.goto(base+'/app?plan='+saved.id);
await page.locator('#plan-form input[name="0.4"]').waitFor();
await db.query('DELETE FROM roast_plans WHERE id=$1',[saved.id]);
await page.locator('#plan-form input[name="0.4"]').fill('530');
await page.getByRole('link',{name:'▤ Your plans',exact:true}).click();
await page.waitForURL('**/plans*');
await page.getByRole('link',{name:'Recovered fruit',exact:true}).waitFor();
const rows=(await db.query('SELECT id,plan FROM roast_plans')).rows;
assert.equal(rows.length,1);assert.notEqual(rows[0].id,saved.id);assert.equal(rows[0].plan.fields['0.4'],'530');assert.deepEqual(errors,[]);
});
test('browser: Your Plans saves to server when browser draft storage is full', browserOptions, async t => {
const {page,base,create,db,errors}=await session(t);const saved=await create(warningPlan());
await page.goto(base+'/app?plan='+saved.id);await page.locator('#plan-form input[name="0.4"]').waitFor();
await page.evaluate(()=>{const original=Storage.prototype.setItem;Storage.prototype.setItem=function(key,value){if(key.startsWith('roastPlannerPlan.v2:'))throw new DOMException('Storage full','QuotaExceededError');return original.call(this,key,value);};});
await page.locator('#plan-form input[name="0.4"]').fill('540');await page.getByRole('link',{name:'▤ Your plans',exact:true}).click();await page.waitForURL('**/plans*');
assert.equal((await db.query('SELECT plan FROM roast_plans WHERE id=$1',[saved.id])).rows[0].plan.fields['0.4'],'540');assert.deepEqual(errors,[]);
});
test('browser: rejected sync shows the cause and keeps the edited plan open', browserOptions, async t => {
const {page,base,create,db}=await session(t);const saved=await create(warningPlan());await page.goto(base+'/app?plan='+saved.id);await page.locator('#plan-form input[name="0.4"]').waitFor();
await page.route('**/api/plans/'+saved.id,async route=>route.request().method()==='PUT'?route.fulfill({status:403,contentType:'application/json',body:JSON.stringify({ok:false,code:'csrf_failed'})}):route.continue());
await page.locator('#plan-form input[name="0.4"]').fill('550');await page.getByRole('link',{name:'▤ Your plans',exact:true}).click();await page.getByText(/Your sign-in session needs refreshing/).waitFor();assert.ok(page.url().includes('/app?plan='+saved.id));assert.equal((await db.query('SELECT COUNT(*) AS n FROM roast_plans')).rows[0].n,1);
const draft=await page.evaluate(()=>JSON.parse(localStorage.getItem(Object.keys(localStorage).find(k=>k.startsWith('roastPlannerPlan.v2:')&&!k.endsWith(':last-user')))));assert.equal(draft.plan.fields['0.4'],'550');
});