Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 7 additions & 24 deletions e2e-tests/code-editing-and-ast-interaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Tests for code editing functionality and AST tool interaction.
*/
import { expect, test } from "@playwright/test";
import { getCodeEditor, replaceCodeEditorValue } from "./helpers/code-editor";

type HighlightSamplesState = {
intervalId: number;
Expand All @@ -26,19 +27,7 @@ test(`should change code, then highlight code and AST nodes matching ESQuery sel
}) => {
await page.goto("/");

// focus code editor textbox
await page
.getByRole("region", { name: "Code Editor Panel" })
.getByRole("textbox")
.nth(1)
.click();

// delete the default code
await page.keyboard.press("ControlOrMeta+KeyA");
await page.keyboard.press("Backspace");

// add new code
await page.keyboard.type("console.log('Hello, World!');");
await replaceCodeEditorValue(page, "console.log('Hello, World!');");

// add an ESQuery selector
await page.getByRole("textbox", { name: "ESQuery Selector" }).click();
Expand Down Expand Up @@ -73,22 +62,16 @@ test(`should keep ESQuery highlights aligned while typing before a matching lite
}) => {
await page.goto("/");

const codeEditor = page
.getByRole("region", { name: "Code Editor Panel" })
.getByRole("textbox")
.nth(1);
const highlight = page.locator(".bg-editorHighlightedRangeColor");
const codeEditor = getCodeEditor(page);
const highlight = codeEditor.locator(".bg-editorHighlightedRangeColor");

await codeEditor.click();
await codeEditor.press("ControlOrMeta+KeyA");
await codeEditor.press("Backspace");
await codeEditor.pressSequentially("42;");
await replaceCodeEditorValue(page, "42;");

await page
.getByRole("textbox", { name: "ESQuery Selector" })
.fill("Literal");

await expect(highlight).toHaveText("42");
await expect(highlight).toHaveText(["42"]);

await codeEditor.click();
await codeEditor.press("Home");
Expand Down Expand Up @@ -125,5 +108,5 @@ test(`should keep ESQuery highlights aligned while typing before a matching lite
expect(
highlightSamples.every(highlightText => highlightText === "42"),
).toBe(true);
await expect(highlight).toHaveText("42");
await expect(highlight).toHaveText(["42"]);
});
14 changes: 14 additions & 0 deletions e2e-tests/helpers/code-editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Page } from "@playwright/test";

export function getCodeEditor(page: Page) {
return page
.getByRole("region", { name: "Code Editor Panel" })
.getByRole("textbox")
.last();
Comment on lines +6 to +7
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCodeEditor() currently selects the last textbox within the "Code Editor Panel" region. Since the panel already contains multiple textboxes (e.g. the ESQuery selector) and could gain more over time, this selector is brittle and may start pointing at the wrong element. Prefer selecting the editor by its accessible name (the CodeMirror component sets aria-label="Code Editor"), e.g. query the textbox with name "Code Editor" (optionally still scoped to the region).

Suggested change
.getByRole("textbox")
.last();
.getByRole("textbox", { name: "Code Editor" });

Copilot uses AI. Check for mistakes.
}

export async function replaceCodeEditorValue(page: Page, value: string) {
const codeEditor = getCodeEditor(page);

await codeEditor.fill(value);
}
25 changes: 7 additions & 18 deletions e2e-tests/persistence.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect, test, type Page } from "@playwright/test";
import { getCodeEditor, replaceCodeEditorValue } from "./helpers/code-editor";

const storageKey = "eslint-explorer";

Expand Down Expand Up @@ -35,18 +36,6 @@ async function getStoredHashValue(page: Page): Promise<string> {
}, storageKey);
}

async function replaceEditorValue(page: Page, value: string) {
const codeEditor = page
.getByRole("region", { name: "Code Editor Panel" })
.getByRole("textbox")
.nth(1);

await codeEditor.click();
await codeEditor.press("ControlOrMeta+KeyA");
await codeEditor.press("Backspace");
await codeEditor.pressSequentially(value);
}

test("should persist unicode code safely in the URL hash", async ({ page }) => {
await page.addInitScript(key => {
window.localStorage.removeItem(key);
Expand All @@ -55,7 +44,7 @@ test("should persist unicode code safely in the URL hash", async ({ page }) => {

const unicodeCode = 'const \u03C0 = "\u{1F600}";';

await replaceEditorValue(page, unicodeCode);
await replaceCodeEditorValue(page, unicodeCode);

await expect.poll(() => getPersistedJavaScriptCode(page)).toBe(unicodeCode);
await expect.poll(() => getStoredHashValue(page)).toContain("v2.");
Expand All @@ -67,7 +56,7 @@ test("should persist unicode code safely in the URL hash", async ({ page }) => {
}, storageKey);
await page.goto(`/${persistedHash}`);

await expect(page.locator(".cm-content")).toContainText(unicodeCode);
await expect(getCodeEditor(page)).toContainText(unicodeCode);
});

test("should still load state from legacy hash links", async ({ page }) => {
Expand All @@ -78,7 +67,7 @@ test("should still load state from legacy hash links", async ({ page }) => {

const legacyCode = "console.log('legacy hash');";

await replaceEditorValue(page, legacyCode);
await replaceCodeEditorValue(page, legacyCode);

await expect.poll(() => getPersistedJavaScriptCode(page)).toBe(legacyCode);

Expand All @@ -98,7 +87,7 @@ test("should still load state from legacy hash links", async ({ page }) => {
}, storageKey);
await page.goto(`/#${legacyHash}`);

await expect(page.locator(".cm-content")).toContainText(legacyCode);
await expect(getCodeEditor(page)).toContainText(legacyCode);
});

test("should fall back to localStorage when a v2 hash is malformed", async ({
Expand All @@ -111,7 +100,7 @@ test("should fall back to localStorage when a v2 hash is malformed", async ({

const fallbackCode = "console.log('localStorage fallback');";

await replaceEditorValue(page, fallbackCode);
await replaceCodeEditorValue(page, fallbackCode);

await expect
.poll(() => getPersistedJavaScriptCode(page))
Expand Down Expand Up @@ -145,5 +134,5 @@ test("should fall back to localStorage when a v2 hash is malformed", async ({
);
await page.goto(`/#${malformedHash}`);

await expect(page.locator(".cm-content")).toContainText(fallbackCode);
await expect(getCodeEditor(page)).toContainText(fallbackCode);
});
24 changes: 12 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"devDependencies": {
"@babel/core": "^7.29.0",
"@eslint/core": "^0.17.0",
"@playwright/test": "^1.58.2",
"@playwright/test": "^1.59.1",
"@rolldown/plugin-babel": "^0.2.2",
"@types/eslint-scope": "^8.4.0",
"@types/espree": "^10.1.0",
Expand Down
Loading