Playwright E2E Tests
onyx-dot-app/onyx
Write and maintain Playwright end-to-end tests for the Onyx application.
Procedure for choosing stable selectors when building Page Object Models for the Opik E2E suite by exploring the live UI with the Playwright MCP.
$ npx skills add comet-ml/opik --skill playwright-pom-discovery -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install comet-ml/opik playwright-pom-discovery --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/comet-ml/opik.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/playwright-pom-discovery .claude/skills/playwright-pom-discovery && 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 "playwright-pom-discovery" agent skill from https://github.com/comet-ml/opik/tree/main/.agents/skills/playwright-pom-discovery into .claude/skills/playwright-pom-discovery/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-pom-discovery", 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/comet-ml/opik/tree/main/.agents/skills/playwright-pom-discoveryType 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 comet-ml/opik --skill playwright-pom-discovery -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install comet-ml/opik playwright-pom-discovery --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/comet-ml/opik.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/playwright-pom-discovery .agents/skills/playwright-pom-discovery && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "playwright-pom-discovery" agent skill from https://github.com/comet-ml/opik/tree/main/.agents/skills/playwright-pom-discovery into .agents/skills/playwright-pom-discovery/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-pom-discovery", 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 comet-ml/opik --skill playwright-pom-discovery -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install comet-ml/opik playwright-pom-discovery --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/comet-ml/opik.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/playwright-pom-discovery .cursor/skills/playwright-pom-discovery && 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 "playwright-pom-discovery" agent skill from https://github.com/comet-ml/opik/tree/main/.agents/skills/playwright-pom-discovery into .cursor/skills/playwright-pom-discovery/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-pom-discovery", 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/comet-ml/opik.git --path .agents/skills/playwright-pom-discovery--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 comet-ml/opik --skill playwright-pom-discovery -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install comet-ml/opik playwright-pom-discovery --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/comet-ml/opik.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/playwright-pom-discovery .gemini/skills/playwright-pom-discovery && 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 "playwright-pom-discovery" agent skill from https://github.com/comet-ml/opik/tree/main/.agents/skills/playwright-pom-discovery into .gemini/skills/playwright-pom-discovery/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-pom-discovery", 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 comet-ml/opik playwright-pom-discoveryInstalls 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 comet-ml/opik --skill playwright-pom-discovery -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/comet-ml/opik.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/playwright-pom-discovery .github/skills/playwright-pom-discovery && 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 "playwright-pom-discovery" agent skill from https://github.com/comet-ml/opik/tree/main/.agents/skills/playwright-pom-discovery into .github/skills/playwright-pom-discovery/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-pom-discovery", 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 comet-ml/opik --skill playwright-pom-discovery -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install comet-ml/opik playwright-pom-discovery --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/comet-ml/opik.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/playwright-pom-discovery .opencode/skills/playwright-pom-discovery && 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 "playwright-pom-discovery" agent skill from https://github.com/comet-ml/opik/tree/main/.agents/skills/playwright-pom-discovery into .opencode/skills/playwright-pom-discovery/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playwright-pom-discovery", 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.
playwright-pom-discoveryProcedure for choosing stable selectors when building Page Object Models for the Opik E2E suite by exploring the live UI with the Playwright MCP.
The skill applies when you touch anything under tests_end_to_end/e2e/pom/, write a data-testid or getByRole locator against the live Opik UI, or add a method for an element you have not seen. It does not apply to pure fixture work or to matcher and type-only changes. The agent starts by naming the target page, the entities it needs, such as a project with at least one trace for a logs page, and the auth state, since exploring an empty-state page yields a POM that only ever sees the empty state.
State is seeded through the SDK or a bridge and never by clicking through the UI, which would test the UI with itself and be slower and flakier. The agent then explores the running page with the Playwright MCP using an accessibility snapshot and a data-testid listing, picks the most stable locator for each element and verifies it before committing. It is the discovery step used by the writing-e2e-tests skill.
10 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 8e3f6e5. 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:
curlFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use curl, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
OPIK_API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Playwright POM Discovery loads about 4.4k tokens when it runs. Until then it costs about 123 tokens; SKILL.md has 1,910 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 comet-ml/opik at commit 8e3f6e5, republished under its Apache-2.0 licence (© comet-ml). 1,910 words, ~4,412 tokens.
.claude/skills/playwright-pom-discovery/SKILL.md (or your agent's skills folder).This skill is the how of choosing selectors for a POM in the Opik E2E suite. You already know which POM you're building; this skill tells you how to figure out what's on the page, what selectors are stable, and how to verify your method actually works before checking it in.
Announce at start: "I'm using the playwright-pom-discovery skill to build the X page object."
tests_end_to_end/e2e/pom/.data-testid selector, a getByRole, or any other Playwright locator targeting the live Opik UI.It does NOT apply to:
digraph pom_discovery {
rankdir=TB;
"Identify target page + entity preconditions" [shape=box];
"Seed required state (project, dataset, trace, etc.)?" [shape=diamond];
"Create state via SDK / bridge before opening UI" [shape=box];
"Open page via browser_navigate (with auth)" [shape=box];
"browser_snapshot to get accessibility tree" [shape=box];
"browser_evaluate to enumerate data-testids" [shape=box];
"Pick selector preference (testid > role > label > css)" [shape=box];
"Write POM method" [shape=box];
"Verify via browser_generate_locator and test_run" [shape=box];
"Method works?" [shape=diamond];
"Selector unstable or missing?" [shape=diamond];
"Flag for FE data-testid addition" [shape=box];
"Commit POM method" [shape=box];
"Identify target page + entity preconditions" -> "Seed required state (project, dataset, trace, etc.)?";
"Seed required state (project, dataset, trace, etc.)?" -> "Create state via SDK / bridge before opening UI" [label="yes"];
"Create state via SDK / bridge before opening UI" -> "Open page via browser_navigate (with auth)";
"Seed required state (project, dataset, trace, etc.)?" -> "Open page via browser_navigate (with auth)" [label="no — stateless page"];
"Open page via browser_navigate (with auth)" -> "browser_snapshot to get accessibility tree";
"browser_snapshot to get accessibility tree" -> "browser_evaluate to enumerate data-testids";
"browser_evaluate to enumerate data-testids" -> "Pick selector preference (testid > role > label > css)";
"Pick selector preference (testid > role > label > css)" -> "Write POM method";
"Write POM method" -> "Verify via browser_generate_locator and test_run";
"Verify via browser_generate_locator and test_run" -> "Method works?";
"Method works?" -> "Commit POM method" [label="yes"];
"Method works?" -> "Selector unstable or missing?" [label="no"];
"Selector unstable or missing?" -> "Flag for FE data-testid addition" [label="yes"];
"Selector unstable or missing?" -> "browser_snapshot to get accessibility tree" [label="no, try different selector"];
"Flag for FE data-testid addition" -> "Pick selector preference (testid > role > label > css)";
}Before opening the browser, answer in writing:
LogsPage models /<workspace>/projects/<projectId>/logs. Get the route from the FE source under apps/opik-frontend/src/v2/pages/ — each page has a directory matching its name.LogsPage — needs a project AND at least one trace under it. An empty project shows only the empty state.DatasetItemsPage — needs a dataset AND at least one item. Empty datasets show only the "Create item" CTA.TestSuitesPage — needs at least one test suite to show row interactions.OnlineEvaluationPage — works empty, but creating a rule shows the rule list.If the page genuinely is stateless (e.g., an empty list page), skip seeding and go straight to step 3. If it's not, seed first — exploring an empty-state UI will lead you to write a POM that only ever sees the empty state.
Never click-create state through the UI just to populate the page you're exploring. Two reasons:
Instead, use the bridge or the TS SDK to seed before opening the browser. For the discovery phase, a one-off seed script is fine — you don't need to wire it into the test suite yet (the test's fixture handles that later).
Example seed for LogsPage discovery:
// scratch script, not committed
import { Opik } from 'opik';
const opik = new Opik({
apiKey: process.env.OPIK_API_KEY,
workspaceName: process.env.OPIK_WORKSPACE,
apiUrl: process.env.OPIK_BASE_URL + '/api',
});
// project + a few traces with structure variety
const projectName = `discovery-logs-${Date.now()}`;
await opik.api.projects.createProject({ name: projectName });
import { track } from 'opik';
const decoratedFn = track({ name: 'discovery-trace', projectName }, async (input: string) => {
return `output for ${input}`;
});
await decoratedFn('hello');
await decoratedFn('world');
await opik.flush();
console.log(`Discovery project: ${projectName}`);Run it locally with staging or local-dev creds, note the project name, then navigate the UI to that project's logs page in the next step. Tear it down by hand at the end of the discovery session (backendClient.deleteProject if it's wired up, or curl otherwise) — discovery state should never accumulate.
Reusable seed patterns by page family:
| Page being built | Seed via | Why |
|---|---|---|
LogsPage, TracePanelPage | opik.track decorator + at least one call | Empty projects show only the empty state |
DatasetsPage | opik.api.datasets.createDataset({...}) for ~3 datasets with different shapes | List interactions need multiple rows |
DatasetItemsPage | opik.Dataset(name).insert([...]) with 3+ items | Item-table interactions need data |
TestSuitesPage, TestSuitePage | opik.TestSuite(...) with items + evaluator + run | Suite states (running/completed/failed) need a real run |
ExperimentsPage | experiment.evaluate(...) against a dataset | Experiment rows need a completed run |
OnlineEvaluationPage | (works empty for rule list); seed a rule via UI once during discovery | Rule list shows after at least one rule exists |
AnnotationQueuesPage, AnnotationQueuePage | opik.TracesAnnotationQueue(...) with 3+ traces | Reviewer flow needs items to score |
Common gotchas:
opik.flush(), the trace may take 1–3 seconds to appear in the Logs page. If you snapshot too fast, you'll see the empty state. Wait or refresh.Auth setup once per discovery session:
The browser MCP needs an authenticated page. Against local OSS (http://localhost:5173, workspace default) there's no login wall — navigate straight to the page. Against an authenticated deployment, reuse the suite's storage state: global-setup mints .auth/user.json the first time you run a Playwright test, and the browser MCP can load it as its storageState. Don't script a full login flow during discovery — it's a distraction.
Arm the dialog handler before your first navigation (below). Several Opik pages guard against data loss with a native beforeunload "Leave site?" confirm — it fires the moment you navigate (or reload) with unsaved state: a staged draft on the Dataset Items page (useNavigationBlocker), a dirty form, an open editor. A native dialog is not in the accessibility snapshot, so browser_navigate just silently blocks on it until it times out — you won't see why. Do this before the navigation step below, not after. Guard against it two ways, both cheap:
Register an auto-accept handler at the start of the session, before your first navigation:
mcp__Playwright__browser_handle_dialog(accept=true)This dismisses any beforeunload/confirm that appears so navigation never hangs. (If one has already blocked you, call the same tool to clear it, then carry on.)
Leave the page clean. Before navigating away, clear unsaved state through the UI the way a user would — commit or discard the draft, close the editor, reset the form. This is also what the POM method itself must do, so doing it in discovery validates that path. Don't rely on the auto-handler alone: a discarded draft leaves the page in a real, testable state; a force-dismissed dialog leaves stale draft state behind.
Standard discovery navigation:
mcp__Playwright__browser_navigate(url="http://localhost:5173/...")Get the exact route from the FE source — apps/opik-frontend/src/v2/router.tsx for the route table, or the page directory under apps/opik-frontend/src/v2/pages/<PageName>/. Most data pages are project-scoped, e.g. /{workspace}/projects/{projectId}/datasets/ or /{workspace}/projects/{projectId}/logs. Confirm against the router rather than guessing — routes change.
mcp__Playwright__browser_snapshot()This returns the structured accessibility tree, not pixels. Each interactive element has:
button, textbox, link, combobox, row, etc.)aria-label)ref ID you can use with browser_click(ref="...") to interactWhy this is the right primitive: Playwright's getByRole(...) selectors map 1:1 to what the snapshot shows. If you see button "Create suite" in the snapshot, you can confidently write page.getByRole('button', { name: 'Create suite' }).
Read the snapshot before writing any selector. Don't guess. Don't grep the FE source for what you think the button is called — the rendered DOM is the only source of truth that matters.
data-testidsThe snapshot tells you what's interactive but not what's been explicitly marked stable by the FE team. For that:
mcp__Playwright__browser_evaluate(function="""() => {
return Array.from(document.querySelectorAll('[data-testid]'))
.map(e => ({
testid: e.getAttribute('data-testid'),
tag: e.tagName.toLowerCase(),
text: (e.textContent || '').slice(0, 60).trim(),
visible: e.offsetParent !== null,
}))
.filter(e => e.visible);
}""")This returns every test id currently rendered on the page, with enough context to know what each one is. Test ids are the preferred selector — they're the FE team's contract for "this won't change." Use them when they exist.
In priority order:
data-testid — most stable. page.getByTestId('create-suite-button'). If a test id exists, use it.getByRole(name) — stable across most refactors as long as the accessible name doesn't change. page.getByRole('button', { name: 'Create suite' }).getByLabel for form inputs that have a label. page.getByLabel('Dataset name').getByText — fragile if the text is dynamic or i18n'd. Use only for truly static labels.// no test id; FE team to add — link to ticket.The decision is binary at write time, not runtime. Pick one selector and commit to it — write deterministic selectors, not runtime-healing ones.
browser_generate_locator when you're unsureIf the accessibility tree shows three buttons with similar names, or the element has both a test id and a role and you're not sure which is canonical:
mcp__Playwright__browser_snapshot() # to find the ref of the element
mcp__playwright-test__browser_generate_locator(ref="<the-ref>")This returns the locator code Playwright itself would generate if you used codegen against this element. Trust that output — Playwright's locator selection logic is well-tuned for resilience.
If you don't have access to mcp__playwright-test__ tools, fall back to writing the selector manually based on the snapshot + test id list, then verify in step 8.
After writing the POM method, the verification loop is:
// scratch test, not committed
import { test, expect } from '@playwright/test';
import { LogsPage } from '../pom/logs.page';
test('discovery: LogsPage.filterByProject works', async ({ page }) => {
await page.goto('http://localhost:5173/<workspace>/projects/<seed-project-id>/logs');
const logs = new LogsPage(page);
await logs.filterByProject('discovery-logs-...');
expect(await logs.countTraces()).toBeGreaterThan(0);
});Run it through:
mcp__playwright-test__test_run(testPath="path/to/scratch.spec.ts")If it passes, the POM method works against the live page. If it fails, read the failure trace (Playwright's artifacts), don't just adjust selectors blindly. Common failures:
If after all of the above the only working selector is a CSS path like .MuiTable-root > tbody > tr:nth-child(2), stop and flag it. The missing-data-testid protocol:
data-testid to the FE component in the same change as your POM. Find the component under apps/opik-frontend/src/v2/pages/<Page>/... or its shared-component dependency, add data-testid="<descriptive-name>", then use that in the POM.getByRole with explicit accessible name (survives most refactors).data-testid should be added.The data-testid naming convention is kebab-case, descriptive, scoped to the page/component: dataset-items-table, create-suite-button, trace-row-{traceId}. Avoid generic names like submit-button that conflict across pages.
Once the POM method works against the live UI:
data-testid additions go in the same change (cross-package is fine; reviewers expect it for test-enablement work).backendClient.deleteProject(...) or curl -X DELETE ...).These are red flags that mean you skipped a step:
| Symptom | What you skipped |
|---|---|
| "Let me look at the FE source to find the right selector" | Step 4 — snapshot the rendered DOM, not the source. Components compose; what renders is what matters. |
| "I'll write the POM method and check if it works when the test runs" | Step 8 — verify in isolation before committing. Iterating inside a full run is slower. |
| "The page is empty, so I'll just check the empty state" | Step 2 — seed real data. An empty-state-only POM never exercises the row template, the open-detail action, etc. |
"I'll use page.locator('.button:nth-child(3)') — it's fine" | Step 9 — flag missing testids and add them to the FE. Brittle selectors are the #1 source of E2E flake. |
"The test id I see is generic, like button-1 — I'll use that" | Step 5 + 9 — generic test ids are nearly as bad as no test id. Rename it to something descriptive in the same change. |
| "I'll add the POM but skip the seed because the test will create state" | Step 2 — even during the test, you're now writing untested POM code against a page state you've never seen. |
"browser_navigate is hanging / timing out for no reason" | Step 3 — a native "Leave site?" dialog is blocking (unsaved draft/form). It's not in the snapshot. Arm browser_handle_dialog(accept=true) up front, and discard the draft in-app before leaving. |
This is the discovery sub-step of writing an E2E test. The writing-e2e-tests skill invokes it once it has scoped the test and analyzed the feature: you seed the page's state, explore the live UI, and come back with the selectors each POM method will use plus any data-testids the FE needs. The test and POM get written and run from there.
© comet-ml, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/playwright-pom-discovery of comet-ml/opik.
Open the folder on GitHubat commit 8e3f6e5
Playwright POM Discovery 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 |
|---|---|---|---|---|---|---|
| Playwright POM Discovery this skillcomet-ml/opik | 22k | — | ~4.4k | Automated safety check: Pass | Apache-2.0 | |
| Playwright E2E Testsonyx-dot-app/onyx | 32k | 1 repos | ~2.8k | Automated safety check: Notes | Custom licence | |
| E2E VerificationChorus-AIDLC/Chorus | 1.2k | — | ~1.5k | Automated safety check: Notes | AGPL-3.0 | |
| Playwright Testingchongdashu/vibejam-starter-pack | 149 | — | ~2.2k | Automated safety check: Pass | None | |
| Frontend Playwright E2Eansible/ansible-ui | 113 | — | ~2.5k | Automated safety check: Notes | Apache-2.0 | |
| Dev Webtestclassmethod/tsumiki | 974 | — | ~4.2k | Automated safety check: Pass | MIT |
onyx-dot-app/onyx
Write and maintain Playwright end-to-end tests for the Onyx application.
Chorus-AIDLC/Chorus
A skill your agent uses when manually verifying a Chorus frontend change in a real browser — finding local login credentials, driving the running dev server with the Playwright MCP, logging in…
chongdashu/vibejam-starter-pack
Plan, implement, and debug frontend tests: unit/integration/E2E/visual/a11y.
ansible/ansible-ui
Write, run, and debug Playwright E2E / integration / live tests.
classmethod/tsumiki
This skill should be used when the user asks to "dev-webtest", "Webテスト", "画面の動作確認", "E2Eテスト", "web test", "visual check", "モンキーテスト", "アクセシビリティチェック", "レスポンシブテスト", "フォームテスト".
petrkindlmann/qa-skills
Goal-driven E2E testing where a browser agent (Playwright MCP / computer-use) reads a natural-language goal and explores the app via the accessibility tree to assert outcomes — no pre-written script.
comet-ml/opik
Checklist for wiring a new linter into Opik's Code Quality pipeline: the four files to edit, the silent-failure gotchas and the pass/fail verification loop.
comet-ml/opik
Shows how to add product analytics events to Opik's frontend, Java backend and Python SDK, all reporting through Segment to PostHog with an opik_ name prefix.
comet-ml/opik
Investigates a failed Opik end-to-end test from CI, TestOps or a local run, decides regression versus flake, and proposes a fix without editing tests.
comet-ml/opik
Rules for writing PR descriptions, changelog entries and feature documentation in the Opik repository, including the exact headings that CI requires.
comet-ml/opik
Turns a code change into one committed, passing Playwright end-to-end spec by resolving the change scope and handing authoring to a companion skill.
comet-ml/opik
Starts, rebuilds, and troubleshoots the Opik local dev stack, including an optional Comet Platform integration mode for the Opik team.
Works with
Categories
Procedure for choosing stable selectors when building Page Object Models for the Opik E2E suite by exploring the live UI with the Playwright MCP. The skill applies when you touch anything under tests_end_to_end/e2e/pom/, write a data-testid or getByRole locator against the live Opik UI, or add a method for an element you have not seen. It does not apply to pure fixture work or to matcher and type-only changes.
Playwright POM Discovery fits situations like: adding a page object for a new page in the Opik E2E suite; choosing between data-testid, getByRole and other Playwright locators; verifying a selector against the live UI before committing.
Run `npx skills add comet-ml/opik --skill playwright-pom-discovery -a claude-code`. Or copy the skill folder (.agents/skills/playwright-pom-discovery in comet-ml/opik) into .claude/skills/playwright-pom-discovery in your project. Claude Code loads it when a task matches its description.
Run `npx skills add comet-ml/opik --skill playwright-pom-discovery -a codex`. Or copy the skill folder (.agents/skills/playwright-pom-discovery in comet-ml/opik) into .agents/skills/playwright-pom-discovery 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 comet-ml/opik --skill playwright-pom-discovery -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/playwright-pom-discovery, .gemini/skills/playwright-pom-discovery, .github/skills/playwright-pom-discovery and .opencode/skills/playwright-pom-discovery in your project.
Going by SKILL.md and its folder, Playwright POM Discovery needs the command-line tools its instructions call (curl) and credentials named OPIK_API_KEY. Our summary lists: The Playwright MCP server; A running Opik instance with an authenticated workspace; The Opik SDK or bridge for seeding test state.
SKILL.md contains no URLs. Its commands use curl, which can reach the network depending on how they are called. 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.
Playwright POM Discovery is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.4k tokens (SKILL.md is roughly 18k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Playwright POM Discovery: Playwright E2E Tests (onyx-dot-app/onyx, 32k stars), E2E Verification (Chorus-AIDLC/Chorus, 1.2k stars), Playwright Testing (chongdashu/vibejam-starter-pack, 149 stars) and Frontend Playwright E2E (ansible/ansible-ui, 113 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
comet-ml (a GitHub organization) maintains it in comet-ml/opik, which has 22,443 GitHub stars. The repository holds 19 skills in this directory. The repository was last updated on October 8, 2026.
Source: comet-ml/opik on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.