Web Application Testing
anthropics/skills
Tests local web applications with Python Playwright scripts, checking frontend behavior, capturing screenshots and reading browser console logs.
Write and run web E2E tests (Playwright) using TDD — locations, patterns, commands, and debugging.
$ npx skills add kdlbs/kandev --skill e2e -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install kdlbs/kandev e2e --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/kdlbs/kandev.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/e2e .claude/skills/e2e && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "e2e" agent skill from https://github.com/kdlbs/kandev/tree/main/.agents/skills/e2e into .claude/skills/e2e/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "e2e", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/kdlbs/kandev/tree/main/.agents/skills/e2eType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add kdlbs/kandev --skill e2e -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install kdlbs/kandev e2e --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kdlbs/kandev.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/e2e .agents/skills/e2e && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "e2e" agent skill from https://github.com/kdlbs/kandev/tree/main/.agents/skills/e2e into .agents/skills/e2e/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "e2e", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add kdlbs/kandev --skill e2e -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install kdlbs/kandev e2e --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kdlbs/kandev.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/e2e .cursor/skills/e2e && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "e2e" agent skill from https://github.com/kdlbs/kandev/tree/main/.agents/skills/e2e into .cursor/skills/e2e/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "e2e", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/kdlbs/kandev.git --path .agents/skills/e2e--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add kdlbs/kandev --skill e2e -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install kdlbs/kandev e2e --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kdlbs/kandev.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/e2e .gemini/skills/e2e && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "e2e" agent skill from https://github.com/kdlbs/kandev/tree/main/.agents/skills/e2e into .gemini/skills/e2e/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "e2e", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install kdlbs/kandev e2eInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add kdlbs/kandev --skill e2e -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/kdlbs/kandev.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/e2e .github/skills/e2e && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "e2e" agent skill from https://github.com/kdlbs/kandev/tree/main/.agents/skills/e2e into .github/skills/e2e/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "e2e", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add kdlbs/kandev --skill e2e -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install kdlbs/kandev e2e --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/kdlbs/kandev.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/e2e .opencode/skills/e2e && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "e2e" agent skill from https://github.com/kdlbs/kandev/tree/main/.agents/skills/e2e into .opencode/skills/e2e/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "e2e", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
e2eWrite and run web E2E tests (Playwright) using TDD — locations, patterns, commands, and debugging.
E2E is an agent skill from kdlbs/kandev. Write and run web E2E tests (Playwright) using TDD — locations, patterns, commands, and debugging.
Its SKILL.md is about 6.4k tokens, which your agent loads only when the skill is triggered. The skill folder holds 6 other files, including reference files (for example `references/dev-workflow.md`, `references/failure-triage.md` and `references/fixture-state.md`).
It sits in Testing & QA, covering End-to-end testing, Test-driven development and Browser testing. It works with Playwright. The repository describes itself as: AI Kanban & Development Environment. Orchestrate multiple agents, review changes, open PRs. Multi-provider, self-hostable, no telemetry. The licence is AGPL-3.0.
5 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit b734113. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
pnpmmakeplaywrightFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use pnpm, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
E2E loads about 6.4k tokens when it runs, and up to ~14k if it reads all its reference files. Until then it costs about 26 tokens; SKILL.md has 2,906 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from kdlbs/kandev at commit b734113, republished under its AGPL-3.0 licence (© kdlbs). 2,906 words, ~6,435 tokens.
.claude/skills/e2e/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.Write and run E2E coverage directly in the primary conversation. For a cost-controlled feature workflow, the user switches that conversation to the lower-cost implementation/test model before this phase.
Write E2E tests using TDD (Red-Green-Refactor). Always run the tests you create and watch them fail before implementing.
/tdd — Follow the Red-Green-Refactor cycle when writing tests./pr-fixup — Use after the PR opens only for CI or reviewer findings./playwright-cli — Interactive browser automation. Use to validate features against the dev server before writing tests, and to debug failing tests with --debug=cli.Load references/fixture-state.md for capability-readiness, remembered
workflow selection, cross-record correlation, restart persistence, and
provider-backed PR feedback fixture rules.
Load references/ui-state-and-cleanup.md for lifecycle, WebSocket, terminal,
Dockview, and sidebar/context-menu rules.
apps/web/e2e/
apps/web/e2e/
├── fixtures/
│ ├── backend.ts # Worker-scoped backend + frontend process
│ ├── test-base.ts # Extended fixture (apiClient, seedData, testPage)
│ └── office-fixture.ts # Office fixtures (officeApi, officeSeed with workspace+agent)
├── helpers/
│ ├── api-client.ts # HTTP client for seeding data (read for available methods)
│ └── office-api-client.ts # Office-specific API client (onboarding, issues, agents)
├── pages/ # Page objects (read for available pages and methods)
└── tests/ # Spec files (*.spec.ts), grouped by feature
├── task/ # Task creation, deletion, archiving, environment, subtasks
├── kanban/ # Kanban board, mobile kanban, preview panel
├── session/ # Session lifecycle, resume, recovery, multi-session, layout
├── workflow/ # Workflow steps, settings, automation, import/export
├── git/ # Git changes panel, commits, diffs, symlinks
├── pr/ # PR detection, watchers, changes panel
├── terminal/ # Terminal agent, keyboard, settings
├── chat/ # Quick chat, message queue, clarification, markdown, toolbar
├── settings/ # Config management, agent profiles, editor integration
└── review/ # Code review diffsEach worker gets an isolated backend, frontend, database, and mock agent — no Docker, no API keys needed.
Always run headless (make test-e2e). Never use --headed, e2e:headed, or test-e2e-headed — headed mode requires a display and will fail in agent environments.
Fresh worktree bootstrap: Before the first pnpm or E2E command in a new worktree, install the workspace dependencies:
cd apps && pnpm install --frozen-lockfileDo this once before changing into apps/web or running a filtered package
command. Shared .git metadata does not include apps/node_modules.
pnpm e2e:run (managed runner — builds, runs, tears down)e2e/scripts/run-e2e.sh handles the build, the run, and cleanup in one command. Use it instead of stitching the steps together. It auto-selects docker vs host, runs a resource-bounded number of shards concurrently, enforces one Playwright worker per shard and strict WS accounting by default (matching CI), and never leaves root-owned artifacts behind.
cd apps/web
pnpm e2e:run # auto: docker if daemon + CI image available, else host; builds first
pnpm e2e:run tests/task/my-test.spec.ts # single file (extra args pass through to Playwright)
pnpm e2e:run tests/path/spec.ts -- --grep "exact test name" # exact CI failure with a fresh build
pnpm e2e:run --shards 3 # 3 shards concurrently on this machine (isolated)
pnpm e2e:run --no-build -- --grep "task creation" # runner options before --; Playwright options after
pnpm e2e:run --no-build --project mobile-chrome tests/layout/mobile-spa-resilience.spec.ts
pnpm e2e:docker # force the docker CI image (full isolation from a host dev instance)
pnpm e2e:clean # remove build/test artifacts, incl. root-owned ones from prior docker runsSelect the owning Playwright project. The default chromium project
intentionally excludes routing, auth, mobile, and container suites; a matching
path with the wrong project exits with No tests found. Pass the project before
the spec path, for example:
pnpm e2e:run --project auth tests/auth/auth-lifecycle.spec.ts
pnpm e2e:run --project routing tests/office-routing-<name>.spec.ts
pnpm e2e:run --project containers tests/docker/<name>.spec.tsUse mobile-chrome only for mobile-*.spec.ts files. Confirm Playwright discovers the intended test count before treating a focused command as evidence.
e2e:run accepts one --project; repeating it selects only the last value, so run desktop and mobile separately when both are required and confirm discovery for each.
Read resource-safety.md for full-run limits and compact output before managed E2E runs or full local suites.
The runner solves the sharp edges hand-rolling would hit: in docker it builds the CGO backend on the host and runs it in the runtime image (forward-compatible when the host glibc ≤ the image's — the usual case; it smoke-tests this and only falls back to the build image if the host is newer), builds the Vite web assets on the host, runs them through the Go-served SPA, and keeps Playwright output container-local. See apps/web/e2e/README.md → "the managed runner".
--no-build reuses Vite, backend, and packaged fixtures; prefer normal managed builds
after source/base changes. If intentional, rebuild both with make -C apps/backend build
and make -C apps/backend e2e-plugin-package; global setup requires the plugin tarball.
For raw Docker/SSH/container runs, make build-backend does not build the Linux
mock-agent fixture. Prefer managed; otherwise run make build-backend build-backend-remote-helpers build-web. If KANDEV_MOCK_AGENT_LINUX_BINARY is
missing, run make -C apps/backend build-mock-agent-linux before diagnosing code.
make test-e2e # all tests, headless (host)
cd apps && pnpm --filter @kandev/web e2e:raw -- tests/task/my-test.spec.ts # single file
cd apps && pnpm --filter @kandev/web e2e:raw -- --grep "task creation" # workspace-filter syntax; from apps/web use `pnpm e2e:raw --project=mobile-chrome e2e/tests/<area>/<spec>.spec.ts` without a standalone `--`; run `--list` first if forwarding syntax is uncertainFor flake reproduction, load failure-triage.md.
CRITICAL: E2E tests run against the production Vite build served by the Go backend, not dev mode. After any frontend code change, you must rebuild before running tests (pnpm e2e:run does this for you):
make build-web # ~30s, required after every frontend changeWithout this, tests can exercise stale code: after backend changes run make -C apps/backend build before reproducing Playwright failures, and compare the binary timestamp/hash if a fixed test still fails. make test-e2e and pnpm e2e:run handle both builds.
helpers/api-client.ts and pages/ to discover available seed methods and page objects; use data-testid attributes for selectors — add them to components as needed../../fixtures/test-base — provides testPage, apiClient, and seedData (pre-created workspace with default workflow). Pull backend from the fixture too when you need the backend URL — it's worker-scoped, dynamic, and process.env.KANDEV_API_BASE_URL is not set in the Playwright runner. Use backend.baseUrl.apiClient.mockGitHub*() methods to seed mock dataworkspace_id. mockGitHubAssociateTaskPR defaults an omitted ID to the active workspace; pass it explicitly for foreign-workspace cases, and keep production handlers fail-closed for missing or mismatched IDs. Direct-store bridges that inject Git status or inspect Changes must resolve environmentIdBySessionId[sessionId] and use environment-keyed state; sessions can share one environment.For a touch-specific interaction, use Playwright .tap() rather than .click()
so the app receives a touch pointerType. Run focused mobile specs with
pnpm e2e:run --project mobile-chrome e2e/tests/<area>/mobile-<name>.spec.ts.
--project is a runner option and must precede --; the mobile project only
matches mobile-*.spec.ts files, so another filename can produce no tests.
After the interaction settles, assert the resulting state and exercise a later
mouse or pen entry when the UI maintains hybrid-device pointer state.
For keyboard or accessibility regressions, start from the real visible opener
and use real keyboard input (Tab, Enter/Space, and control typeahead when
applicable). Do not focus an inner target programmatically to bypass focus
management; assert that the target is reachable from the opener, especially
for portaled Radix menus.
For a UI change whose contract is a rendered size or alignment relationship, assert that relationship from the intended elements' bounding boxes rather than only asserting visibility. Scope locators to the affected toolbar, dialog, or panel so unrelated controls cannot make the assertion pass.
const metrics = page.getByTestId("task-metrics");
const actions = page.getByTestId("task-actions");
const [metricsBox, actionsBox] = await Promise.all([
metrics.boundingBox(),
actions.boundingBox(),
]);
expect(metricsBox).not.toBeNull();
expect(actionsBox).not.toBeNull();
expect(metricsBox!.height).toBeCloseTo(actionsBox!.height, 1);Run the assertion in the relevant desktop and mobile projects when responsive
layout can change the result. Do not rely on fixed pixels when the product
contract is equality or alignment. For control-size regressions, read the mobile-parity sizing contract.
For computed colors, parse alpha/opacity semantically or assert a deliberate class/data contract; do not compare serialized getComputedStyle strings because browsers may return rgba(), oklab(), or color().
Animation-aware geometry: Before reading dialog or panel geometry, wait only for currently running Web Animations with finite effect.getComputedTiming().iterations; await animation.finished.catch(() => undefined) because Radix overlays can cancel animations during close or replacement. Never blanket-await infinite animations or use a fixed sleep; then read bounding boxes and assert the relationship.
For virtualized viewport-edge assertions, require the first visible row's top to cover the content top. Assert the last visible row's bottom only when the content overflows; allow legitimate trailing space and end padding in short trees. Check adjacent gaps and overlaps, and include blank-top, blank-bottom, and short-tree fixtures. Do not assert the opposite edge after filtering rows to viewport intersection, because that can reject valid virtualization states.
For narrow-width clipping or overlap regressions, visibility and containment
are insufficient: assert a real hit target. Check document.elementFromPoint()
at the control center resolves to the control (or its descendant), then prove
the action remains clickable at the legal minimum width.
apiClient.createTaskWithAgent(...) returns CreateTaskResponse, which is Task & { session_id?: string; agent_execution_id?: string }. Read created.session_id directly — don't call listTaskSessions(taskId) just to fetch the session that was auto-started by the same call./t/:id contains the TASK ID, not the session ID. Backend routes like /port-proxy/:sessionId/:port/*path expect the session ID. Don't extract IDs from window.location.pathname when you need a session ID — pull from the API response.page.request shares cookies/storage with the page context. Fine for the current no-auth local backend; if auth ever lands, this is where you'd plug it in.window.__KANDEV_BOOT_PAYLOAD__ may not issue a browser-visible API request on first paint. Use apiClient to seed or re-query backend state, assert the user-visible outcome, and reserve page.waitForResponse("**/api/v1/...") for client-side fetches that the browser actually performs.dev_script configured, so the preview panel renders a placeholder ("Configure a dev script…") and the URL input never appears — tests that try to drive it hang on the locator timeout. To use the preview iframe in a test, set one first: await apiClient.updateRepository(seedData.repositoryId, { dev_script: "echo dev" }). Then click the Preview dockview tab (await session.clickTab("Preview")) — the toolbar will mount and the URL input becomes targetable.Example:
import { test, expect } from "../../fixtures/test-base";
import { KanbanPage } from "../../pages/kanban-page";
test.describe("my feature", () => {
test("does something", async ({ testPage, seedData, apiClient }) => {
const task = await apiClient.createTask(seedData.workspaceId, "Test Task", "Description");
const kanban = new KanbanPage(testPage);
await kanban.goto(seedData.workspaceId);
await expect(kanban.taskCardByTitle("Test Task")).toBeVisible();
});
});For interactive development and PR captures, load dev-workflow.md. Production-build verification remains required after development.
Tests are grouped by feature area in subdirectories under tests/. When creating a new test:
pr/, a test for session resume goes in session/, etc.test.describe blocks. Don't create a new file for each narrow scenario.../../ (e.g., from "../../fixtures/test-base").*-helpers.ts file whenever they are used by multiple spec files, even when small; keep only scenario-specific setup in specs. Reusable page polling, seeding, and Dockview cleanup belong in the helper module.apps/web/e2e/tests/. Playwright discovery
imports files in that tree, so a Vitest file can fail discovery before any
browser test runs. Put pure Vitest tests beside the component or helper, and
after adding an E2E-adjacent helper test run:
cd apps/web && pnpm exec playwright test --config e2e/playwright.config.ts --project=chromium --project=mobile-chrome --project=containers --list --reporter=json.
Require zero discovery errors before treating the E2E suite as runnable.toContainText or a dedicated locator when labels include metadata such as file sizes.testPage.reload()) and assert the state is still correct. This catches hydration bugs and Go boot-payload/client-store mismatches.toHaveCount(1) (or an equivalent uniqueness assertion) before interaction; never use .first() to hide duplicate renders. For send-flow regressions, pair the visible UI assertion with persisted API/state evidence when watchWs is not the causal contract; an event-observer timeout alone does not prove that the send failed.role="status" such as Cancelling..., assert that status instead of a generic Loading label or spinner. Generic busy indicators can belong to an unrelated request and do not prove that cancellation progressed.test.afterEach. The backend is worker-scoped, and e2eReset does not reset every persisted setting, including system_metrics_display; leaking one can affect later tests in the same worker. Fixtures are lazy: acquire testPage before setting a non-default persisted value in beforeEach, otherwise page initialization can reapply the default and silently undo setup. Verify with the focused test that depends on that setting.e2eReset do not reset every seeded record. A test that creates or PATCHes a
canonical seedData profile, repository, executor, integration/workspace
preset, or other non-user setting must use unique names for shared/global
rows, capture every changed baseline, and restore every mutation in
test.afterEach or finally (not only delete rows created by the test).
Prefer a disposable record when the UI can select it. Verify by running the mutating spec followed by its affected neighbour with --workers=1 --retries=0. Remove temporary or untracked files written into worker-scoped checkouts in finally/afterEach, including assertion-failure paths.locator.evaluate and
page.evaluate callbacks execute in the browser, so they cannot close over
Node/test variables. Pass expected values as the argument instead, for
example locator.evaluate((el, expected) => Math.abs(el.scrollTop - expected), baseline).dwell(page, duration, "negative-assertion", reason) for the observation window. Do not use waitForRequest timeouts or
timeout exceptions as proof that a late request did not occur..last() can match
an earlier turn and is not evidence that the later prompt was accepted.preventDefault() and stopPropagation() before dismissing the inner panel. A bubble-phase window handler runs after Radix can dismiss the outer dialog. Add a regression that asserts the inner panel collapses while the outer dialog remains open.apiClient to set up preconditions quickly, but always verify the result by opening the page and checking the DOM./t/<task-id> instead of clicking a Kanban card; card rendering and virtualization add unrelated failure surface. For virtualized file trees, do not retain source and target locators while rows can recycle. Dispatch dragstart with a page-owned DataTransfer, reveal the target, dispatch drop, and clean up the transfer state before asserting the result.For failed specs, shard artifacts, or suspected contention, load failure-triage.md.
data-testid selectors over text-based locators. Text content can change when UI is updated (e.g., hiding a badge), breaking tests that match by text. Use getByTestId() or locator("[data-testid='...']") for stable targeting. When translated labels intentionally identify multiple routes, scope by stable href or a dedicated test ID rather than role/name alone.instant-open, delayed-open, or open; use [data-slot="tooltip-content"]:not([data-state="closed"]), then scope to the visible portal/popover/container and active ancestor. For routes with multiple surfaces, scope controls to the active container first; portal overlays must be selected by visible overlay or the trigger's aria-controls, not assumed descendants. For portaled pickers, locate the active visible role=listbox or picker container first, then scope getByRole('option') within it. Hidden mounts can make global locators match the wrong instance; do not use .first() to hide duplicates.has locators relative to each candidate. locator(...).filter({ has: ... }) resolves the has locator beneath each candidate, so a container-scoped locator can never match a sibling or ancestor action. Use a relative CSS :has(...) selector or a locator rooted relative to the candidate row.filter({ hasText }) is substring matching; for counts or sibling content, use a stable data-* attribute or getByText(label, { exact: true }) and assert uniqueness. When a label also appears in descriptive prose, target its semantic heading with getByRole('heading', { name: label, exact: true }) and assert uniqueness. Prefer page object methods like clickSessionChatTab() (stable data-testid) over fragile text matches such as sessionTabByText("1").openSidebarMenuAndClick() helper in session-page.ts retries the full open-click sequence on detachment — use this pattern for similar interactions.Follow /tdd when writing E2E tests:
data-testid attributes, run the test until green© kdlbs, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 5 other files (references) in .agents/skills/e2e of kdlbs/kandev.
Open the folder on GitHubat commit b734113
E2E next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| E2E this skillkdlbs/kandev | 909 | — | ~6.4k | Automated safety check: Pass | AGPL-3.0 | |
| Web Application Testinganthropics/skills | 180k | 51 repos | ~966 | Automated safety check: Pass | Apache-2.0 | |
| playwright-cli Browser Automationgithub/gh-aw | 5.4k | 24 repos | ~2.8k | Automated safety check: Pass | MIT | |
| Write and Verify Playwright Testsappsmithorg/appsmith | 41k | — | ~2.9k | Automated safety check: Notes | Apache-2.0 | |
| Cucumber and Playwright E2E Testslanggenius/dify | 158k | — | ~682 | Automated safety check: Pass | Custom licence | |
| E2E Testinglangflow-ai/langflow | 156k | — | ~3.3k | Automated safety check: Pass | MIT |
anthropics/skills
Tests local web applications with Python Playwright scripts, checking frontend behavior, capturing screenshots and reading browser console logs.
github/gh-aw
Drives a real browser from the command line with playwright-cli to open pages, interact, mock requests, save state and work with Playwright tests.
appsmithorg/appsmith
Writes a Playwright end-to-end test from a prompt, runs it against a live Appsmith deployment and retries with fixes up to three times until it passes.
langgenius/dify
Guides changes and reviews of the Cucumber and Playwright end-to-end suite under `e2e/`: feature files, step definitions, support code, tags, locators and assertions.
langflow-ai/langflow
Write and review Playwright E2E tests for Langflow. An agent skill from langflow-ai/langflow.
handsontable/handsontable
Guides writing and changing Playwright end-to-end tests for Handsontable using page objects, data-testid hooks and deterministic waits.
kdlbs/kandev
Generate a single-file HTML walkthrough that explains a PR's purpose, user impact, interface changes, compatibility risks, and implementation.
kdlbs/kandev
Diagnose Kandev bugs, running-instance issues, UI/browser failures, and runtime behavior.
kdlbs/kandev
Improve Kandev's AI harness from session learnings or explicit requests.
kdlbs/kandev
Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, polar chart (polar/radial lollipop), loop/flywheel…
kdlbs/kandev
Implement changes using Test-Driven Development (Red-Green-Refactor).
kdlbs/kandev
Run a broad local verification audit only when the user explicitly requests it or PR/CI remediation requires it.
Works with
Categories
Write and run web E2E tests (Playwright) using TDD — locations, patterns, commands, and debugging. E2E is an agent skill from kdlbs/kandev. Write and run web E2E tests (Playwright) using TDD — locations, patterns, commands, and debugging.
E2E fits situations like: tasks that involve End-to-end testing; tasks that involve Test-driven development; tasks that involve Browser testing.
Run `npx skills add kdlbs/kandev --skill e2e -a claude-code`. Or copy the skill folder (.agents/skills/e2e in kdlbs/kandev) into .claude/skills/e2e in your project. Claude Code loads it when a task matches its description.
Run `npx skills add kdlbs/kandev --skill e2e -a codex`. Or copy the skill folder (.agents/skills/e2e in kdlbs/kandev) into .agents/skills/e2e in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add kdlbs/kandev --skill e2e -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/e2e, .gemini/skills/e2e, .github/skills/e2e and .opencode/skills/e2e in your project.
Going by SKILL.md and its folder, E2E needs the command-line tools its instructions call (pnpm, make and playwright). Our summary lists: Docker.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
E2E is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.4k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 7.1k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with E2E: Web Application Testing (anthropics/skills, 180k stars), playwright-cli Browser Automation (github/gh-aw, 5.4k stars), Write and Verify Playwright Tests (appsmithorg/appsmith, 41k stars) and Cucumber and Playwright E2E Tests (langgenius/dify, 158k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
kdlbs (a GitHub organization) maintains it in kdlbs/kandev, which has 909 GitHub stars. The repository holds 45 skills in this directory. The repository was last updated on October 8, 2026.
Source: kdlbs/kandev on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.