Skip to content

test: setup E2E testing via Playwright and implement some tests #88

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -48,3 +48,33 @@ jobs:
- name: Check formatting of everything (prettier)
run: |
npm run fmt:check

e2e-test:
name: E2E Tests (Playwright)
timeout-minutes: 60
# 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@v4

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: npm

- name: Install dependencies
run: |
npm ci

- name: Run Playwright tests
run: |
npm run test:e2e

- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -24,3 +24,9 @@ npm-debug.log*

# typescript
*.tsbuildinfo

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ To install and set up the project, follow these steps:
1. Ensure you have Node.js v20 installed. You can download it from the [official Node.js website](https://nodejs.org/).
2. Clone the repository to your local machine.
3. Install the project dependencies using npm - `npm install`.
4. Setup Docker to run E2E tests via Playwright ([Docker Desktop](https://www.docker.com/products/docker-desktop/) or [Docker Engine](https://docs.docker.com/engine/install/)).

This will install all the necessary packages and dependencies required to run the project.

@@ -28,6 +29,7 @@ Open [http://localhost:5173](http://localhost:5173) with your browser to see the

- `npm run start`: Starts the development server.
- `npm run build`: Builds the app for production.
- `npm run test:e2e:update`: Runs all End-to-End tests and updates the screenshots/snapshots.

## Configuration

59 changes: 59 additions & 0 deletions e2e-tests/code-editing-and-ast-interaction.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Tests for code editing functionality and AST tool interaction.
*/
import test, { expect } from "@playwright/test";

/**
* This test verifies that:
* - Users can edit code in the editor
* - The AST updates in response to code changes
* - ESQuery selectors correctly highlight matching code and AST nodes
* - AST node expansion functionality works properly
*/
test(`should change code, then highlight code and AST nodes matching ESQuery selector`, async ({
page,
}) => {
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("Control+KeyA");
await page.keyboard.press("Backspace");

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

// add an ESQuery selector
await page.getByRole("textbox", { name: "ESQuery Selector" }).click();
await page.keyboard.type("CallExpression");

// wait for the debounced update of the AST to happen
await expect(
page
.getByRole("listitem")
.filter({ hasText: "end" })
.filter({ hasText: "29" }),
).toBeVisible();

// expand AST nodes for ExpressionStatement and CallExpression
await page
.getByRole("region", { name: "Program" })
.getByRole("listitem")
.filter({ hasText: "Array" })
.getByRole("button", { name: "Toggle Property" })
.click();
await page.getByRole("button", { name: "ExpressionStatement" }).click();
await page
.getByRole("region", { name: "ExpressionStatement" })
.getByLabel("Toggle Property")
.click();

// screenshot
await expect(page).toHaveScreenshot();
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions e2e-tests/light-dark-theme.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Tests for theme switching functionality.
*/

import { test, expect } from "@playwright/test";

/**
* This test verifies that:
* - The application shows light theme by default
* - Users can toggle between light and dark themes
* - Theme changes are visually reflected in the UI
*/
test("should show light theme by default and switch to dark theme", async ({
page,
}) => {
await page.goto("/");

await expect(page).toHaveScreenshot("light-theme.png");

await page.getByRole("button", { name: "Toggle theme" }).click();
await page.getByRole("menuitem", { name: "Dark" }).click();

await expect(page).toHaveScreenshot("dark-theme.png");
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Unable to render rich display

Invalid image source.

33 changes: 33 additions & 0 deletions e2e-tests/options.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Tests for language selection and options panel functionality.
*/
import { test, expect } from "@playwright/test";

/**
* This test verifies that:
* - Users can open the language options popover
* - Users can switch between supported languages (JavaScript, JSON, Markdown, CSS)
* - For each language the entire page is correctly rendered
*/
test("should switch language and show options for each", async ({ page }) => {
await page.goto("/");

await page.getByRole("button", { name: "Language Options" }).click();

await expect(page).toHaveScreenshot("page-javascript.png");

await page.getByRole("combobox", { name: "Language" }).click();
await page.getByRole("option", { name: "JSON JSON" }).click();

await expect(page).toHaveScreenshot("page-json.png");

await page.getByRole("combobox", { name: "Language" }).click();
await page.getByRole("option", { name: "Markdown Markdown" }).click();

await expect(page).toHaveScreenshot("page-markdown.png");

await page.getByRole("combobox", { name: "Language" }).click();
await page.getByRole("option", { name: "CSS CSS" }).click();

await expect(page).toHaveScreenshot("page-css.png");
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions e2e-tests/tools.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Tests for the Code Analysis Tools Panel.
*/
import { test, expect } from "@playwright/test";

/**
* This test verifies that:
* - Users can switch between different code analysis tools (AST, Scope, Code Path)
* - Each tool displays correctly
* - Tool-specific interactions work as expected (e.g. scope selection)
*/
test("should switch to each tool and show it", async ({ page }) => {
await page.goto("/");

await expect(
page.getByRole("region", { name: "Code Analysis Tools Panel" }),
).toHaveScreenshot("tools-ast.png");

await page.getByRole("button", { name: "Scope" }).click();
await page.getByRole("button", { name: "global" }).click();
// move mouse away to avoid accordion hover state
await page.mouse.move(0, 0);

await expect(
page.getByRole("region", { name: "Code Analysis Tools Panel" }),
).toHaveScreenshot("tools-scope.png");

await page.getByRole("button", { name: "Code Path" }).click();

await expect(
page.getByRole("region", { name: "Code Analysis Tools Panel" }),
).toHaveScreenshot("tools-code-path.png");
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 84 additions & 0 deletions package-lock.json

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

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -18,7 +18,9 @@
"lint:js": "eslint --max-warnings 0 .",
"lint:fix:js": "eslint --max-warnings 0 . --fix",
"fmt": "prettier --write .",
"fmt:check": "prettier --check ."
"fmt:check": "prettier --check .",
"test:e2e": "docker pull mcr.microsoft.com/playwright:v1.51.1-noble && cross-env PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:36719/ playwright test",
"test:e2e:update": "npm run test:e2e -- --update-snapshots"
},
"lint-staged": {
"**/*.{ts,tsx,jsx,js}": [
@@ -79,13 +81,15 @@
"zustand": "^4.5.4"
},
"devDependencies": {
"@playwright/test": "1.51.1",
"@types/eslint-scope": "^3.7.7",
"@types/espree": "^10.0.0",
"@types/esquery": "^1.5.4",
"@types/node": "^18.19.44",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.4.1",
"cross-env": "^7.0.3",
"eslint": "^9.25.1",
"got": "^14.4.3",
"lint-staged": "^15.2.9",
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.