name: cypress-to-scout-migration description: > 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.
Cypress to Scout Migration
Overview
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.
Required sub-skills
- REQUIRED: scout-create-scaffold (generate Scout directory structure)
- REQUIRED: scout-ui-testing (page objects, browser auth, parallel UI)
- REQUIRED: scout-api-testing (apiClient/auth, apiServices patterns)
- REQUIRED: scout-best-practices-reviewer (review migrated tests)
Core principle
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.
Tools
- Scaffold a spec file:
bash scripts/scaffold_scout_spec.sh --name <name> --domain <path> --plugin-test-dir <path> [--type parallel|sequential] - Check selector validity:
bash scripts/extract_selectors.sh <cypress-test-file> --app-src <path-to-plugin-source>
All paths relative to this skill's directory.
Step 0: Load solution-specific skill [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.
Phase 1: Triage (before touching any code) [medium freedom]
For each Cypress test, pass all five gates before migrating.
Gate 0: Is the feature still valid?
- Check if the feature under test still exists in the codebase
- Run
scripts/extract_selectors.sh <test-file>to verify selectors still exist - Check if the feature was removed, redesigned, or moved behind a feature flag
| 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 |
Gate 1: Is it already covered?
Search for existing coverage in:
- Scout tests in
test/scout/ - API integration tests
- Unit tests co-located with source code
- Other Cypress tests covering same behavior
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.
Gate 2: Is it at the right layer?
| 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.
Gate 3: Does it add value?
Delete without migrating if the test:
- Only verifies a page loads without errors
- Only checks that a button or element exists
- Tests trivial behavior already covered by type safety
- Has been skipped for 3+ months with no progress
- Tests deprecated or soon-to-be-removed functionality
Gate 4: Flakiness risk assessment
Two checks — current status and source code risk scan.
4a: Current status
If the test is currently skipped (.skip, @skipInServerless, etc.) or chronically flaky:
- Determine if flakiness is a test problem or an app bug
- App bug → fix the app first, then write the Scout test
- Cypress-specific issue (timing, selectors) → migrate with proper patterns
If the solution has a flaky-test-doctor skill, use it for deeper root cause analysis.
4b: Source code risk scan
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.
PR strategy
One Cypress spec file = one PR. Each migrated spec file must be submitted as its own pull request:
- Keeps reviews focused and manageable
- Isolates risk — a problem in one migration doesn't block others
- Makes it easy to revert a single migration if issues surface
Every PR must pass the Flaky Test Runner before merging. Run the new Scout test through the flaky test suite runner to verify stability.
Phase 2: Migration
Tests that pass all triage gates proceed here. Do not port Cypress code 1:1. Rewrite using Scout patterns.
Step 1: Determine test type [high freedom]
- UI test if it verifies user flows, page rendering, or interactive behavior
- API test if the Cypress test primarily validates data via API assertions
Step 2: Set up Scout scaffold [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).
Step 3: Map Cypress patterns to Scout [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 |
Step 3b: Data cleanup audit [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:
- Saved objects (rules, cases, dashboards, saved queries)
- ES indices or documents
- Fleet agents, policies, integrations
- User preferences, UI settings, localStorage state
- API keys or credentials
Add explicit cleanup in the Scout test (
afterAll/afterEach)Add defensive cleanup in
beforeAll— handles leftover data from a previous failed runVerify cleanup works — run the test twice in a row locally. Second run fails → cleanup is incomplete.
Step 4: Write the Scout test [high freedom]
Read the scout-ui-testing or scout-api-testing skill for implementation details.
Key rules:
- Tags are required — Scout validates UI test tags at runtime
- One suite per file — single top-level
test.describe()orspaceTest.describe() - UI actions in page objects, assertions in specs
- API-based setup/teardown via
apiServices/kbnClient - Use
test.step()for multi-step flows to reuse browser context - Parallelize when possible — use
spaceTest+scoutSpacefor worker-isolated spaces - Test fixture for per-test isolated setup; Worker fixture for shared setup
- Page objects encapsulate selectors and actions; assertions stay in specs
- EUI wrappers — use Scout's
EuiComboBoxWrapper,EuiDataGridWrapper, etc. - All created data must be cleaned up — see Step 3b
Step 5: Review and verify [low freedom — mandatory checklist]
- Run the
scout-best-practices-reviewerskill against the new test - Make sure the test fails — intentionally break the feature and confirm the test catches it
- Run locally:
node scripts/scout.js run-tests --stateful --testFiles <path> - Update manifests:
node scripts/scout.js update-test-config-manifests - Open a PR with only this spec file's migration (one spec per PR)
- Run the Flaky Test Runner — do not merge until stable
Phase 3: Cleanup
After the Scout test is verified:
- Delete the Cypress test file
- Remove orphaned code (tasks, screens, objects only used by the deleted test)
- Check for orphaned imports (grep to verify no other usages)
- Remove from all relevant config/index files
- Update manifests:
node scripts/scout.js update-test-config-manifests
Common mistakes
- Trusting the Cypress test's cleanup — Scout shares the env, so you must add cleanup
- Porting Cypress code line-by-line instead of rewriting with Scout patterns
- Migrating tests that belong at API/unit layer
- Forgetting triage gates and migrating flaky/invalid/duplicate tests
- Skipping the Gate 4b risk scan — Cypress patterns that seem harmless produce flaky Scout tests
- Using
page.waitForTimeout()— forbidden, same ascy.wait(ms) - Using
page.waitForLoadState('networkidle')— anti-pattern, actively removed from Scout tests; wait for specific elements instead - Using short custom timeouts on
waitFor()(e.g., 3s) — causes CI flakiness; use the default (10s) - Adding explicit waits before
clear(),fill(),click()— these auto-wait; the extra wait is redundant - Specifying
{ state: 'visible' }onwaitFor()— it's the default, omit it - Missing Scout tags (validated at runtime)
- Using
esArchiverfor system indices (usekbnClient) - Not parallelizing tests that could run in parallel
- Putting assertions in page objects (keep in specs)
- Skipping the "make sure your test fails" verification
- Batching multiple spec migrations into a single PR
- Merging without running the Flaky Test Runner
- Using
fill()on Kibana query bars —QueryStringInputsubmits React props, not DOM value; usepressSequentially()(see best practices) - Asserting
.euiTableRowcount as 0 —EuiBasicTablealways renders an empty-state row; assert on the message text instead (see best practices)
Phase 4: Skill improvement
After every migration, review what you learned and suggest updating this skill if any of these apply:
- New Kibana/EUI component interaction pattern — a component required a non-obvious Playwright approach (e.g.,
pressSequentiallyfor query bars,dispatchEventfor unstable popovers, CSS:has()for tooltip anchors) - New flakiness pattern — a Cypress pattern caused flakiness in Scout that isn't already in
references/flakiness-risk-patterns.md - New API service or page object — reusable infrastructure warrants documenting in the solution-specific skill
- New role or auth method — a convenience login method was added (e.g.,
loginAsT1Analyst) - Lint rule workaround — a Playwright lint rule required a non-obvious alternative (e.g.,
dispatchEventinstead offorce: true,toContainText([...])instead ofnth()) - Ownership or architecture insight — learned where UI code lives, which plugin owns what, or how data flows
Prompt the user: "During this migration I learned [X]. Want me to add it to the skill so future migrations benefit?"
References
Open only what you need:
- Key differences between Cypress and Scout (auth, CI, MKI, tags, execution model):
references/cypress-vs-scout-differences.md - Best practices for writing Scout tests (fixtures, page objects, parallelism):
references/migration-best-practices.md - Flakiness risk patterns to scan for during Gate 4b (hard-coded waits, missing cleanup, force clicks, etc.):
references/flakiness-risk-patterns.md - Complete before/after migration example with annotated decisions (Timeline creation):
references/example-migration.md
Solution-specific extensions (Step 0)
Load the matching skill when migrating tests from that solution:
- Security Solution (tests in
x-pack/solutions/security/):x-pack/solutions/security/plugins/security_solution/.agents/skills/cypress-to-scout-migration/SKILL.md