Migrate Kibana Cypress E2E tests (.cy.ts) to Scout (Playwright). Applies to any Kibana plugin or solution. Includes triage gates (duplicate detection, layer analysis, value assessment), Cypress-to-Scout pattern mapping, data cleanup audit, and PR workflow. Use when: (1) migrating a Cypress test to Scout, (2) converting .cy.ts to .spec.ts, (3) planning a Cypress-to-Scout migration batch, (4) rewriting Cypress screens/tasks as Scout page objects, (5) asked "how do I move this Cypress test to Scout/Playwright", (6) asked about differences between Cypress and Scout.
Migrate Cypress tests to Scout by first validating each test through triage gates, then rewriting using Scout patterns. Never migrate Cypress tests directly — validate first, then rewrite following Scout best practices.
Exercise behavior in the least flaky automation layer first: UNIT > API > UI
A Cypress E2E test should only become a Scout E2E test if it genuinely tests a user workflow that cannot be verified at a lower layer.
bash scripts/scaffold_scout_spec.sh --name <name> --domain <path> --plugin-test-dir <path> [--type parallel|sequential]bash scripts/extract_selectors.sh <cypress-test-file> --app-src <path-to-plugin-source>All paths relative to this skill's directory.
[mandatory]Before starting triage or migration, check the References section for a solution-specific extension skill that matches the Cypress test's location. If one exists, read it immediately — it overrides general conventions with solution-specific paths, packages, roles, API services, and templates. All subsequent phases must follow the solution-specific conventions when they conflict with this skill.
[medium freedom]For each Cypress test, pass all five gates before migrating.
scripts/extract_selectors.sh <test-file> to verify selectors still exist| Finding | Action |
|---|---|
| Feature exists unchanged | Continue to Gate 1 |
| Feature redesigned | Write new Scout test from scratch (don't port) |
| Feature removed | Delete Cypress test, no migration needed |
Search for existing coverage in:
test/scout/Don't rely on test names — check what the test actually asserts.
If covered at a lower layer → delete Cypress, no migration needed.
If covered in Scout → delete Cypress. Scout now runs on serverless (MKI), so Cypress tests are no longer needed for serverless (MKI) coverage.
| What the test validates | Right layer |
|---|---|
| Data transformation / API response | API test or unit test |
| Component rendering in isolation | Unit test (RTL) |
| User workflow across pages | Scout UI test |
| Permission-gated UI behavior | Scout UI test (with role-based auth) |
| Visual/cosmetic behavior | Consider deletion or visual regression tool |
If the test belongs at API/unit layer → write coverage there instead.
Delete without migrating if the test:
Two checks — current status and source code risk scan.
If the test is currently skipped (.skip, @skipInServerless, etc.) or chronically flaky:
If the solution has a flaky-test-doctor skill, use it for deeper root cause analysis.
Even if the Cypress test passes reliably today, its source code may contain patterns that would produce a flaky Scout test. Scan the Cypress file and its imported tasks/screens against the pattern catalog in references/flakiness-risk-patterns.md.
| Risk level | Action |
|---|---|
| No risky patterns | Proceed to migration |
| Medium-risk patterns | Proceed — address each pattern during rewrite (note planned remediation) |
| High/critical-risk patterns | Assess effort — may be simpler to write the test from scratch |
| App-level timing issues detected | Fix the app first, then write the Scout test |
For tests with 3+ critical/high-risk patterns, strongly consider writing the Scout test from scratch using the feature spec rather than porting the Cypress logic.
One Cypress spec file = one PR. Each migrated spec file must be submitted as its own pull request:
Every PR must pass the Flaky Test Runner before merging. Run the new Scout test through the flaky test suite runner to verify stability.
Tests that pass all triage gates proceed here. Do not port Cypress code 1:1. Rewrite using Scout patterns.
[high freedom][low freedom — use script]If the plugin doesn't have a Scout test directory yet, read the scout-create-scaffold skill.
Generate the spec file boilerplate:
bash scripts/scaffold_scout_spec.sh --name <spec_name> --domain <domain_path> \
--plugin-test-dir <plugin>/test/scout/ui --type parallel
Use the solution-specific skill's page object and API service templates as starting points (if available).
[medium freedom]| Cypress | Scout |
|---|---|
cy.visit() | page.gotoApp() or page object goto() |
cy.get('[data-test-subj="x"]') | page.testSubj.locator('x') |
cy.intercept() + cy.wait() | Playwright auto-waiting or expect.poll() |
cy.request() (setup/teardown) | apiServices / kbnClient in beforeAll |
cy.wait(ms) | Forbidden — use expect.poll() or locator assertions |
| Screens files (selectors) | Page object class with locators |
| Tasks files (actions) | Page object methods |
{ force: true } | Fix the underlying issue — don't port force clicks (app bugs: use dispatchEvent('click') — see best practices) |
.within() | .locator() chaining (no stale reference issues) |
beforeEach (UI setup) | apiServices in beforeAll (API-based setup) |
@ess / @serverless tags | tags.stateful.<domain>, tags.serverless.<solution>.<tier> |
ftrConfig (serverless tiers) | Scout test tags |
ftrConfig (feature flags) | Kibana Core APIs (MKI/cloud) or custom server config (stateless) |
esArchiver (system indices) | Forbidden — use kbnClient |
[low freedom — must be thorough]Critical: Cypress runs each spec in a clean environment, so many Cypress tests never clean up after themselves. Scout shares the environment across specs — leftover data will break other tests. Do not trust the Cypress test's cleanup.
Read the Cypress test and its tasks/setup — identify every resource created:
Add explicit cleanup in the Scout test (afterAll / afterEach)
Add defensive cleanup in beforeAll — handles leftover data from a previous failed run
Verify cleanup works — run the test twice in a row locally. Second run fails → cleanup is incomplete.
[high freedom]Read the scout-ui-testing or scout-api-testing skill for implementation details.
Key rules:
test.describe() or spaceTest.describe()apiServices / kbnClienttest.step() for multi-step flows to reuse browser contextspaceTest + scoutSpace for worker-isolated spacesEuiComboBoxWrapper, EuiDataGridWrapper, etc.[low freedom — mandatory checklist]scout-best-practices-reviewer skill against the new testnode scripts/scout.js run-tests --stateful --testFiles <path>node scripts/scout.js update-test-config-manifestsAfter the Scout test is verified:
node scripts/scout.js update-test-config-manifestspage.waitForTimeout() — forbidden, same as cy.wait(ms)page.waitForLoadState('networkidle') — anti-pattern, actively removed from Scout tests; wait for specific elements insteadwaitFor() (e.g., 3s) — causes CI flakiness; use the default (10s)clear(), fill(), click() — these auto-wait; the extra wait is redundant{ state: 'visible' } on waitFor() — it's the default, omit itesArchiver for system indices (use kbnClient)fill() on Kibana query bars — QueryStringInput submits React props, not DOM value; use pressSequentially() (see best practices).euiTableRow count as 0 — EuiBasicTable always renders an empty-state row; assert on the message text instead (see best practices)After every migration, review what you learned and suggest updating this skill if any of these apply:
pressSequentially for query bars, dispatchEvent for unstable popovers, CSS :has() for tooltip anchors)references/flakiness-risk-patterns.mdloginAsT1Analyst)dispatchEvent instead of force: true, toContainText([...]) instead of nth())Prompt the user: "During this migration I learned [X]. Want me to add it to the skill so future migrations benefit?"
Open only what you need:
references/cypress-vs-scout-differences.mdreferences/migration-best-practices.mdreferences/flakiness-risk-patterns.mdreferences/example-migration.mdLoad the matching skill when migrating tests from that solution:
x-pack/solutions/security/): x-pack/solutions/security/plugins/security_solution/.agents/skills/cypress-to-scout-migration/SKILL.md