From bf61564510648ce1e132aca100e08e6f1be82539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Mon, 20 Apr 2026 22:27:37 +0900 Subject: [PATCH 01/15] test: add test cases for AST node expansion --- e2e-tests/code-editing-and-ast-interaction.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 3d683bd1..3ac4e5dc 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -127,3 +127,7 @@ test(`should keep ESQuery highlights aligned while typing before a matching lite ).toBe(true); await expect(highlight).toHaveText(["42"]); }); + +test.describe("AST node expansion", () => { + // TODO +}); From a5d69f66e98f5f5c13e6c3afd5f4427655be4446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Mon, 25 May 2026 19:28:51 +0900 Subject: [PATCH 02/15] wip --- .../code-editing-and-ast-interaction.test.ts | 94 ++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 3ac4e5dc..d9c8e435 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -129,5 +129,97 @@ test(`should keep ESQuery highlights aligned while typing before a matching lite }); test.describe("AST node expansion", () => { - // TODO + test.beforeEach(async ({ page }) => { + await page.goto("/"); + await page.getByRole("button", { name: "Language Options" }).click(); + }); + + test.describe("Language: JavaScript", () => {}); + + test.describe("Language: JSON", () => {}); + + test.describe("Language: Markdown", () => { + test.beforeEach(async ({ page }) => { + // `Language`: select `Markdown` + await page + .getByRole("combobox", { exact: true, name: "Language" }) + .click(); + await page + .getByRole("option", { exact: true, name: "Markdown" }) + .click(); + }); + + test("Mode: CommonMark", async ({ page }) => { + // `Mode`: `CommonMark` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "CommonMark" }) + .click(); + await expect(modeSelect).toHaveText("CommonMark"); + + // `Front Matter`: `Off` + const frontMatterSelect = page.getByRole("combobox", { + exact: true, + name: "Front Matter", + }); + await frontMatterSelect.click(); + await page + .getByRole("option", { exact: true, name: "Off" }) + .click(); + await expect(frontMatterSelect).toHaveText("Off"); + + // `Math`: `false` + const mathSwitch = page.getByRole("switch", { + exact: true, + name: "Math", + }); + await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a CommonMark sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("text, *emphasis*, **strong**"); + + // Verify that the AST structure matches expectations for CommonMark. + await page + .getByRole("region", { name: "root" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. paragraph" }).click(); + + await page + .getByRole("region", { name: "0. paragraph" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[4 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. text" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. emphasis" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "2. text" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "3. strong" }), + ).toBeVisible(); + }); + }); + + test.describe("Language: CSS", () => {}); + + test.describe("Language: HTML", () => {}); }); From 62bf5b03abf7162bf9eca4735ec2a247f8517f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Mon, 25 May 2026 19:37:58 +0900 Subject: [PATCH 03/15] wip --- .../code-editing-and-ast-interaction.test.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index d9c8e435..52927341 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -217,6 +217,78 @@ test.describe("AST node expansion", () => { page.getByRole("button", { name: "3. strong" }), ).toBeVisible(); }); + + test("Mode: GFM", async ({ page }) => { + // `Mode`: `GFM` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { + exact: true, + name: "GitHub-Flavored", + }) + .click(); + await expect(modeSelect).toHaveText("GitHub-Flavored"); + + // `Front Matter`: `Off` + const frontMatterSelect = page.getByRole("combobox", { + exact: true, + name: "Front Matter", + }); + await frontMatterSelect.click(); + await page + .getByRole("option", { exact: true, name: "Off" }) + .click(); + await expect(frontMatterSelect).toHaveText("Off"); + + // `Math`: `false` + const mathSwitch = page.getByRole("switch", { + exact: true, + name: "Math", + }); + await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a GFM sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("text, ~~delete~~, *emphasis*"); + + // Verify that the AST structure matches expectations for GFM. + await page + .getByRole("region", { name: "root" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. paragraph" }).click(); + + await page + .getByRole("region", { name: "0. paragraph" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[4 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. text" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. delete" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "2. text" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "3. emphasis" }), + ).toBeVisible(); + }); }); test.describe("Language: CSS", () => {}); From 4b699d52b7e619f5de3b2925bd1711264887e993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Mon, 25 May 2026 19:48:26 +0900 Subject: [PATCH 04/15] wip --- .../code-editing-and-ast-interaction.test.ts | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 52927341..518a0b75 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -226,10 +226,7 @@ test.describe("AST node expansion", () => { }); await modeSelect.click(); await page - .getByRole("option", { - exact: true, - name: "GitHub-Flavored", - }) + .getByRole("option", { exact: true, name: "GitHub-Flavored" }) .click(); await expect(modeSelect).toHaveText("GitHub-Flavored"); @@ -289,6 +286,60 @@ test.describe("AST node expansion", () => { page.getByRole("button", { name: "3. emphasis" }), ).toBeVisible(); }); + + test("Frontmatter: YAML", async ({ page }) => { + // `Mode`: `CommonMark` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "CommonMark" }) + .click(); + await expect(modeSelect).toHaveText("CommonMark"); + + // `Front Matter`: `YAML` + const frontMatterSelect = page.getByRole("combobox", { + exact: true, + name: "Front Matter", + }); + await frontMatterSelect.click(); + await page + .getByRole("option", { exact: true, name: "YAML" }) + .click(); + await expect(frontMatterSelect).toHaveText("YAML"); + + // `Math`: `false` + const mathSwitch = page.getByRole("switch", { + exact: true, + name: "Math", + }); + await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a Markdown sample with YAML frontmatter into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("---\ntitle: Test\n---\n\ntext"); + + // Verify that the AST structure matches expectations for YAML frontmatter. + await page + .getByRole("region", { name: "root" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[2 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. yaml" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. paragraph" }), + ).toBeVisible(); + }); }); test.describe("Language: CSS", () => {}); From a8e6c8d19e98037a6e1f1dd6bcab3e0532c89831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Mon, 25 May 2026 19:55:40 +0900 Subject: [PATCH 05/15] wip --- .../code-editing-and-ast-interaction.test.ts | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 518a0b75..571b8a90 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -340,6 +340,114 @@ test.describe("AST node expansion", () => { page.getByRole("button", { name: "1. paragraph" }), ).toBeVisible(); }); + + test("Frontmatter: TOML", async ({ page }) => { + // `Mode`: `CommonMark` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "CommonMark" }) + .click(); + await expect(modeSelect).toHaveText("CommonMark"); + + // `Front Matter`: `TOML` + const frontMatterSelect = page.getByRole("combobox", { + exact: true, + name: "Front Matter", + }); + await frontMatterSelect.click(); + await page + .getByRole("option", { exact: true, name: "TOML" }) + .click(); + await expect(frontMatterSelect).toHaveText("TOML"); + + // `Math`: `false` + const mathSwitch = page.getByRole("switch", { + exact: true, + name: "Math", + }); + await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a Markdown sample with TOML frontmatter into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("+++\ncount = 1\n+++\n\ntext"); + + // Verify that the AST structure matches expectations for TOML frontmatter. + await page + .getByRole("region", { name: "root" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[2 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. toml" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. paragraph" }), + ).toBeVisible(); + }); + + test("Frontmatter: JSON", async ({ page }) => { + // `Mode`: `CommonMark` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "CommonMark" }) + .click(); + await expect(modeSelect).toHaveText("CommonMark"); + + // `Front Matter`: `JSON` + const frontMatterSelect = page.getByRole("combobox", { + exact: true, + name: "Front Matter", + }); + await frontMatterSelect.click(); + await page + .getByRole("option", { exact: true, name: "JSON" }) + .click(); + await expect(frontMatterSelect).toHaveText("JSON"); + + // `Math`: `false` + const mathSwitch = page.getByRole("switch", { + exact: true, + name: "Math", + }); + await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a Markdown sample with JSON frontmatter into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill('---\n{"count":1}\n---\n\ntext'); + + // Verify that the AST structure matches expectations for JSON frontmatter. + await page + .getByRole("region", { name: "root" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[2 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. json" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. paragraph" }), + ).toBeVisible(); + }); }); test.describe("Language: CSS", () => {}); From aafec49127ac3122fbd60409a8f317319bda1c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Mon, 25 May 2026 20:04:50 +0900 Subject: [PATCH 06/15] wip --- .../code-editing-and-ast-interaction.test.ts | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 571b8a90..bb1e02f6 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -448,6 +448,80 @@ test.describe("AST node expansion", () => { page.getByRole("button", { name: "1. paragraph" }), ).toBeVisible(); }); + + test("Math: true", async ({ page }) => { + // `Mode`: `CommonMark` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "CommonMark" }) + .click(); + await expect(modeSelect).toHaveText("CommonMark"); + + // `Front Matter`: `Off` + const frontMatterSelect = page.getByRole("combobox", { + exact: true, + name: "Front Matter", + }); + await frontMatterSelect.click(); + await page + .getByRole("option", { exact: true, name: "Off" }) + .click(); + await expect(frontMatterSelect).toHaveText("Off"); + + // `Math`: `true` + const mathSwitch = page.getByRole("switch", { + exact: true, + name: "Math", + }); + await mathSwitch.click(); + await expect(mathSwitch).toHaveAttribute("aria-checked", "true"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a Markdown sample with inline and block math into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("inline $x + y$ math\n\n$$\nx + y\n$$"); + + // Verify that the AST structure matches expectations for math. + await page + .getByRole("region", { name: "root" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[2 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. paragraph" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. math" }), + ).toBeVisible(); + + await page.getByRole("button", { name: "0. paragraph" }).click(); + + await page + .getByRole("region", { name: "0. paragraph" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[3 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. text" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. inlineMath" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "2. text" }), + ).toBeVisible(); + }); }); test.describe("Language: CSS", () => {}); From 0335008fcd13f54db4406ae9270ce636137d7b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 17:56:33 +0900 Subject: [PATCH 07/15] wip --- .../code-editing-and-ast-interaction.test.ts | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index bb1e02f6..dbef4991 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -134,9 +134,29 @@ test.describe("AST node expansion", () => { await page.getByRole("button", { name: "Language Options" }).click(); }); - test.describe("Language: JavaScript", () => {}); + test.describe("Language: JavaScript", () => { + test.beforeEach(async ({ page }) => { + // `Language`: select `JavaScript` + await page + .getByRole("combobox", { exact: true, name: "Language" }) + .click(); + await page + .getByRole("option", { exact: true, name: "JavaScript" }) + .click(); + }); + }); - test.describe("Language: JSON", () => {}); + test.describe("Language: JSON", () => { + test.beforeEach(async ({ page }) => { + // `Language`: select `JSON` + await page + .getByRole("combobox", { exact: true, name: "Language" }) + .click(); + await page + .getByRole("option", { exact: true, name: "JSON" }) + .click(); + }); + }); test.describe("Language: Markdown", () => { test.beforeEach(async ({ page }) => { @@ -524,7 +544,27 @@ test.describe("AST node expansion", () => { }); }); - test.describe("Language: CSS", () => {}); + test.describe("Language: CSS", () => { + test.beforeEach(async ({ page }) => { + // `Language`: select `CSS` + await page + .getByRole("combobox", { exact: true, name: "Language" }) + .click(); + await page + .getByRole("option", { exact: true, name: "CSS" }) + .click(); + }); + }); - test.describe("Language: HTML", () => {}); + test.describe("Language: HTML", () => { + test.beforeEach(async ({ page }) => { + // `Language`: select `HTML` + await page + .getByRole("combobox", { exact: true, name: "Language" }) + .click(); + await page + .getByRole("option", { exact: true, name: "HTML" }) + .click(); + }); + }); }); From 0a5ee0d8a93c8fdd3dc83ee73c4ae0408b58f45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 18:10:18 +0900 Subject: [PATCH 08/15] wip --- .../code-editing-and-ast-interaction.test.ts | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index dbef4991..7627eebd 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -156,6 +156,211 @@ test.describe("AST node expansion", () => { .getByRole("option", { exact: true, name: "JSON" }) .click(); }); + + test("Mode: JSON", async ({ page }) => { + // `Mode`: `JSON` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "json" }) + .click(); + await expect(modeSelect).toHaveText("json"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a JSON sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill('{"foo":"bar"}'); + + // Verify that the AST structure matches expectations for JSON. + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "bodyObject{type, members, loc, ...}" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "membersArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Member" }), + ).toBeVisible(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "tokensArray[5 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. LBrace" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. String" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "2. Colon" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "3. String" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "4. RBrace" }), + ).toBeVisible(); + }); + + test("Mode: JSONC", async ({ page }) => { + // `Mode`: `JSONC` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "jsonc" }) + .click(); + await expect(modeSelect).toHaveText("jsonc"); + + // `Allow Trailing Commas`: `false` + const allowTrailingCommasSwitch = page.getByRole("switch", { + exact: true, + name: "Allow Trailing Commas", + }); + await expect(allowTrailingCommasSwitch).toHaveAttribute( + "aria-checked", + "false", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a JSONC sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill('{\n// comment\n"foo":"bar"\n}'); + + // Verify that the AST structure matches expectations for JSONC. + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "bodyObject{type, members, loc, ...}" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "membersArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Member" }), + ).toBeVisible(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "tokensArray[6 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. LBrace" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. LineComment" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "2. String" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "3. Colon" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "4. String" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "5. RBrace" }), + ).toBeVisible(); + }); + + test("Mode: JSON5", async ({ page }) => { + // `Mode`: `JSON5` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "json5" }) + .click(); + await expect(modeSelect).toHaveText("json5"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a JSON5 sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill('{foo:"bar",}'); + + // Verify that the AST structure matches expectations for JSON5. + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "bodyObject{type, members, loc, ...}" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "membersArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Member" }), + ).toBeVisible(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "tokensArray[6 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. LBrace" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. Identifier" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "2. Colon" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "3. String" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "4. Comma" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "5. RBrace" }), + ).toBeVisible(); + }); }); test.describe("Language: Markdown", () => { From c7c3d03a6af903f97aca640025b5ee1405e3fbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 18:16:54 +0900 Subject: [PATCH 09/15] wip --- .../code-editing-and-ast-interaction.test.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 7627eebd..53b7b302 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -361,6 +361,86 @@ test.describe("AST node expansion", () => { page.getByRole("button", { name: "5. RBrace" }), ).toBeVisible(); }); + + test("Allow Trailing Commas: true", async ({ page }) => { + // `Mode`: `JSONC` + const modeSelect = page.getByRole("combobox", { + exact: true, + name: "Mode", + }); + await modeSelect.click(); + await page + .getByRole("option", { exact: true, name: "jsonc" }) + .click(); + await expect(modeSelect).toHaveText("jsonc"); + + // `Allow Trailing Commas`: `true` + const allowTrailingCommasSwitch = page.getByRole("switch", { + exact: true, + name: "Allow Trailing Commas", + }); + await allowTrailingCommasSwitch.click(); + await expect(allowTrailingCommasSwitch).toHaveAttribute( + "aria-checked", + "true", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a JSONC sample with a trailing comma into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill('{\n// comment\n"foo":"bar",\n}'); + + // Verify that the AST structure matches expectations for JSONC with trailing commas. + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "bodyObject{type, members, loc, ...}" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "membersArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Member" }), + ).toBeVisible(); + + await page + .getByRole("region", { name: "Document" }) + .getByRole("listitem") + .filter({ hasText: "tokensArray[7 elements]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. LBrace" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "1. LineComment" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "2. String" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "3. Colon" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "4. String" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "5. Comma" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "6. RBrace" }), + ).toBeVisible(); + }); }); test.describe("Language: Markdown", () => { From 85684a86286fc36e565184d6243230b50caf3eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 18:30:51 +0900 Subject: [PATCH 10/15] wip --- .../code-editing-and-ast-interaction.test.ts | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 53b7b302..7693d893 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -839,6 +839,71 @@ test.describe("AST node expansion", () => { .getByRole("option", { exact: true, name: "CSS" }) .click(); }); + + test("Tolerant Parsing: false", async ({ page }) => { + // `Tolerant Parsing`: `false` + const tolerantSwitch = page.getByRole("switch", { + exact: true, + name: "Tolerant Parsing", + }); + await expect(tolerantSwitch).toHaveAttribute( + "aria-checked", + "false", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a CSS sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("a{}"); + + // Verify that the AST structure matches expectations for CSS. + await page + .getByRole("region", { name: "StyleSheet" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Rule" }), + ).toBeVisible(); + }); + + test("Tolerant Parsing: true", async ({ page }) => { + // `Tolerant Parsing`: `true` + const tolerantSwitch = page.getByRole("switch", { + exact: true, + name: "Tolerant Parsing", + }); + await tolerantSwitch.click(); + await expect(tolerantSwitch).toHaveAttribute( + "aria-checked", + "true", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a CSS sample that requires tolerant parsing into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("a{"); + + // Verify that the AST structure matches expectations for tolerant CSS. + await page + .getByRole("region", { name: "StyleSheet" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Rule" }), + ).toBeVisible(); + }); }); test.describe("Language: HTML", () => { From c8260504b6e4ac1f31b1997eb0e689b1ea679798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 18:51:07 +0900 Subject: [PATCH 11/15] wip --- .../code-editing-and-ast-interaction.test.ts | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index 7693d893..cafa5435 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -144,6 +144,183 @@ test.describe("AST node expansion", () => { .getByRole("option", { exact: true, name: "JavaScript" }) .click(); }); + + test("SourceType: Module", async ({ page }) => { + // `Source Type`: `Module` + const sourceTypeSelect = page.getByRole("combobox", { + exact: true, + name: "Source Type", + }); + await sourceTypeSelect.click(); + await page + .getByRole("option", { exact: true, name: "Module" }) + .click(); + await expect(sourceTypeSelect).toHaveText("Module"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a JavaScript module sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill('import x from "x";'); + + // Verify that the AST structure matches expectations for module source type. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. ImportDeclaration" }), + ).toBeVisible(); + }); + + test("SourceType: CommonJS", async ({ page }) => { + // `Source Type`: `CommonJS` + const sourceTypeSelect = page.getByRole("combobox", { + exact: true, + name: "Source Type", + }); + await sourceTypeSelect.click(); + await page + .getByRole("option", { exact: true, name: "CommonJS" }) + .click(); + await expect(sourceTypeSelect).toHaveText("CommonJS"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a CommonJS sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("return;"); + + // Verify that the AST structure matches expectations for CommonJS source type. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. ReturnStatement" }), + ).toBeVisible(); + }); + + test("SourceType: Script", async ({ page }) => { + // `Source Type`: `Script` + const sourceTypeSelect = page.getByRole("combobox", { + exact: true, + name: "Source Type", + }); + await sourceTypeSelect.click(); + await page + .getByRole("option", { exact: true, name: "Script" }) + .click(); + await expect(sourceTypeSelect).toHaveText("Script"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a script sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("with (x) {}"); + + // Verify that the AST structure matches expectations for script source type. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. WithStatement" }), + ).toBeVisible(); + }); + + test("ECMAScript Version: 2026", async ({ page }) => { + // `ECMAScript Version`: `2026` + const esVersionSelect = page.getByRole("combobox", { + exact: true, + name: "ECMAScript Version", + }); + await esVersionSelect.click(); + await page + .getByRole("option", { exact: true, name: "2026" }) + .click(); + await expect(esVersionSelect).toHaveText("2026"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a JavaScript sample with using syntax into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("using x = y;"); + + // Verify that the AST structure matches expectations for using syntax. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page + .getByRole("button", { name: "0. VariableDeclaration" }) + .click(); + + await expect( + page + .getByRole("region", { name: "0. VariableDeclaration" }) + .getByRole("listitem") + .filter({ hasText: "kindusing" }), + ).toBeVisible(); + }); + + test("JSX: true", async ({ page }) => { + // `JSX`: `true` + const jsxSwitch = page.getByRole("switch", { + exact: true, + name: "JSX", + }); + await expect(jsxSwitch).toHaveAttribute("aria-checked", "true"); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill a JSX sample into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill(";"); + + // Verify that the AST structure matches expectations for JSX. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page + .getByRole("button", { name: "0. ExpressionStatement" }) + .click(); + + await expect( + page + .getByRole("region", { name: "0. ExpressionStatement" }) + .getByRole("listitem") + .filter({ + hasText: "expressionJSXElement{type, start, end, ...}", + }), + ).toBeVisible(); + }); }); test.describe("Language: JSON", () => { From b2805eb3d2bbdef2176feba8d04dc6d48b6bf4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 18:51:42 +0900 Subject: [PATCH 12/15] wip --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1258e7e3..20993dda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,6 @@ jobs: name: E2E Tests (Playwright) timeout-minutes: 10 # run only if triggered by push on a branch or by a PR event for a PR which is not a draft - if: ${{ !github.event.pull_request || github.event.pull_request.draft == false }} runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 From eb054e9ee6e8ab651b8dc99b2fe3c0e973694db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 19:09:49 +0900 Subject: [PATCH 13/15] wip --- .../code-editing-and-ast-interaction.test.ts | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index cafa5435..dd3f9b8b 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -1093,5 +1093,264 @@ test.describe("AST node expansion", () => { .getByRole("option", { exact: true, name: "HTML" }) .click(); }); + + test("Template Engine Syntax: Handlebars", async ({ page }) => { + // `Template Engine Syntax`: `Handlebars` + const templateEngineSyntaxSelect = page.getByRole("combobox", { + exact: true, + name: "Template Engine Syntax", + }); + await templateEngineSyntaxSelect.click(); + await page + .getByRole("option", { exact: true, name: "Handlebars" }) + .click(); + await expect(templateEngineSyntaxSelect).toHaveText("Handlebars"); + + // `Front Matter`: `false` + const frontMatterSwitch = page.getByRole("switch", { + exact: true, + name: "Front Matter", + }); + await expect(frontMatterSwitch).toHaveAttribute( + "aria-checked", + "false", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill an HTML sample with Handlebars syntax into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("

{{x}}

"); + + // Verify that the AST structure matches expectations for Handlebars syntax. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Document" }).click(); + + await page + .getByRole("region", { name: "0. Document" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Tag" }).click(); + + await page + .getByRole("region", { name: "0. Tag" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Text" }).click(); + + await page + .getByRole("region", { name: "0. Text" }) + .getByRole("listitem") + .filter({ hasText: "partsArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Template" }), + ).toBeVisible(); + }); + + test("Template Engine Syntax: Twig", async ({ page }) => { + // `Template Engine Syntax`: `Twig` + const templateEngineSyntaxSelect = page.getByRole("combobox", { + exact: true, + name: "Template Engine Syntax", + }); + await templateEngineSyntaxSelect.click(); + await page + .getByRole("option", { exact: true, name: "Twig" }) + .click(); + await expect(templateEngineSyntaxSelect).toHaveText("Twig"); + + // `Front Matter`: `false` + const frontMatterSwitch = page.getByRole("switch", { + exact: true, + name: "Front Matter", + }); + await expect(frontMatterSwitch).toHaveAttribute( + "aria-checked", + "false", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill an HTML sample with Twig syntax into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("

{%x%}

"); + + // Verify that the AST structure matches expectations for Twig syntax. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Document" }).click(); + + await page + .getByRole("region", { name: "0. Document" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Tag" }).click(); + + await page + .getByRole("region", { name: "0. Tag" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Text" }).click(); + + await page + .getByRole("region", { name: "0. Text" }) + .getByRole("listitem") + .filter({ hasText: "partsArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Template" }), + ).toBeVisible(); + }); + + test("Template Engine Syntax: ERB", async ({ page }) => { + // `Template Engine Syntax`: `ERB` + const templateEngineSyntaxSelect = page.getByRole("combobox", { + exact: true, + name: "Template Engine Syntax", + }); + await templateEngineSyntaxSelect.click(); + await page + .getByRole("option", { exact: true, name: "ERB" }) + .click(); + await expect(templateEngineSyntaxSelect).toHaveText("ERB"); + + // `Front Matter`: `false` + const frontMatterSwitch = page.getByRole("switch", { + exact: true, + name: "Front Matter", + }); + await expect(frontMatterSwitch).toHaveAttribute( + "aria-checked", + "false", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill an HTML sample with ERB syntax into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("

<%x%>

"); + + // Verify that the AST structure matches expectations for ERB syntax. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Document" }).click(); + + await page + .getByRole("region", { name: "0. Document" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Tag" }).click(); + + await page + .getByRole("region", { name: "0. Tag" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Text" }).click(); + + await page + .getByRole("region", { name: "0. Text" }) + .getByRole("listitem") + .filter({ hasText: "partsArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Template" }), + ).toBeVisible(); + }); + + test("Front Matter: true", async ({ page }) => { + // `Template Engine Syntax`: `None` + const templateEngineSyntaxSelect = page.getByRole("combobox", { + exact: true, + name: "Template Engine Syntax", + }); + await expect(templateEngineSyntaxSelect).toHaveText("None"); + + // `Front Matter`: `true` + const frontMatterSwitch = page.getByRole("switch", { + exact: true, + name: "Front Matter", + }); + await frontMatterSwitch.click(); + await expect(frontMatterSwitch).toHaveAttribute( + "aria-checked", + "true", + ); + + // Hide the settings menu to ensure it doesn't interfere with the test. + await page.keyboard.press("Escape"); + + // Fill an HTML sample with front matter into the editor. + await page + .getByRole("textbox", { exact: true, name: "Code Editor" }) + .fill("---\ntitle: Test\n---\n

x

"); + + // Verify that the AST structure matches expectations for HTML front matter. + await page + .getByRole("region", { name: "Program" }) + .getByRole("listitem") + .filter({ hasText: "bodyArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await page.getByRole("button", { name: "0. Document" }).click(); + + await page + .getByRole("region", { name: "0. Document" }) + .getByRole("listitem") + .filter({ hasText: "childrenArray[1 element]" }) + .getByRole("button", { name: "Toggle Property" }) + .click(); + + await expect( + page.getByRole("button", { name: "0. Tag" }), + ).toBeVisible(); + }); }); }); From 6d053991304d932ffd921f8cc5dcf3075a04ef08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A3=A8=EB=B0=80LuMir?= Date: Thu, 28 May 2026 20:06:58 +0900 Subject: [PATCH 14/15] wip --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20993dda..1258e7e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,7 @@ jobs: name: E2E Tests (Playwright) timeout-minutes: 10 # run only if triggered by push on a branch or by a PR event for a PR which is not a draft + if: ${{ !github.event.pull_request || github.event.pull_request.draft == false }} runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 From 0cb8efdcfa2d764d09a57d95187d848fc9552e15 Mon Sep 17 00:00:00 2001 From: LuMir Date: Wed, 15 Jul 2026 22:45:46 +0900 Subject: [PATCH 15/15] wip --- .../code-editing-and-ast-interaction.test.ts | 423 ++++-------------- 1 file changed, 82 insertions(+), 341 deletions(-) diff --git a/e2e-tests/code-editing-and-ast-interaction.test.ts b/e2e-tests/code-editing-and-ast-interaction.test.ts index dd3f9b8b..d23a4c85 100644 --- a/e2e-tests/code-editing-and-ast-interaction.test.ts +++ b/e2e-tests/code-editing-and-ast-interaction.test.ts @@ -6,7 +6,7 @@ // Imports //----------------------------------------------------------------------------- -import { expect, test } from "@playwright/test"; +import { expect, test, type Page } from "@playwright/test"; //----------------------------------------------------------------------------- // Type Definitions @@ -23,6 +23,40 @@ declare global { } } +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +/** + * Sets and verifies a combobox option in the language options popup. + * @param page The current Playwright page. + * @param name The accessible name of the option control. + * @param value The displayed option value. + */ +async function setComboboxOption(page: Page, name: string, value: string) { + const optionSelect = page.getByRole("combobox", { exact: true, name }); + await optionSelect.click(); + await page.getByRole("option", { exact: true, name: value }).click(); + await expect(optionSelect).toHaveText(value); +} + +/** + * Sets and verifies a switch option in the language options popup. + * @param page The current Playwright page. + * @param name The accessible name of the option control. + * @param value The desired switch state. + */ +async function setSwitchOption(page: Page, name: string, value: boolean) { + const optionSwitch = page.getByRole("switch", { exact: true, name }); + const expectedValue = String(value); + + if ((await optionSwitch.getAttribute("aria-checked")) !== expectedValue) { + await optionSwitch.click(); + } + + await expect(optionSwitch).toHaveAttribute("aria-checked", expectedValue); +} + //----------------------------------------------------------------------------- // Tests //----------------------------------------------------------------------------- @@ -137,25 +171,12 @@ test.describe("AST node expansion", () => { test.describe("Language: JavaScript", () => { test.beforeEach(async ({ page }) => { // `Language`: select `JavaScript` - await page - .getByRole("combobox", { exact: true, name: "Language" }) - .click(); - await page - .getByRole("option", { exact: true, name: "JavaScript" }) - .click(); + await setComboboxOption(page, "Language", "JavaScript"); }); test("SourceType: Module", async ({ page }) => { // `Source Type`: `Module` - const sourceTypeSelect = page.getByRole("combobox", { - exact: true, - name: "Source Type", - }); - await sourceTypeSelect.click(); - await page - .getByRole("option", { exact: true, name: "Module" }) - .click(); - await expect(sourceTypeSelect).toHaveText("Module"); + await setComboboxOption(page, "Source Type", "Module"); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -180,15 +201,7 @@ test.describe("AST node expansion", () => { test("SourceType: CommonJS", async ({ page }) => { // `Source Type`: `CommonJS` - const sourceTypeSelect = page.getByRole("combobox", { - exact: true, - name: "Source Type", - }); - await sourceTypeSelect.click(); - await page - .getByRole("option", { exact: true, name: "CommonJS" }) - .click(); - await expect(sourceTypeSelect).toHaveText("CommonJS"); + await setComboboxOption(page, "Source Type", "CommonJS"); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -213,15 +226,7 @@ test.describe("AST node expansion", () => { test("SourceType: Script", async ({ page }) => { // `Source Type`: `Script` - const sourceTypeSelect = page.getByRole("combobox", { - exact: true, - name: "Source Type", - }); - await sourceTypeSelect.click(); - await page - .getByRole("option", { exact: true, name: "Script" }) - .click(); - await expect(sourceTypeSelect).toHaveText("Script"); + await setComboboxOption(page, "Source Type", "Script"); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -246,15 +251,7 @@ test.describe("AST node expansion", () => { test("ECMAScript Version: 2026", async ({ page }) => { // `ECMAScript Version`: `2026` - const esVersionSelect = page.getByRole("combobox", { - exact: true, - name: "ECMAScript Version", - }); - await esVersionSelect.click(); - await page - .getByRole("option", { exact: true, name: "2026" }) - .click(); - await expect(esVersionSelect).toHaveText("2026"); + await setComboboxOption(page, "ECMAScript Version", "2026"); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -286,11 +283,7 @@ test.describe("AST node expansion", () => { test("JSX: true", async ({ page }) => { // `JSX`: `true` - const jsxSwitch = page.getByRole("switch", { - exact: true, - name: "JSX", - }); - await expect(jsxSwitch).toHaveAttribute("aria-checked", "true"); + await setSwitchOption(page, "JSX", true); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -326,25 +319,12 @@ test.describe("AST node expansion", () => { test.describe("Language: JSON", () => { test.beforeEach(async ({ page }) => { // `Language`: select `JSON` - await page - .getByRole("combobox", { exact: true, name: "Language" }) - .click(); - await page - .getByRole("option", { exact: true, name: "JSON" }) - .click(); + await setComboboxOption(page, "Language", "JSON"); }); test("Mode: JSON", async ({ page }) => { // `Mode`: `JSON` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "json" }) - .click(); - await expect(modeSelect).toHaveText("json"); + await setComboboxOption(page, "Mode", "json"); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -399,25 +379,10 @@ test.describe("AST node expansion", () => { test("Mode: JSONC", async ({ page }) => { // `Mode`: `JSONC` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "jsonc" }) - .click(); - await expect(modeSelect).toHaveText("jsonc"); + await setComboboxOption(page, "Mode", "jsonc"); // `Allow Trailing Commas`: `false` - const allowTrailingCommasSwitch = page.getByRole("switch", { - exact: true, - name: "Allow Trailing Commas", - }); - await expect(allowTrailingCommasSwitch).toHaveAttribute( - "aria-checked", - "false", - ); + await setSwitchOption(page, "Allow Trailing Commas", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -475,15 +440,7 @@ test.describe("AST node expansion", () => { test("Mode: JSON5", async ({ page }) => { // `Mode`: `JSON5` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "json5" }) - .click(); - await expect(modeSelect).toHaveText("json5"); + await setComboboxOption(page, "Mode", "json5"); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -541,26 +498,10 @@ test.describe("AST node expansion", () => { test("Allow Trailing Commas: true", async ({ page }) => { // `Mode`: `JSONC` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "jsonc" }) - .click(); - await expect(modeSelect).toHaveText("jsonc"); + await setComboboxOption(page, "Mode", "jsonc"); // `Allow Trailing Commas`: `true` - const allowTrailingCommasSwitch = page.getByRole("switch", { - exact: true, - name: "Allow Trailing Commas", - }); - await allowTrailingCommasSwitch.click(); - await expect(allowTrailingCommasSwitch).toHaveAttribute( - "aria-checked", - "true", - ); + await setSwitchOption(page, "Allow Trailing Commas", true); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -623,43 +564,18 @@ test.describe("AST node expansion", () => { test.describe("Language: Markdown", () => { test.beforeEach(async ({ page }) => { // `Language`: select `Markdown` - await page - .getByRole("combobox", { exact: true, name: "Language" }) - .click(); - await page - .getByRole("option", { exact: true, name: "Markdown" }) - .click(); + await setComboboxOption(page, "Language", "Markdown"); }); test("Mode: CommonMark", async ({ page }) => { // `Mode`: `CommonMark` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "CommonMark" }) - .click(); - await expect(modeSelect).toHaveText("CommonMark"); + await setComboboxOption(page, "Mode", "CommonMark"); // `Front Matter`: `Off` - const frontMatterSelect = page.getByRole("combobox", { - exact: true, - name: "Front Matter", - }); - await frontMatterSelect.click(); - await page - .getByRole("option", { exact: true, name: "Off" }) - .click(); - await expect(frontMatterSelect).toHaveText("Off"); + await setComboboxOption(page, "Front Matter", "Off"); // `Math`: `false` - const mathSwitch = page.getByRole("switch", { - exact: true, - name: "Math", - }); - await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + await setSwitchOption(page, "Math", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -702,33 +618,13 @@ test.describe("AST node expansion", () => { test("Mode: GFM", async ({ page }) => { // `Mode`: `GFM` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "GitHub-Flavored" }) - .click(); - await expect(modeSelect).toHaveText("GitHub-Flavored"); + await setComboboxOption(page, "Mode", "GitHub-Flavored"); // `Front Matter`: `Off` - const frontMatterSelect = page.getByRole("combobox", { - exact: true, - name: "Front Matter", - }); - await frontMatterSelect.click(); - await page - .getByRole("option", { exact: true, name: "Off" }) - .click(); - await expect(frontMatterSelect).toHaveText("Off"); + await setComboboxOption(page, "Front Matter", "Off"); // `Math`: `false` - const mathSwitch = page.getByRole("switch", { - exact: true, - name: "Math", - }); - await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + await setSwitchOption(page, "Math", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -771,33 +667,13 @@ test.describe("AST node expansion", () => { test("Frontmatter: YAML", async ({ page }) => { // `Mode`: `CommonMark` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "CommonMark" }) - .click(); - await expect(modeSelect).toHaveText("CommonMark"); + await setComboboxOption(page, "Mode", "CommonMark"); // `Front Matter`: `YAML` - const frontMatterSelect = page.getByRole("combobox", { - exact: true, - name: "Front Matter", - }); - await frontMatterSelect.click(); - await page - .getByRole("option", { exact: true, name: "YAML" }) - .click(); - await expect(frontMatterSelect).toHaveText("YAML"); + await setComboboxOption(page, "Front Matter", "YAML"); // `Math`: `false` - const mathSwitch = page.getByRole("switch", { - exact: true, - name: "Math", - }); - await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + await setSwitchOption(page, "Math", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -825,33 +701,13 @@ test.describe("AST node expansion", () => { test("Frontmatter: TOML", async ({ page }) => { // `Mode`: `CommonMark` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "CommonMark" }) - .click(); - await expect(modeSelect).toHaveText("CommonMark"); + await setComboboxOption(page, "Mode", "CommonMark"); // `Front Matter`: `TOML` - const frontMatterSelect = page.getByRole("combobox", { - exact: true, - name: "Front Matter", - }); - await frontMatterSelect.click(); - await page - .getByRole("option", { exact: true, name: "TOML" }) - .click(); - await expect(frontMatterSelect).toHaveText("TOML"); + await setComboboxOption(page, "Front Matter", "TOML"); // `Math`: `false` - const mathSwitch = page.getByRole("switch", { - exact: true, - name: "Math", - }); - await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + await setSwitchOption(page, "Math", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -879,33 +735,13 @@ test.describe("AST node expansion", () => { test("Frontmatter: JSON", async ({ page }) => { // `Mode`: `CommonMark` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "CommonMark" }) - .click(); - await expect(modeSelect).toHaveText("CommonMark"); + await setComboboxOption(page, "Mode", "CommonMark"); // `Front Matter`: `JSON` - const frontMatterSelect = page.getByRole("combobox", { - exact: true, - name: "Front Matter", - }); - await frontMatterSelect.click(); - await page - .getByRole("option", { exact: true, name: "JSON" }) - .click(); - await expect(frontMatterSelect).toHaveText("JSON"); + await setComboboxOption(page, "Front Matter", "JSON"); // `Math`: `false` - const mathSwitch = page.getByRole("switch", { - exact: true, - name: "Math", - }); - await expect(mathSwitch).toHaveAttribute("aria-checked", "false"); + await setSwitchOption(page, "Math", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -933,34 +769,13 @@ test.describe("AST node expansion", () => { test("Math: true", async ({ page }) => { // `Mode`: `CommonMark` - const modeSelect = page.getByRole("combobox", { - exact: true, - name: "Mode", - }); - await modeSelect.click(); - await page - .getByRole("option", { exact: true, name: "CommonMark" }) - .click(); - await expect(modeSelect).toHaveText("CommonMark"); + await setComboboxOption(page, "Mode", "CommonMark"); // `Front Matter`: `Off` - const frontMatterSelect = page.getByRole("combobox", { - exact: true, - name: "Front Matter", - }); - await frontMatterSelect.click(); - await page - .getByRole("option", { exact: true, name: "Off" }) - .click(); - await expect(frontMatterSelect).toHaveText("Off"); + await setComboboxOption(page, "Front Matter", "Off"); // `Math`: `true` - const mathSwitch = page.getByRole("switch", { - exact: true, - name: "Math", - }); - await mathSwitch.click(); - await expect(mathSwitch).toHaveAttribute("aria-checked", "true"); + await setSwitchOption(page, "Math", true); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -1009,24 +824,12 @@ test.describe("AST node expansion", () => { test.describe("Language: CSS", () => { test.beforeEach(async ({ page }) => { // `Language`: select `CSS` - await page - .getByRole("combobox", { exact: true, name: "Language" }) - .click(); - await page - .getByRole("option", { exact: true, name: "CSS" }) - .click(); + await setComboboxOption(page, "Language", "CSS"); }); test("Tolerant Parsing: false", async ({ page }) => { // `Tolerant Parsing`: `false` - const tolerantSwitch = page.getByRole("switch", { - exact: true, - name: "Tolerant Parsing", - }); - await expect(tolerantSwitch).toHaveAttribute( - "aria-checked", - "false", - ); + await setSwitchOption(page, "Tolerant Parsing", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -1051,15 +854,7 @@ test.describe("AST node expansion", () => { test("Tolerant Parsing: true", async ({ page }) => { // `Tolerant Parsing`: `true` - const tolerantSwitch = page.getByRole("switch", { - exact: true, - name: "Tolerant Parsing", - }); - await tolerantSwitch.click(); - await expect(tolerantSwitch).toHaveAttribute( - "aria-checked", - "true", - ); + await setSwitchOption(page, "Tolerant Parsing", true); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -1086,35 +881,19 @@ test.describe("AST node expansion", () => { test.describe("Language: HTML", () => { test.beforeEach(async ({ page }) => { // `Language`: select `HTML` - await page - .getByRole("combobox", { exact: true, name: "Language" }) - .click(); - await page - .getByRole("option", { exact: true, name: "HTML" }) - .click(); + await setComboboxOption(page, "Language", "HTML"); }); test("Template Engine Syntax: Handlebars", async ({ page }) => { // `Template Engine Syntax`: `Handlebars` - const templateEngineSyntaxSelect = page.getByRole("combobox", { - exact: true, - name: "Template Engine Syntax", - }); - await templateEngineSyntaxSelect.click(); - await page - .getByRole("option", { exact: true, name: "Handlebars" }) - .click(); - await expect(templateEngineSyntaxSelect).toHaveText("Handlebars"); + await setComboboxOption( + page, + "Template Engine Syntax", + "Handlebars", + ); // `Front Matter`: `false` - const frontMatterSwitch = page.getByRole("switch", { - exact: true, - name: "Front Matter", - }); - await expect(frontMatterSwitch).toHaveAttribute( - "aria-checked", - "false", - ); + await setSwitchOption(page, "Front Matter", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -1166,25 +945,10 @@ test.describe("AST node expansion", () => { test("Template Engine Syntax: Twig", async ({ page }) => { // `Template Engine Syntax`: `Twig` - const templateEngineSyntaxSelect = page.getByRole("combobox", { - exact: true, - name: "Template Engine Syntax", - }); - await templateEngineSyntaxSelect.click(); - await page - .getByRole("option", { exact: true, name: "Twig" }) - .click(); - await expect(templateEngineSyntaxSelect).toHaveText("Twig"); + await setComboboxOption(page, "Template Engine Syntax", "Twig"); // `Front Matter`: `false` - const frontMatterSwitch = page.getByRole("switch", { - exact: true, - name: "Front Matter", - }); - await expect(frontMatterSwitch).toHaveAttribute( - "aria-checked", - "false", - ); + await setSwitchOption(page, "Front Matter", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -1236,25 +1000,10 @@ test.describe("AST node expansion", () => { test("Template Engine Syntax: ERB", async ({ page }) => { // `Template Engine Syntax`: `ERB` - const templateEngineSyntaxSelect = page.getByRole("combobox", { - exact: true, - name: "Template Engine Syntax", - }); - await templateEngineSyntaxSelect.click(); - await page - .getByRole("option", { exact: true, name: "ERB" }) - .click(); - await expect(templateEngineSyntaxSelect).toHaveText("ERB"); + await setComboboxOption(page, "Template Engine Syntax", "ERB"); // `Front Matter`: `false` - const frontMatterSwitch = page.getByRole("switch", { - exact: true, - name: "Front Matter", - }); - await expect(frontMatterSwitch).toHaveAttribute( - "aria-checked", - "false", - ); + await setSwitchOption(page, "Front Matter", false); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape"); @@ -1313,15 +1062,7 @@ test.describe("AST node expansion", () => { await expect(templateEngineSyntaxSelect).toHaveText("None"); // `Front Matter`: `true` - const frontMatterSwitch = page.getByRole("switch", { - exact: true, - name: "Front Matter", - }); - await frontMatterSwitch.click(); - await expect(frontMatterSwitch).toHaveAttribute( - "aria-checked", - "true", - ); + await setSwitchOption(page, "Front Matter", true); // Hide the settings menu to ensure it doesn't interfere with the test. await page.keyboard.press("Escape");