| 1 | //go:build browser |
| 2 | |
| 3 | package slidestest |
| 4 | |
| 5 | import ( |
| 6 | "testing" |
| 7 | |
| 8 | "github.com/chromedp/chromedp" |
| 9 | "github.com/chromedp/chromedp/kb" |
| 10 | ) |
| 11 | |
| 12 | // TestBrowserDroppedBatchStaysFailed: estilo's savestatus module loads |
| 13 | // and moves the editor's save line, and a batch the server refused |
| 14 | // (a 422, dropped rather than retried) keeps the line on the |
| 15 | // dropped-batch words even after a later save succeeds — main's |
| 16 | // behaviour since the "failed" state split from "Offline, retrying". |
| 17 | func TestBrowserDroppedBatchStaysFailed(t *testing.T) { |
| 18 | rig, mailer := newRig(t) |
| 19 | browserSignIn(t, rig, mailer) |
| 20 | var deck string |
| 21 | rig.Run(chromedp.Evaluate(`document.querySelector('a[href^="/d/"]').getAttribute('href')`, &deck)) |
| 22 | const heading = `[slides-stage] [data-block="heading"]` |
| 23 | rig.Run( |
| 24 | chromedp.Navigate(rig.Origin+deck+"/edit"), |
| 25 | waitFor(`document.querySelector('`+heading+`')?.getAttribute('contenteditable') === 'true'`), |
| 26 | // The line moves at all: a keystroke is "saving", then "saved". |
| 27 | chromedp.Evaluate(`(() => { |
| 28 | const line = document.querySelector('[estilo-save-status]') |
| 29 | window.__states = [] |
| 30 | new MutationObserver(() => window.__states.push(line.dataset.state)).observe(line, { attributes: true }) |
| 31 | })()`, nil), |
| 32 | chromedp.Click(heading, chromedp.ByQuery), |
| 33 | chromedp.SendKeys(heading, string(kb.End)+" kept", chromedp.ByQuery), |
| 34 | waitFor(`window.__states.includes('saving') && document.querySelector('[estilo-save-status]').dataset.state === 'saved'`), |
| 35 | chromedp.Evaluate(`(() => { |
| 36 | const before = window.fetch |
| 37 | let refuse = true |
| 38 | window.fetch = async (...args) => { |
| 39 | if (String(args[0]).endsWith('/updates') && args[1]?.method === 'POST') { |
| 40 | if (refuse) { refuse = false; setTimeout(() => { window.__refused = true }, 0); return new Response('', { status: 422 }) } |
| 41 | const res = await before(...args) |
| 42 | if (res.ok) setTimeout(() => { window.__savedAfter = true }, 0) |
| 43 | return res |
| 44 | } |
| 45 | return before(...args) |
| 46 | } |
| 47 | })()`, nil), |
| 48 | chromedp.Click(heading, chromedp.ByQuery), |
| 49 | chromedp.SendKeys(heading, string(kb.End)+" dropped", chromedp.ByQuery), |
| 50 | waitFor(`window.__refused === true`), |
| 51 | // The resync rebuilt the tab from the server: the dropped words are gone. |
| 52 | waitFor(`!document.querySelector('`+heading+`').textContent.includes('dropped')`), |
| 53 | waitFor(`document.querySelector('`+heading+`')?.getAttribute('contenteditable') === 'true'`), |
| 54 | chromedp.Click(heading, chromedp.ByQuery), |
| 55 | chromedp.SendKeys(heading, string(kb.End)+" later", chromedp.ByQuery), |
| 56 | waitFor(`window.__savedAfter === true`), |
| 57 | chromedp.Sleep(300), |
| 58 | ) |
| 59 | var state, text, want string |
| 60 | rig.Run( |
| 61 | chromedp.Evaluate(`document.querySelector('[estilo-save-status]').dataset.state`, &state), |
| 62 | chromedp.Evaluate(`document.querySelector('[estilo-save-status]').textContent`, &text), |
| 63 | chromedp.Evaluate(`document.querySelector('[estilo-save-status]').dataset.textFailed`, &want), |
| 64 | ) |
| 65 | if state != "failed" || text != want || want == "" { |
| 66 | t.Fatalf("after a dropped batch and a later save the line is state=%q text=%q, want failed %q", state, text, want) |
| 67 | } |
| 68 | rig.Screen("[slides-editor]", "dropped batch stays failed") |
| 69 | } |
| 70 | |