diff options
| author | Hizbu Azka <hanifan.azka@gmail.com> | 2026-10-23 10:45:25 +0700 |
|---|---|---|
| committer | Hizbu Azka <hanifan.azka@gmail.com> | 2026-10-23 10:46:41 +0700 |
| commit | 272b2b94a268e1e342dab2daae4031f4eabdbb8e (patch) | |
| tree | 8e5c49b6e528e5a1a7aa4190a21fbffeebdde165 /_tools/e2e/tests | |
| parent | 2bd366b984bb0ac3ac98691a51f837731af67b2a (diff) | |
run prettier
Diffstat (limited to '_tools/e2e/tests')
| -rw-r--r-- | _tools/e2e/tests/regex-editor.spec.ts | 695 |
1 files changed, 409 insertions, 286 deletions
diff --git a/_tools/e2e/tests/regex-editor.spec.ts b/_tools/e2e/tests/regex-editor.spec.ts index 1f3c0d3..fd0a04f 100644 --- a/_tools/e2e/tests/regex-editor.spec.ts +++ b/_tools/e2e/tests/regex-editor.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, Page, Locator, Dialog } from '@playwright/test'; +import { test, expect, Page, Locator, Dialog } from "@playwright/test"; /** * Playwright spec for https://hizbu.com/regex/ @@ -11,33 +11,39 @@ import { test, expect, Page, Locator, Dialog } from '@playwright/test'; * DOM node (`cmView` / `cmTile` property). If a CodeMirror upgrade ever moves that * property, only the `__cmView` init script below needs updating. */ -const URL = 'http://localhost:4000/regex/'; +const URL = "http://localhost:4000/regex/"; const KEY = { - patterns: 'regex_patterns', - docs: 'regex_documents', - titles: 'regex_tab_titles', - active: 'regex_active_tab', + patterns: "regex_patterns", + docs: "regex_documents", + titles: "regex_tab_titles", + active: "regex_active_tab", }; -const DEFAULT_TITLES = ['Emails & IDs', 'Code Snippet', 'Server Logs']; -const NEW_TAB_TEXT = '// Type or paste text here...\n'; +const DEFAULT_TITLES = ["Emails & IDs", "Code Snippet", "Server Logs"]; +const NEW_TAB_TEXT = "// Type or paste text here...\n"; // ---------- locators ---------- -const topEditor = (p: Page) => p.locator('#regex-editor .cm-content'); -const mainEditor = (p: Page) => p.locator('#cm6-editor .cm-content'); +const topEditor = (p: Page) => p.locator("#regex-editor .cm-content"); +const mainEditor = (p: Page) => p.locator("#cm6-editor .cm-content"); const tabs = (p: Page) => p.locator('#tab-bar [role="tab"]'); -const tabLabel = (p: Page, i: number) => tabs(p).nth(i).locator('span[title="Double-click to rename"]'); -const tabClose = (p: Page, i: number) => tabs(p).nth(i).locator('.tab-close'); -const addTabBtn = (p: Page) => p.locator('#add-tab-btn'); -const fullscreen = (p: Page) => p.locator('#fullscreen'); +const tabLabel = (p: Page, i: number) => + tabs(p).nth(i).locator('span[title="Double-click to rename"]'); +const tabClose = (p: Page, i: number) => tabs(p).nth(i).locator(".tab-close"); +const addTabBtn = (p: Page) => p.locator("#add-tab-btn"); +const fullscreen = (p: Page) => p.locator("#fullscreen"); const matchTexts = (p: Page, n: number) => p.locator(`#cm6-editor .cm-regex-match-${n}`).allTextContents(); // ---------- helpers ---------- const docText = (l: Locator): Promise<string> => l.evaluate((el: any) => { - try { return (window as any).__cmView(el).state.doc.toString(); } - catch { return [...el.querySelectorAll('.cm-line')].map((x: any) => x.textContent).join('\n'); } + try { + return (window as any).__cmView(el).state.doc.toString(); + } catch { + return [...el.querySelectorAll(".cm-line")] + .map((x: any) => x.textContent) + .join("\n"); + } }); const selection = (l: Locator): Promise<{ anchor: number; head: number }> => @@ -47,13 +53,16 @@ const selection = (l: Locator): Promise<{ anchor: number; head: number }> => }); /** Focus the editor WITHOUT changing its selection (unlike click). */ -const focusEditor = (l: Locator) => l.evaluate((el: any) => (window as any).__cmView(el).focus()); +const focusEditor = (l: Locator) => + l.evaluate((el: any) => (window as any).__cmView(el).focus()); -const storage = (p: Page, key: string) => p.evaluate((k) => localStorage.getItem(k), key); -const storageJson = async (p: Page, key: string) => JSON.parse((await storage(p, key)) ?? 'null'); +const storage = (p: Page, key: string) => + p.evaluate((k) => localStorage.getItem(k), key); +const storageJson = async (p: Page, key: string) => + JSON.parse((await storage(p, key)) ?? "null"); async function waitReady(p: Page) { - await expect(p.locator('#loading')).toHaveCount(0, { timeout: 30_000 }); + await expect(p.locator("#loading")).toHaveCount(0, { timeout: 30_000 }); await expect(topEditor(p)).toBeVisible(); await expect(mainEditor(p)).toBeVisible(); } @@ -66,10 +75,10 @@ async function reload(p: Page) { /** Replace the whole content of a CodeMirror editor by typing it. */ async function setDoc(p: Page, editor: Locator, lines: string[]) { await editor.click(); - await p.keyboard.press('ControlOrMeta+a'); - await p.keyboard.press('Backspace'); + await p.keyboard.press("ControlOrMeta+a"); + await p.keyboard.press("Backspace"); for (let i = 0; i < lines.length; i++) { - if (i > 0) await p.keyboard.press('Enter'); + if (i > 0) await p.keyboard.press("Enter"); if (lines[i]) await p.keyboard.type(lines[i]); } } @@ -81,11 +90,16 @@ async function redo(p: Page) { // CodeMirror historyKeymap: Mod-y = redo (Win/Linux), Mod-Shift-z = redo (Mac). // Ctrl+Shift+Z is avoided on purpose: on Linux Chromium it was observed to act as undo. const isMac = await p.evaluate(() => /Mac/i.test(navigator.platform)); - await p.keyboard.press(isMac ? 'Meta+Shift+z' : 'Control+y'); + await p.keyboard.press(isMac ? "Meta+Shift+z" : "Control+y"); } /** Close a tab; asserts the native confirm() text, then accepts or dismisses it. */ -async function closeTab(p: Page, index: number, accept: boolean, expectedName?: string) { +async function closeTab( + p: Page, + index: number, + accept: boolean, + expectedName?: string, +) { // window.confirm() blocks the page, so click() cannot resolve until the dialog is answered. // The handler therefore must be registered BEFORE the click and answer immediately. const seen: { type: string; message: string }[] = []; @@ -93,16 +107,19 @@ async function closeTab(p: Page, index: number, accept: boolean, expectedName?: seen.push({ type: d.type(), message: d.message() }); await (accept ? d.accept() : d.dismiss()); }; - p.on('dialog', handler); + p.on("dialog", handler); try { await tabClose(p, index).click(); } finally { - p.off('dialog', handler); + p.off("dialog", handler); } - expect(seen, 'expected exactly one confirm() dialog').toHaveLength(1); - expect(seen[0].type).toBe('confirm'); - if (expectedName) expect(seen[0].message).toBe(`Are you sure you want to close "${expectedName}"?`); + expect(seen, "expected exactly one confirm() dialog").toHaveLength(1); + expect(seen[0].type).toBe("confirm"); + if (expectedName) + expect(seen[0].message).toBe( + `Are you sure you want to close "${expectedName}"?`, + ); } test.beforeEach(async ({ page }) => { @@ -112,7 +129,10 @@ test.beforeEach(async ({ page }) => { (window as any).__cmView = (el: any) => { const t = el.cmView ?? el.cmTile; const v = t?.view ?? t?.root?.view ?? t?.rootView?.view; - if (!v) throw new Error('Could not locate CodeMirror EditorView on .cm-content'); + if (!v) + throw new Error( + "Could not locate CodeMirror EditorView on .cm-content", + ); return v; }; }); @@ -124,260 +144,312 @@ test.beforeEach(async ({ page }) => { // ===================================================================== // 0. initial state // ===================================================================== -test.describe('initial state', () => { - test('loads default tabs, regexes and highlights', async ({ page }) => { +test.describe("initial state", () => { + test("loads default tabs, regexes and highlights", async ({ page }) => { await expect(tabs(page)).toHaveCount(3); await expect(tabs(page).nth(0)).toHaveClass(/active/); - for (let i = 0; i < 3; i++) await expect(tabLabel(page, i)).toHaveText(DEFAULT_TITLES[i]); + for (let i = 0; i < 3; i++) + await expect(tabLabel(page, i)).toHaveText(DEFAULT_TITLES[i]); - await expect(topEditor(page)).toContainText('[0-9]{4}-[0-9]{2}-[0-9]{2}'); - await expect.poll(() => matchTexts(page, 0)).toContain('support@example.com'); - await expect.poll(() => matchTexts(page, 1)).toContain('2026-10-17'); + await expect(topEditor(page)).toContainText("[0-9]{4}-[0-9]{2}-[0-9]{2}"); + await expect + .poll(() => matchTexts(page, 0)) + .toContain("support@example.com"); + await expect.poll(() => matchTexts(page, 1)).toContain("2026-10-17"); }); }); // ===================================================================== // 1. every regex line is reflected in the lower editor // ===================================================================== -test.describe('regex lines -> lower editor highlights', () => { - test('each line is reflected (highlight class N = line index % 5) as it is typed', async ({ page }) => { - await setDoc(page, mainEditor(page), ['abc 123 DEF 456']); +test.describe("regex lines -> lower editor highlights", () => { + test("each line is reflected (highlight class N = line index % 5) as it is typed", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["abc 123 DEF 456"]); await setDoc(page, topEditor(page), []); // clear default regexes, leaves top editor focused const lines = [ - { text: '/[a-z]+/', expected: ['abc'] }, - { text: '/[0-9]+/', expected: ['123', '456'] }, - { text: '/[A-Z]+/', expected: ['DEF'] }, - { text: '/zzz/', expected: [] }, - { text: '/ /', expected: [' ', ' ', ' '] }, + { text: "/[a-z]+/", expected: ["abc"] }, + { text: "/[0-9]+/", expected: ["123", "456"] }, + { text: "/[A-Z]+/", expected: ["DEF"] }, + { text: "/zzz/", expected: [] }, + { text: "/ /", expected: [" ", " ", " "] }, ]; for (let i = 0; i < lines.length; i++) { - if (i > 0) await page.keyboard.press('Enter'); + if (i > 0) await page.keyboard.press("Enter"); await page.keyboard.type(lines[i].text); // lower editor shows this line's matches with its colour class await expect.poll(() => matchTexts(page, i)).toEqual(lines[i].expected); // top editor line fringe uses the same colour index - await expect(page.locator('#regex-editor .cm-line').nth(i)) - .toHaveClass(new RegExp(`cm-regex-line-${i % 5}`)); + await expect(page.locator("#regex-editor .cm-line").nth(i)).toHaveClass( + new RegExp(`cm-regex-line-${i % 5}`), + ); // all earlier lines are still reflected - for (let j = 0; j < i; j++) expect(await matchTexts(page, j)).toEqual(lines[j].expected); + for (let j = 0; j < i; j++) + expect(await matchTexts(page, j)).toEqual(lines[j].expected); } - await expect.poll(() => storage(page, KEY.patterns)).toBe(lines.map((l) => l.text).join('\n')); - }); - - test('colour index wraps after 5 lines', async ({ page }) => { - await setDoc(page, mainEditor(page), ['w0 w1 w2 w3 w4 w5']); - await setDoc(page, topEditor(page), [0, 1, 2, 3, 4, 5].map((i) => `/w${i}/`)); - - await expect.poll(() => matchTexts(page, 0)).toEqual(['w0', 'w5']); - for (const n of [1, 2, 3, 4]) expect(await matchTexts(page, n)).toEqual([`w${n}`]); - await expect(page.locator('#regex-editor .cm-line').nth(5)).toHaveClass(/cm-regex-line-0/); - }); - - test('blank lines are skipped but still consume a line index', async ({ page }) => { - await setDoc(page, mainEditor(page), ['abc 123']); - await setDoc(page, topEditor(page), ['/abc/', '', '/123/']); - - await expect.poll(() => matchTexts(page, 0)).toEqual(['abc']); + await expect + .poll(() => storage(page, KEY.patterns)) + .toBe(lines.map((l) => l.text).join("\n")); + }); + + test("colour index wraps after 5 lines", async ({ page }) => { + await setDoc(page, mainEditor(page), ["w0 w1 w2 w3 w4 w5"]); + await setDoc( + page, + topEditor(page), + [0, 1, 2, 3, 4, 5].map((i) => `/w${i}/`), + ); + + await expect.poll(() => matchTexts(page, 0)).toEqual(["w0", "w5"]); + for (const n of [1, 2, 3, 4]) + expect(await matchTexts(page, n)).toEqual([`w${n}`]); + await expect(page.locator("#regex-editor .cm-line").nth(5)).toHaveClass( + /cm-regex-line-0/, + ); + }); + + test("blank lines are skipped but still consume a line index", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["abc 123"]); + await setDoc(page, topEditor(page), ["/abc/", "", "/123/"]); + + await expect.poll(() => matchTexts(page, 0)).toEqual(["abc"]); expect(await matchTexts(page, 1)).toEqual([]); - expect(await matchTexts(page, 2)).toEqual(['123']); + expect(await matchTexts(page, 2)).toEqual(["123"]); }); - test('editing a line updates highlights; deleting all lines removes them', async ({ page }) => { - await setDoc(page, mainEditor(page), ['cat dog bird']); - await setDoc(page, topEditor(page), ['/cat/']); - await expect.poll(() => matchTexts(page, 0)).toEqual(['cat']); + test("editing a line updates highlights; deleting all lines removes them", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["cat dog bird"]); + await setDoc(page, topEditor(page), ["/cat/"]); + await expect.poll(() => matchTexts(page, 0)).toEqual(["cat"]); // replace line 0 - await page.keyboard.press('ControlOrMeta+Home'); - await page.keyboard.press('Shift+End'); - await page.keyboard.type('/dog/'); - await expect.poll(() => matchTexts(page, 0)).toEqual(['dog']); + await page.keyboard.press("ControlOrMeta+Home"); + await page.keyboard.press("Shift+End"); + await page.keyboard.type("/dog/"); + await expect.poll(() => matchTexts(page, 0)).toEqual(["dog"]); // delete everything - await page.keyboard.press('ControlOrMeta+a'); - await page.keyboard.press('Backspace'); - await expect(page.locator('#cm6-editor [class*="cm-regex-match-"]')).toHaveCount(0); + await page.keyboard.press("ControlOrMeta+a"); + await page.keyboard.press("Backspace"); + await expect( + page.locator('#cm6-editor [class*="cm-regex-match-"]'), + ).toHaveCount(0); }); - test('editing the lower editor text refreshes highlights', async ({ page }) => { - await setDoc(page, topEditor(page), ['/foo/']); - await setDoc(page, mainEditor(page), ['nothing here']); - await expect(page.locator('#cm6-editor .cm-regex-match-0')).toHaveCount(0); + test("editing the lower editor text refreshes highlights", async ({ + page, + }) => { + await setDoc(page, topEditor(page), ["/foo/"]); + await setDoc(page, mainEditor(page), ["nothing here"]); + await expect(page.locator("#cm6-editor .cm-regex-match-0")).toHaveCount(0); - await page.keyboard.type(' foo foo'); - await expect.poll(() => matchTexts(page, 0)).toEqual(['foo', 'foo']); + await page.keyboard.type(" foo foo"); + await expect.poll(() => matchTexts(page, 0)).toEqual(["foo", "foo"]); }); - test('flags: g default, explicit flags honoured, no-g matches only once', async ({ page }) => { - await setDoc(page, mainEditor(page), ['abc ABC abc']); + test("flags: g default, explicit flags honoured, no-g matches only once", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["abc ABC abc"]); - await setDoc(page, topEditor(page), ['/abc/gi']); - await expect.poll(() => matchTexts(page, 0)).toEqual(['abc', 'ABC', 'abc']); + await setDoc(page, topEditor(page), ["/abc/gi"]); + await expect.poll(() => matchTexts(page, 0)).toEqual(["abc", "ABC", "abc"]); - await setDoc(page, topEditor(page), ['/abc/i']); // no g -> first match only - await expect.poll(() => matchTexts(page, 0)).toEqual(['abc']); + await setDoc(page, topEditor(page), ["/abc/i"]); // no g -> first match only + await expect.poll(() => matchTexts(page, 0)).toEqual(["abc"]); - await setDoc(page, topEditor(page), ['abc']); // no slashes -> raw pattern, g flag - await expect.poll(() => matchTexts(page, 0)).toEqual(['abc', 'abc']); + await setDoc(page, topEditor(page), ["abc"]); // no slashes -> raw pattern, g flag + await expect.poll(() => matchTexts(page, 0)).toEqual(["abc", "abc"]); }); - test('invalid regex line is ignored without breaking other lines', async ({ page }) => { + test("invalid regex line is ignored without breaking other lines", async ({ + page, + }) => { const errors: string[] = []; - page.on('pageerror', (e) => errors.push(e.message)); + page.on("pageerror", (e) => errors.push(e.message)); - await setDoc(page, mainEditor(page), ['abc 123']); - await setDoc(page, topEditor(page), ['/(/', '/123/']); + await setDoc(page, mainEditor(page), ["abc 123"]); + await setDoc(page, topEditor(page), ["/(/", "/123/"]); - await expect.poll(() => matchTexts(page, 1)).toEqual(['123']); + await expect.poll(() => matchTexts(page, 1)).toEqual(["123"]); expect(await matchTexts(page, 0)).toEqual([]); expect(errors).toEqual([]); }); - test('top editor gets one fringe-coloured line per regex line', async ({ page }) => { - await setDoc(page, topEditor(page), ['a', 'b', 'c', 'd', 'e', 'f']); - const lines = page.locator('#regex-editor .cm-line'); + test("top editor gets one fringe-coloured line per regex line", async ({ + page, + }) => { + await setDoc(page, topEditor(page), ["a", "b", "c", "d", "e", "f"]); + const lines = page.locator("#regex-editor .cm-line"); await expect(lines).toHaveCount(6); - for (let i = 0; i < 6; i++) await expect(lines.nth(i)).toHaveClass(new RegExp(`cm-regex-line-${i % 5}`)); + for (let i = 0; i < 6; i++) + await expect(lines.nth(i)).toHaveClass( + new RegExp(`cm-regex-line-${i % 5}`), + ); }); }); // ===================================================================== // 2. localStorage // ===================================================================== -test.describe('localStorage', () => { - test('nothing is written until the first change, then all keys exist', async ({ page }) => { +test.describe("localStorage", () => { + test("nothing is written until the first change, then all keys exist", async ({ + page, + }) => { expect(await storage(page, KEY.docs)).toBeNull(); - await setDoc(page, topEditor(page), ['/x/']); - await expect.poll(() => storage(page, KEY.patterns)).toBe('/x/'); + await setDoc(page, topEditor(page), ["/x/"]); + await expect.poll(() => storage(page, KEY.patterns)).toBe("/x/"); expect(await storageJson(page, KEY.titles)).toEqual(DEFAULT_TITLES); - expect(await storage(page, KEY.active)).toBe('0'); + expect(await storage(page, KEY.active)).toBe("0"); expect((await storageJson(page, KEY.docs)).length).toBe(3); }); - test('regex editor text is stored and restored', async ({ page }) => { - await setDoc(page, topEditor(page), ['/one/', '/two/g']); - await expect.poll(() => storage(page, KEY.patterns)).toBe('/one/\n/two/g'); + test("regex editor text is stored and restored", async ({ page }) => { + await setDoc(page, topEditor(page), ["/one/", "/two/g"]); + await expect.poll(() => storage(page, KEY.patterns)).toBe("/one/\n/two/g"); await reload(page); - expect(await docText(topEditor(page))).toBe('/one/\n/two/g'); + expect(await docText(topEditor(page))).toBe("/one/\n/two/g"); }); - test('lower editor text is stored per tab and restored', async ({ page }) => { - await setDoc(page, mainEditor(page), ['tab zero text']); + test("lower editor text is stored per tab and restored", async ({ page }) => { + await setDoc(page, mainEditor(page), ["tab zero text"]); await switchTo(page, 1); - await setDoc(page, mainEditor(page), ['tab one text']); + await setDoc(page, mainEditor(page), ["tab one text"]); - await expect.poll(async () => (await storageJson(page, KEY.docs))?.[0]).toBe('tab zero text'); - expect((await storageJson(page, KEY.docs))[1]).toBe('tab one text'); - expect(await storage(page, KEY.active)).toBe('1'); + await expect + .poll(async () => (await storageJson(page, KEY.docs))?.[0]) + .toBe("tab zero text"); + expect((await storageJson(page, KEY.docs))[1]).toBe("tab one text"); + expect(await storage(page, KEY.active)).toBe("1"); await reload(page); await expect(tabs(page).nth(1)).toHaveClass(/active/); // active tab restored - expect(await docText(mainEditor(page))).toBe('tab one text'); + expect(await docText(mainEditor(page))).toBe("tab one text"); await switchTo(page, 0); - expect(await docText(mainEditor(page))).toBe('tab zero text'); + expect(await docText(mainEditor(page))).toBe("tab zero text"); }); - test('tab titles and tab count are stored and restored', async ({ page }) => { + test("tab titles and tab count are stored and restored", async ({ page }) => { await addTabBtn(page).click(); await tabLabel(page, 0).dblclick(); - await page.locator('#tab-bar input').fill('Renamed'); - await page.keyboard.press('Enter'); + await page.locator("#tab-bar input").fill("Renamed"); + await page.keyboard.press("Enter"); - await expect.poll(() => storageJson(page, KEY.titles)).toEqual(['Renamed', 'Code Snippet', 'Server Logs', 'Dataset 4']); + await expect + .poll(() => storageJson(page, KEY.titles)) + .toEqual(["Renamed", "Code Snippet", "Server Logs", "Dataset 4"]); await reload(page); await expect(tabs(page)).toHaveCount(4); - await expect(tabLabel(page, 0)).toHaveText('Renamed'); - await expect(tabLabel(page, 3)).toHaveText('Dataset 4'); + await expect(tabLabel(page, 0)).toHaveText("Renamed"); + await expect(tabLabel(page, 3)).toHaveText("Dataset 4"); }); - test('highlights are re-applied after reload', async ({ page }) => { - await setDoc(page, mainEditor(page), ['needle hay needle']); - await setDoc(page, topEditor(page), ['/needle/']); - await expect.poll(() => matchTexts(page, 0)).toEqual(['needle', 'needle']); + test("highlights are re-applied after reload", async ({ page }) => { + await setDoc(page, mainEditor(page), ["needle hay needle"]); + await setDoc(page, topEditor(page), ["/needle/"]); + await expect.poll(() => matchTexts(page, 0)).toEqual(["needle", "needle"]); await reload(page); - await expect.poll(() => matchTexts(page, 0)).toEqual(['needle', 'needle']); + await expect.poll(() => matchTexts(page, 0)).toEqual(["needle", "needle"]); }); - test('clearing localStorage resets to defaults', async ({ page }) => { - await setDoc(page, topEditor(page), ['/wiped/']); - await expect.poll(() => storage(page, KEY.patterns)).toBe('/wiped/'); + test("clearing localStorage resets to defaults", async ({ page }) => { + await setDoc(page, topEditor(page), ["/wiped/"]); + await expect.poll(() => storage(page, KEY.patterns)).toBe("/wiped/"); await page.evaluate(() => localStorage.clear()); await reload(page); await expect(tabs(page)).toHaveCount(3); - expect(await docText(topEditor(page))).not.toContain('wiped'); + expect(await docText(topEditor(page))).not.toContain("wiped"); }); }); // ===================================================================== // 3. rename tab on double click // ===================================================================== -test.describe('rename tab', () => { - const input = (p: Page) => p.locator('#tab-bar input'); +test.describe("rename tab", () => { + const input = (p: Page) => p.locator("#tab-bar input"); - test('double click shows a focused input with the current name selected', async ({ page }) => { + test("double click shows a focused input with the current name selected", async ({ + page, + }) => { await tabLabel(page, 0).dblclick(); await expect(input(page)).toBeVisible(); await expect(input(page)).toBeFocused(); await expect(input(page)).toHaveValue(DEFAULT_TITLES[0]); - expect(await input(page).evaluate((el: HTMLInputElement) => el.selectionEnd! - el.selectionStart!)) - .toBe(DEFAULT_TITLES[0].length); + expect( + await input(page).evaluate( + (el: HTMLInputElement) => el.selectionEnd! - el.selectionStart!, + ), + ).toBe(DEFAULT_TITLES[0].length); }); - test('Enter commits, trims, and persists across reload', async ({ page }) => { + test("Enter commits, trims, and persists across reload", async ({ page }) => { await tabLabel(page, 0).dblclick(); - await input(page).fill(' My Tab '); - await page.keyboard.press('Enter'); + await input(page).fill(" My Tab "); + await page.keyboard.press("Enter"); await expect(input(page)).toHaveCount(0); - await expect(tabLabel(page, 0)).toHaveText('My Tab'); - await expect.poll(async () => (await storageJson(page, KEY.titles))[0]).toBe('My Tab'); + await expect(tabLabel(page, 0)).toHaveText("My Tab"); + await expect + .poll(async () => (await storageJson(page, KEY.titles))[0]) + .toBe("My Tab"); await reload(page); - await expect(tabLabel(page, 0)).toHaveText('My Tab'); + await expect(tabLabel(page, 0)).toHaveText("My Tab"); }); - test('Escape cancels', async ({ page }) => { + test("Escape cancels", async ({ page }) => { await tabLabel(page, 0).dblclick(); - await input(page).fill('nope'); - await page.keyboard.press('Escape'); + await input(page).fill("nope"); + await page.keyboard.press("Escape"); await expect(tabLabel(page, 0)).toHaveText(DEFAULT_TITLES[0]); }); - test('blur commits', async ({ page }) => { + test("blur commits", async ({ page }) => { await tabLabel(page, 0).dblclick(); - await input(page).fill('Blurred'); + await input(page).fill("Blurred"); await mainEditor(page).click(); - await expect(tabLabel(page, 0)).toHaveText('Blurred'); + await expect(tabLabel(page, 0)).toHaveText("Blurred"); }); - test('empty / whitespace-only name keeps the old name', async ({ page }) => { + test("empty / whitespace-only name keeps the old name", async ({ page }) => { await tabLabel(page, 0).dblclick(); - await input(page).fill(' '); - await page.keyboard.press('Enter'); + await input(page).fill(" "); + await page.keyboard.press("Enter"); await expect(tabLabel(page, 0)).toHaveText(DEFAULT_TITLES[0]); }); - test('typing in the rename input (incl. Enter) never edits the editors', async ({ page }) => { - await setDoc(page, mainEditor(page), ['keep me']); - await setDoc(page, topEditor(page), ['/keep/']); + test("typing in the rename input (incl. Enter) never edits the editors", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["keep me"]); + await setDoc(page, topEditor(page), ["/keep/"]); await tabLabel(page, 0).dblclick(); - await input(page).fill('abc'); - await page.keyboard.press('Enter'); - expect(await docText(mainEditor(page))).toBe('keep me'); - expect(await docText(topEditor(page))).toBe('/keep/'); + await input(page).fill("abc"); + await page.keyboard.press("Enter"); + expect(await docText(mainEditor(page))).toBe("keep me"); + expect(await docText(topEditor(page))).toBe("/keep/"); }); - test('renaming a non-active tab also works (and activates it)', async ({ page }) => { + test("renaming a non-active tab also works (and activates it)", async ({ + page, + }) => { await tabLabel(page, 2).dblclick(); - await input(page).fill('Logs!'); - await page.keyboard.press('Enter'); - await expect(tabLabel(page, 2)).toHaveText('Logs!'); + await input(page).fill("Logs!"); + await page.keyboard.press("Enter"); + await expect(tabLabel(page, 2)).toHaveText("Logs!"); await expect(tabs(page).nth(2)).toHaveClass(/active/); }); - test('clicking inside the rename input does not switch tabs', async ({ page }) => { + test("clicking inside the rename input does not switch tabs", async ({ + page, + }) => { await tabLabel(page, 0).dblclick(); await input(page).click(); await expect(input(page)).toBeVisible(); @@ -388,86 +460,104 @@ test.describe('rename tab', () => { // ===================================================================== // 4. add / delete tab (with confirmation) // ===================================================================== -test.describe('add and delete tabs', () => { - test('add creates "Dataset N" with starter text and activates it', async ({ page }) => { +test.describe("add and delete tabs", () => { + test('add creates "Dataset N" with starter text and activates it', async ({ + page, + }) => { await addTabBtn(page).click(); await expect(tabs(page)).toHaveCount(4); - await expect(tabLabel(page, 3)).toHaveText('Dataset 4'); + await expect(tabLabel(page, 3)).toHaveText("Dataset 4"); await expect(tabs(page).nth(3)).toHaveClass(/active/); expect(await docText(mainEditor(page))).toBe(NEW_TAB_TEXT); await addTabBtn(page).click(); - await expect(tabLabel(page, 4)).toHaveText('Dataset 5'); + await expect(tabLabel(page, 4)).toHaveText("Dataset 5"); }); - test('add keeps the previous tab content intact', async ({ page }) => { - await setDoc(page, mainEditor(page), ['original']); + test("add keeps the previous tab content intact", async ({ page }) => { + await setDoc(page, mainEditor(page), ["original"]); await addTabBtn(page).click(); await switchTo(page, 0); - expect(await docText(mainEditor(page))).toBe('original'); + expect(await docText(mainEditor(page))).toBe("original"); }); - test('delete shows confirm() with the tab name; cancelling keeps the tab', async ({ page }) => { - await closeTab(page, 1, false, 'Code Snippet'); + test("delete shows confirm() with the tab name; cancelling keeps the tab", async ({ + page, + }) => { + await closeTab(page, 1, false, "Code Snippet"); await expect(tabs(page)).toHaveCount(3); - await expect(tabLabel(page, 1)).toHaveText('Code Snippet'); + await expect(tabLabel(page, 1)).toHaveText("Code Snippet"); }); - test('accepting removes the tab and persists', async ({ page }) => { - await closeTab(page, 1, true, 'Code Snippet'); + test("accepting removes the tab and persists", async ({ page }) => { + await closeTab(page, 1, true, "Code Snippet"); await expect(tabs(page)).toHaveCount(2); - await expect(tabLabel(page, 0)).toHaveText('Emails & IDs'); - await expect(tabLabel(page, 1)).toHaveText('Server Logs'); + await expect(tabLabel(page, 0)).toHaveText("Emails & IDs"); + await expect(tabLabel(page, 1)).toHaveText("Server Logs"); - await expect.poll(() => storageJson(page, KEY.titles)).toEqual(['Emails & IDs', 'Server Logs']); + await expect + .poll(() => storageJson(page, KEY.titles)) + .toEqual(["Emails & IDs", "Server Logs"]); expect((await storageJson(page, KEY.docs)).length).toBe(2); await reload(page); await expect(tabs(page)).toHaveCount(2); }); - test('close click does not switch to the tab being closed when cancelled', async ({ page }) => { + test("close click does not switch to the tab being closed when cancelled", async ({ + page, + }) => { await closeTab(page, 2, false); await expect(tabs(page).nth(0)).toHaveClass(/active/); }); - test('deleting a tab BEFORE the active one keeps the same tab and content active', async ({ page }) => { + test("deleting a tab BEFORE the active one keeps the same tab and content active", async ({ + page, + }) => { await switchTo(page, 2); const before = await docText(mainEditor(page)); await closeTab(page, 0, true); await expect(tabs(page)).toHaveCount(2); await expect(tabs(page).nth(1)).toHaveClass(/active/); - await expect(tabLabel(page, 1)).toHaveText('Server Logs'); + await expect(tabLabel(page, 1)).toHaveText("Server Logs"); expect(await docText(mainEditor(page))).toBe(before); }); - test('deleting the active LAST tab activates the new last tab', async ({ page }) => { + test("deleting the active LAST tab activates the new last tab", async ({ + page, + }) => { await switchTo(page, 2); await closeTab(page, 2, true); await expect(tabs(page)).toHaveCount(2); await expect(tabs(page).nth(1)).toHaveClass(/active/); - await expect(tabLabel(page, 1)).toHaveText('Code Snippet'); + await expect(tabLabel(page, 1)).toHaveText("Code Snippet"); }); - test('deleting the active FIRST tab activates the tab that took its place', async ({ page }) => { + test("deleting the active FIRST tab activates the tab that took its place", async ({ + page, + }) => { await closeTab(page, 0, true); await expect(tabs(page).nth(0)).toHaveClass(/active/); - await expect(tabLabel(page, 0)).toHaveText('Code Snippet'); + await expect(tabLabel(page, 0)).toHaveText("Code Snippet"); }); - test('last remaining tab cannot be closed (no close button)', async ({ page }) => { + test("last remaining tab cannot be closed (no close button)", async ({ + page, + }) => { await closeTab(page, 0, true); await closeTab(page, 0, true); await expect(tabs(page)).toHaveCount(1); - await expect(page.locator('#tab-bar .tab-close')).toHaveCount(0); + await expect(page.locator("#tab-bar .tab-close")).toHaveCount(0); }); - test('deleted tab edits do not leak into remaining tabs', async ({ page }) => { + test("deleted tab edits do not leak into remaining tabs", async ({ + page, + }) => { await switchTo(page, 1); - await setDoc(page, mainEditor(page), ['doomed']); + await setDoc(page, mainEditor(page), ["doomed"]); await closeTab(page, 1, true); for (let i = 0; i < 2; i++) { await switchTo(page, i); - expect(await docText(mainEditor(page))).not.toBe('doomed'); + expect(await docText(mainEditor(page))).not.toBe("doomed"); } }); }); @@ -475,59 +565,74 @@ test.describe('add and delete tabs', () => { // ===================================================================== // 5. fullscreen (body margin / padding / max-width) // ===================================================================== -test.describe('fullscreen', () => { +test.describe("fullscreen", () => { const bodyMetrics = (p: Page) => p.evaluate(() => { const cs = getComputedStyle(document.body); return { margin: [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft], - padding: [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft], + padding: [ + cs.paddingTop, + cs.paddingRight, + cs.paddingBottom, + cs.paddingLeft, + ], maxWidth: cs.maxWidth, - inline: ['margin', 'padding', 'max-width'].map((k) => document.body.style.getPropertyValue(k)), + inline: ["margin", "padding", "max-width"].map((k) => + document.body.style.getPropertyValue(k), + ), }; }); - const ZERO = ['0px', '0px', '0px', '0px']; + const ZERO = ["0px", "0px", "0px", "0px"]; - test('checkbox is unchecked by default', async ({ page }) => { + test("checkbox is unchecked by default", async ({ page }) => { await expect(fullscreen(page)).not.toBeChecked(); }); - test('checking removes body margin & padding and lifts max-width', async ({ page }) => { + test("checking removes body margin & padding and lifts max-width", async ({ + page, + }) => { await fullscreen(page).check(); const m = await bodyMetrics(page); expect(m.margin).toEqual(ZERO); expect(m.padding).toEqual(ZERO); - expect(m.maxWidth).toBe('none'); - expect(m.inline).toEqual(['0px', '0px', 'none']); + expect(m.maxWidth).toBe("none"); + expect(m.inline).toEqual(["0px", "0px", "none"]); - const box = (await page.locator('body').boundingBox())!; + const box = (await page.locator("body").boundingBox())!; expect(box.x).toBe(0); - const clientWidth = await page.evaluate(() => document.documentElement.clientWidth); + const clientWidth = await page.evaluate( + () => document.documentElement.clientWidth, + ); expect(Math.round(box.width)).toBe(clientWidth); }); - test('unchecking restores the original body styles exactly', async ({ page }) => { + test("unchecking restores the original body styles exactly", async ({ + page, + }) => { const before = await bodyMetrics(page); await fullscreen(page).check(); await fullscreen(page).uncheck(); expect(await bodyMetrics(page)).toEqual(before); }); - test('editors become at least as wide in fullscreen', async ({ page }) => { - const wBefore = (await page.locator('#regex-editor').boundingBox())!.width; + test("editors become at least as wide in fullscreen", async ({ page }) => { + const wBefore = (await page.locator("#regex-editor").boundingBox())!.width; await fullscreen(page).check(); - const wAfter = (await page.locator('#regex-editor').boundingBox())!.width; + const wAfter = (await page.locator("#regex-editor").boundingBox())!.width; expect(wAfter).toBeGreaterThanOrEqual(wBefore); }); - test('fullscreen survives tab switching, add/rename and typing', async ({ page }) => { + test("fullscreen survives tab switching, add/rename and typing", async ({ + page, + }) => { await fullscreen(page).check(); await switchTo(page, 1); await addTabBtn(page).click(); - await setDoc(page, topEditor(page), ['/x/']); + await setDoc(page, topEditor(page), ["/x/"]); await tabLabel(page, 0).dblclick(); - await page.locator('#tab-bar input').fill('FS'); - await page.keyboard.press('Enter'); + await page.locator("#tab-bar input").fill("FS"); + await page.keyboard.press("Enter"); const m = await bodyMetrics(page); expect(m.margin).toEqual(ZERO); @@ -535,7 +640,7 @@ test.describe('fullscreen', () => { await expect(fullscreen(page)).toBeChecked(); }); - test('toggling repeatedly is stable', async ({ page }) => { + test("toggling repeatedly is stable", async ({ page }) => { const before = await bodyMetrics(page); for (let i = 0; i < 3; i++) { await fullscreen(page).check(); @@ -550,21 +655,25 @@ test.describe('fullscreen', () => { // 6. undo / redo / selection / cursor preserved across tab movement // (each tab has its own EditorState in the lower editor) // ===================================================================== -test.describe('per-tab editor state', () => { +test.describe("per-tab editor state", () => { const HISTORY_GAP = 700; // CodeMirror groups edits within 500ms into one undo step - test('cursor position is preserved per tab', async ({ page }) => { - await setDoc(page, mainEditor(page), ['line one', 'line two', 'line three']); - await page.keyboard.press('ControlOrMeta+Home'); - await page.keyboard.press('ArrowDown'); - for (let i = 0; i < 4; i++) await page.keyboard.press('ArrowRight'); + test("cursor position is preserved per tab", async ({ page }) => { + await setDoc(page, mainEditor(page), [ + "line one", + "line two", + "line three", + ]); + await page.keyboard.press("ControlOrMeta+Home"); + await page.keyboard.press("ArrowDown"); + for (let i = 0; i < 4; i++) await page.keyboard.press("ArrowRight"); const cursor0 = await selection(mainEditor(page)); expect(cursor0.head).toBe(9 + 4); expect(cursor0.anchor).toBe(cursor0.head); await switchTo(page, 1); await focusEditor(mainEditor(page)); - await page.keyboard.press('ControlOrMeta+End'); + await page.keyboard.press("ControlOrMeta+End"); const len1 = (await docText(mainEditor(page))).length; expect((await selection(mainEditor(page))).head).toBe(len1); @@ -573,23 +682,25 @@ test.describe('per-tab editor state', () => { // typing at the restored position lands exactly there await focusEditor(mainEditor(page)); - await page.keyboard.type('X'); - expect(await docText(mainEditor(page))).toBe('line one\nlineX two\nline three'); + await page.keyboard.type("X"); + expect(await docText(mainEditor(page))).toBe( + "line one\nlineX two\nline three", + ); await switchTo(page, 1); expect((await selection(mainEditor(page))).head).toBe(len1); }); - test('selection range is preserved per tab', async ({ page }) => { - await setDoc(page, mainEditor(page), ['hello brave new world']); - await page.keyboard.press('ControlOrMeta+Home'); - for (let i = 0; i < 6; i++) await page.keyboard.press('ArrowRight'); - for (let i = 0; i < 5; i++) await page.keyboard.press('Shift+ArrowRight'); // "brave" + test("selection range is preserved per tab", async ({ page }) => { + await setDoc(page, mainEditor(page), ["hello brave new world"]); + await page.keyboard.press("ControlOrMeta+Home"); + for (let i = 0; i < 6; i++) await page.keyboard.press("ArrowRight"); + for (let i = 0; i < 5; i++) await page.keyboard.press("Shift+ArrowRight"); // "brave" expect(await selection(mainEditor(page))).toEqual({ anchor: 6, head: 11 }); await switchTo(page, 1); await focusEditor(mainEditor(page)); - await page.keyboard.press('ControlOrMeta+a'); + await page.keyboard.press("ControlOrMeta+a"); const all1 = await selection(mainEditor(page)); expect(all1.anchor).toBe(0); expect(all1.head).toBeGreaterThan(0); @@ -602,14 +713,16 @@ test.describe('per-tab editor state', () => { // selection is real: typing replaces exactly the preserved range await switchTo(page, 0); await focusEditor(mainEditor(page)); - await page.keyboard.type('BRAVE'); - expect(await docText(mainEditor(page))).toBe('hello BRAVE new world'); + await page.keyboard.type("BRAVE"); + expect(await docText(mainEditor(page))).toBe("hello BRAVE new world"); }); - test('selection direction (anchor vs head) is preserved', async ({ page }) => { - await setDoc(page, mainEditor(page), ['abcdefghij']); - await page.keyboard.press('ControlOrMeta+End'); - for (let i = 0; i < 4; i++) await page.keyboard.press('Shift+ArrowLeft'); // backwards + test("selection direction (anchor vs head) is preserved", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["abcdefghij"]); + await page.keyboard.press("ControlOrMeta+End"); + for (let i = 0; i < 4; i++) await page.keyboard.press("Shift+ArrowLeft"); // backwards const sel = await selection(mainEditor(page)); expect(sel.head).toBeLessThan(sel.anchor); @@ -618,76 +731,80 @@ test.describe('per-tab editor state', () => { expect(await selection(mainEditor(page))).toEqual(sel); }); - test('undo history is preserved per tab and does not leak between tabs', async ({ page }) => { + test("undo history is preserved per tab and does not leak between tabs", async ({ + page, + }) => { // tab 0: three separate undo steps - await setDoc(page, mainEditor(page), ['base']); + await setDoc(page, mainEditor(page), ["base"]); await page.waitForTimeout(HISTORY_GAP); - await page.keyboard.type(' one'); + await page.keyboard.type(" one"); await page.waitForTimeout(HISTORY_GAP); - await page.keyboard.type(' two'); + await page.keyboard.type(" two"); await page.waitForTimeout(HISTORY_GAP); - await page.keyboard.type(' three'); - expect(await docText(mainEditor(page))).toBe('base one two three'); + await page.keyboard.type(" three"); + expect(await docText(mainEditor(page))).toBe("base one two three"); // tab 1: its own edit await switchTo(page, 1); const tab1Original = await docText(mainEditor(page)); await focusEditor(mainEditor(page)); - await page.keyboard.press('ControlOrMeta+End'); - await page.keyboard.type(' T1'); + await page.keyboard.press("ControlOrMeta+End"); + await page.keyboard.type(" T1"); await page.waitForTimeout(HISTORY_GAP); - expect(await docText(mainEditor(page))).toBe(tab1Original + ' T1'); + expect(await docText(mainEditor(page))).toBe(tab1Original + " T1"); // back to tab 0: undo works step by step, with no tab-1 text involved await switchTo(page, 0); await focusEditor(mainEditor(page)); - await page.keyboard.press('ControlOrMeta+z'); - expect(await docText(mainEditor(page))).toBe('base one two'); - await page.keyboard.press('ControlOrMeta+z'); - expect(await docText(mainEditor(page))).toBe('base one'); + await page.keyboard.press("ControlOrMeta+z"); + expect(await docText(mainEditor(page))).toBe("base one two"); + await page.keyboard.press("ControlOrMeta+z"); + expect(await docText(mainEditor(page))).toBe("base one"); // tab 1: its undo only removes its own edit await switchTo(page, 1); await focusEditor(mainEditor(page)); - await page.keyboard.press('ControlOrMeta+z'); + await page.keyboard.press("ControlOrMeta+z"); expect(await docText(mainEditor(page))).toBe(tab1Original); - await page.keyboard.press('ControlOrMeta+z'); // nothing more to undo + await page.keyboard.press("ControlOrMeta+z"); // nothing more to undo expect(await docText(mainEditor(page))).toBe(tab1Original); // tab 0 untouched by tab 1's undo await switchTo(page, 0); - expect(await docText(mainEditor(page))).toBe('base one'); + expect(await docText(mainEditor(page))).toBe("base one"); }); - test('redo history is preserved across tab switches', async ({ page }) => { - await setDoc(page, mainEditor(page), ['alpha']); + test("redo history is preserved across tab switches", async ({ page }) => { + await setDoc(page, mainEditor(page), ["alpha"]); await page.waitForTimeout(HISTORY_GAP); - await page.keyboard.type(' beta'); + await page.keyboard.type(" beta"); await page.waitForTimeout(HISTORY_GAP); - await page.keyboard.type(' gamma'); - await page.keyboard.press('ControlOrMeta+z'); - await page.keyboard.press('ControlOrMeta+z'); - expect(await docText(mainEditor(page))).toBe('alpha'); + await page.keyboard.type(" gamma"); + await page.keyboard.press("ControlOrMeta+z"); + await page.keyboard.press("ControlOrMeta+z"); + expect(await docText(mainEditor(page))).toBe("alpha"); await switchTo(page, 2); await focusEditor(mainEditor(page)); - await page.keyboard.type('noise'); + await page.keyboard.type("noise"); await page.waitForTimeout(HISTORY_GAP); await switchTo(page, 0); - expect(await docText(mainEditor(page))).toBe('alpha'); + expect(await docText(mainEditor(page))).toBe("alpha"); await focusEditor(mainEditor(page)); await redo(page); - expect(await docText(mainEditor(page))).toBe('alpha beta'); + expect(await docText(mainEditor(page))).toBe("alpha beta"); await redo(page); - expect(await docText(mainEditor(page))).toBe('alpha beta gamma'); + expect(await docText(mainEditor(page))).toBe("alpha beta gamma"); }); - test('undo restores selection/cursor consistently after returning to a tab', async ({ page }) => { - await setDoc(page, mainEditor(page), ['12345']); + test("undo restores selection/cursor consistently after returning to a tab", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["12345"]); await page.waitForTimeout(HISTORY_GAP); - await page.keyboard.type('678'); - await page.keyboard.press('Home'); + await page.keyboard.type("678"); + await page.keyboard.press("Home"); const cursor = await selection(mainEditor(page)); await switchTo(page, 1); @@ -695,30 +812,32 @@ test.describe('per-tab editor state', () => { expect(await selection(mainEditor(page))).toEqual(cursor); await focusEditor(mainEditor(page)); - await page.keyboard.press('ControlOrMeta+z'); - expect(await docText(mainEditor(page))).toBe('12345'); + await page.keyboard.press("ControlOrMeta+z"); + expect(await docText(mainEditor(page))).toBe("12345"); }); - test('a newly added tab has a fresh history (undo is a no-op) and cursor at start', async ({ page }) => { - await setDoc(page, mainEditor(page), ['something']); + test("a newly added tab has a fresh history (undo is a no-op) and cursor at start", async ({ + page, + }) => { + await setDoc(page, mainEditor(page), ["something"]); await addTabBtn(page).click(); await focusEditor(mainEditor(page)); expect(await selection(mainEditor(page))).toEqual({ anchor: 0, head: 0 }); - await page.keyboard.press('ControlOrMeta+z'); + await page.keyboard.press("ControlOrMeta+z"); expect(await docText(mainEditor(page))).toBe(NEW_TAB_TEXT); }); - test('state survives many rapid tab switches', async ({ page }) => { - await setDoc(page, mainEditor(page), ['AAAAAAAAAA']); - await page.keyboard.press('ControlOrMeta+Home'); - for (let i = 0; i < 2; i++) await page.keyboard.press('ArrowRight'); - for (let i = 0; i < 3; i++) await page.keyboard.press('Shift+ArrowRight'); + test("state survives many rapid tab switches", async ({ page }) => { + await setDoc(page, mainEditor(page), ["AAAAAAAAAA"]); + await page.keyboard.press("ControlOrMeta+Home"); + for (let i = 0; i < 2; i++) await page.keyboard.press("ArrowRight"); + for (let i = 0; i < 3; i++) await page.keyboard.press("Shift+ArrowRight"); const selA = await selection(mainEditor(page)); await switchTo(page, 1); - await setDoc(page, mainEditor(page), ['BBBBBBBBBB']); - await page.keyboard.press('ControlOrMeta+Home'); - for (let i = 0; i < 6; i++) await page.keyboard.press('ArrowRight'); + await setDoc(page, mainEditor(page), ["BBBBBBBBBB"]); + await page.keyboard.press("ControlOrMeta+Home"); + for (let i = 0; i < 6; i++) await page.keyboard.press("ArrowRight"); const selB = await selection(mainEditor(page)); for (let i = 0; i < 5; i++) { @@ -730,24 +849,28 @@ test.describe('per-tab editor state', () => { expect(await selection(mainEditor(page))).toEqual(selA); }); - test('highlights are refreshed per tab when switching (stale regex queries)', async ({ page }) => { - await setDoc(page, topEditor(page), ['/zqx/']); - await setDoc(page, mainEditor(page), ['zqx qqq']); - await expect.poll(() => matchTexts(page, 0)).toEqual(['zqx']); + test("highlights are refreshed per tab when switching (stale regex queries)", async ({ + page, + }) => { + await setDoc(page, topEditor(page), ["/zqx/"]); + await setDoc(page, mainEditor(page), ["zqx qqq"]); + await expect.poll(() => matchTexts(page, 0)).toEqual(["zqx"]); await switchTo(page, 1); - await expect(page.locator('#cm6-editor .cm-regex-match-0')).toHaveCount(0); + await expect(page.locator("#cm6-editor .cm-regex-match-0")).toHaveCount(0); // change the regex while on tab 1, then return to tab 0: must use the NEW regex - await setDoc(page, topEditor(page), ['/qqq/']); + await setDoc(page, topEditor(page), ["/qqq/"]); await switchTo(page, 0); - await expect.poll(() => matchTexts(page, 0)).toEqual(['qqq']); + await expect.poll(() => matchTexts(page, 0)).toEqual(["qqq"]); }); - test('switching tabs does not alter the regex (top) editor', async ({ page }) => { - await setDoc(page, topEditor(page), ['/keep/', '/me/']); + test("switching tabs does not alter the regex (top) editor", async ({ + page, + }) => { + await setDoc(page, topEditor(page), ["/keep/", "/me/"]); await switchTo(page, 1); await switchTo(page, 2); - expect(await docText(topEditor(page))).toBe('/keep/\n/me/'); + expect(await docText(topEditor(page))).toBe("/keep/\n/me/"); }); }); |