//go:build browser package slidestest import ( "testing" "github.com/chromedp/chromedp" "github.com/chromedp/chromedp/kb" ) // TestBrowserDroppedBatchStaysFailed: estilo's savestatus module loads // and moves the editor's save line, and a batch the server refused // (a 422, dropped rather than retried) keeps the line on the // dropped-batch words even after a later save succeeds — main's // behaviour since the "failed" state split from "Offline, retrying". func TestBrowserDroppedBatchStaysFailed(t *testing.T) { rig, mailer := newRig(t) browserSignIn(t, rig, mailer) var deck string rig.Run(chromedp.Evaluate(`document.querySelector('a[href^="/d/"]').getAttribute('href')`, &deck)) const heading = `[slides-stage] [data-block="heading"]` rig.Run( chromedp.Navigate(rig.Origin+deck+"/edit"), waitFor(`document.querySelector('`+heading+`')?.getAttribute('contenteditable') === 'true'`), // The line moves at all: a keystroke is "saving", then "saved". chromedp.Evaluate(`(() => { const line = document.querySelector('[estilo-save-status]') window.__states = [] new MutationObserver(() => window.__states.push(line.dataset.state)).observe(line, { attributes: true }) })()`, nil), chromedp.Click(heading, chromedp.ByQuery), chromedp.SendKeys(heading, string(kb.End)+" kept", chromedp.ByQuery), waitFor(`window.__states.includes('saving') && document.querySelector('[estilo-save-status]').dataset.state === 'saved'`), chromedp.Evaluate(`(() => { const before = window.fetch let refuse = true window.fetch = async (...args) => { if (String(args[0]).endsWith('/updates') && args[1]?.method === 'POST') { if (refuse) { refuse = false; setTimeout(() => { window.__refused = true }, 0); return new Response('', { status: 422 }) } const res = await before(...args) if (res.ok) setTimeout(() => { window.__savedAfter = true }, 0) return res } return before(...args) } })()`, nil), chromedp.Click(heading, chromedp.ByQuery), chromedp.SendKeys(heading, string(kb.End)+" dropped", chromedp.ByQuery), waitFor(`window.__refused === true`), // The resync rebuilt the tab from the server: the dropped words are gone. waitFor(`!document.querySelector('`+heading+`').textContent.includes('dropped')`), waitFor(`document.querySelector('`+heading+`')?.getAttribute('contenteditable') === 'true'`), chromedp.Click(heading, chromedp.ByQuery), chromedp.SendKeys(heading, string(kb.End)+" later", chromedp.ByQuery), waitFor(`window.__savedAfter === true`), chromedp.Sleep(300), ) var state, text, want string rig.Run( chromedp.Evaluate(`document.querySelector('[estilo-save-status]').dataset.state`, &state), chromedp.Evaluate(`document.querySelector('[estilo-save-status]').textContent`, &text), chromedp.Evaluate(`document.querySelector('[estilo-save-status]').dataset.textFailed`, &want), ) if state != "failed" || text != want || want == "" { t.Fatalf("after a dropped batch and a later save the line is state=%q text=%q, want failed %q", state, text, want) } rig.Screen("[slides-editor]", "dropped batch stays failed") }