Skip to main content

e2e-test-creation

Create Cucumber BDD end-to-end tests for new features, enhancements, or updates. Use this when asked to write e2e tests, acceptance tests, feature files, step definitions, or Cucumber scenarios for Daedalus.

소스 정보

저장소
input-output-hk/daedalus
최근 소스 활동
2026년 3월 6일 11:04
감지된 SKILL.md 언어
영어
스타
1,247
포크
299

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
e2e-test-creation
description
Create Cucumber BDD end-to-end tests for new features, enhancements, or updates. Use this when asked to write e2e tests, acceptance tests, feature files, step definitions, or Cucumber scenarios for Daedalus.
# E2E Test Creation Skill Create Cucumber BDD end-to-end tests for the Daedalus wallet application. This skill covers writing `.feature` files, step definitions, and helpers following existing project conventions. --- ## Test Framework | Component | Technology | |------------------|--------------------------------------------------------| | BDD Framework | Cucumber.js with Gherkin syntax | | App Driver | Spectron (Electron testing) + WebDriver.io | | Assertions | Chai (`expect`) | | Mocking | Sinon (unit tests only) | | TypeScript | Compiled via `@swc-node/register` | | Test Runner | `yarn test:e2e` / `yarn test:unit` | --- ## Directory Structure Convention Tests are organized by **domain** under `tests/`. Each domain may have `e2e/` and/or `unit/` subtrees: ``` tests/{domain}/ ├── e2e/ │ ├── features/ │ │ └── {feature-name}.feature # Gherkin scenarios │ ├── steps/ │ │ ├── {feature-name}.ts # Step definitions │ │ └── helpers.ts # Domain-specific helpers │ └── documents/ # Test fixtures (optional) └── unit/ ├── features/ │ └── {feature-name}.feature └── steps/ └── {feature-name}.ts ``` ### Existing Domains | Domain | Path | Description | |------------------|-------------------------|-----------------------------------| | `wallets` | `tests/wallets/` | Wallet CRUD, restore, ordering | | `transactions` | `tests/transactions/` | Send, receive, UTxO display | | `delegation` | `tests/delegation/` | Staking, stake pools, rewards | | `navigation` | `tests/navigation/` | Sidebar, tabs, routing | | `settings` | `tests/settings/` | Language, terms of use, preferences | | `news` | `tests/news/` | Newsfeed, alerts, incidents | | `paper-wallets` | `tests/paper-wallets/` | Paper wallet certificates | | `addresses` | `tests/addresses/` | Address management | | `app` | `tests/app/` | App lifecycle, updates, dialogs | | `common` | `tests/common/` | Shared utilities, mnemonics | | `assets` | `tests/assets/` | Native tokens, NFTs | When adding tests for a **new domain**, create the full subtree. When adding tests for an **existing domain**, add files to the existing structure. --- ## Step 1: Write a Feature File ### File Location ``` tests/{domain}/e2e/features/{feature-name}.feature ``` ### Required Tags Every e2e feature file MUST start with a tag line. Use the appropriate tags: | Tag | Meaning | |--------------|--------------------------------------------------| | `@e2e` | **Required** — marks as an end-to-end test | | `@unit` | Marks as a unit test (use for unit/ features) | | `@wip` | Work in progress — skipped in CI | | `@skip` | Permanently skipped | | `@watch` | Run in watch mode during development | | `@noReload` | Skip app reload between scenarios | | `@restartApp`| Restart Electron app after this scenario | | `@reconnectApp` | Reconnect app backend after this scenario | | `@rewardsCsv`| Clean up exported rewards CSV after scenario | ### Feature File Template ```gherkin @e2e Feature: {Human-readable Feature Name} Background: Given I have completed the basic setup Scenario: {Descriptive Scenario Name} Given {precondition} When {user action} Then {expected outcome} ``` ### Gherkin Conventions 1. **Background section**: Use for shared preconditions across scenarios. Almost all e2e tests include `Given I have completed the basic setup` which handles initial app setup (language, terms of use acceptance). 2. **Scenario Outline with Examples**: Use for testing multiple data variations: ```gherkin Scenario Outline: Switching Between Wallet Tabs Given I am on the "Test wallet" wallet "<FROM>" screen When I click the wallet <TO> button Then I should be on the "Test wallet" wallet "<TO>" screen Examples: | FROM | TO | | summary | send | | summary | receive | ``` 3. **Data Tables**: Use for structured test data: ```gherkin Given I have the following wallets: | name | | Test wallet | ``` 4. **Quoted strings**: Use double quotes for dynamic values referenced in step definitions: ```gherkin Given I am on the "Test wallet" wallet "send" screen ``` ### Common Reusable Steps These steps are already defined and available for use in feature files: ```gherkin # Setup Given I have completed the basic setup Given I dont have a language set Given I didn't accept "Terms of use" # Wallet management Given I have the following wallets: | name | | My Wallet | Given I have a "{wallet}" wallet with funds Given I am on the "{wallet}" wallet "{screen}" screen # Navigation When I click the wallet {button} button When I click on the "{category}" category in the sidebar # Sidebar Given the sidebar submenu is hidden|visible When I click on the sidebar toggle button Then the sidebar submenu should be hidden|visible # General assertions Then I should be on the "{wallet}" wallet "{screen}" screen ``` --- ## Step 2: Write Step Definitions ### File Location ``` tests/{domain}/e2e/steps/{feature-name}.ts ``` ### Step Definition Template ```typescript import { Given, When, Then } from 'cucumber'; import { expect } from 'chai'; // CSS selectors — group all selectors at the top const SELECTORS = { COMPONENT: '.MyComponent_component', BUTTON: '.MyComponent_button', DIALOG: '.MyDialog_overlay', }; Given(/^I am on the my feature screen$/, async function () { await this.client.waitForVisible(SELECTORS.COMPONENT); }); When(/^I click the action button$/, async function () { await this.waitAndClick(SELECTORS.BUTTON); }); Then(/^I should see the dialog$/, async function () { await this.client.waitForVisible(SELECTORS.DIALOG); }); Then(/^I should not see the dialog$/, async function () { await this.client.waitForVisible(SELECTORS.DIALOG, null, true); }); ``` ### Step Definition Conventions 1. **Import pattern**: Always import `Given`, `When`, `Then` from `'cucumber'` and `expect` from `'chai'`. 2. **Regex matching**: Use regex patterns for step matching. Capture dynamic values with groups: ```typescript Given(/^I have a "([^"]*)" wallet$/, async function (walletName) { // walletName captured from quotes in feature file }); ``` 3. **Async functions**: All step definitions MUST use `async function` (not arrow functions — Cucumber needs `this` context). 4. **`this` context**: The test context provides these properties: ```typescript this.client // WebDriver.io client — DOM interaction this.app // Spectron Application instance this.browserWindow // Electron BrowserWindow this.context // Shared data object between steps this.intl // i18n translation helper this.waitAndClick(selector) // Wait for visible + enabled, then click this.waitAndGetText(selector) // Wait for text, then get it this.waitAndSetValue(selector, value) // Wait for element, then set value ``` 5. **CSS Selectors**: Use CSS class selectors matching React component SCSS module classes. Daedalus uses SCSS modules, so class names follow `{ComponentName}_{className}` pattern: ```typescript '.WalletSendForm_component' '.SidebarCategory_active' '.TopBar_leftIcon' '.NavButton_component.summary' ``` 6. **Selectors object**: Group all selectors in a `SELECTORS` const at the top of the file for maintainability. 7. **Executing code in the app context**: Use `this.client.execute()` or `this.client.executeAsync()` to run code inside the Electron renderer process: ```typescript // Synchronous execution const result = await this.client.execute(() => { return daedalus.stores.wallets.active?.name; }); // result.value contains the return value // Async execution (with callback) await this.client.executeAsync((done) => { daedalus.stores.wallets.refreshWalletsData() .then(done) .catch((error) => done(error)); }); ``` 8. **Accessing Daedalus internals**: Inside `execute`/`executeAsync`, the global `daedalus` object exposes: ```typescript daedalus.stores // MobX stores (wallets, networkStatus, sidebar, profile, etc.) daedalus.actions // MobX actions daedalus.api // API client (daedalus.api.ada) daedalus.translations // i18n translation objects ``` 9. **Assertions**: Use Chai's `expect` for assertions: ```typescript const text = await this.waitAndGetText(SELECTORS.TITLE); expect(text).to.equal('Expected Title'); expect(text).to.include('partial'); expect(result.value).to.be.true; ``` --- ## Step 3: Write Domain-Specific Helpers ### File Location ``` tests/{domain}/e2e/steps/helpers.ts ``` ### Helper Function Patterns ```typescript import { DEFAULT_TIMEOUT } from '../../../common/e2e/steps/config'; /** * Wait for an element and extract data from it. */ export const getElementData = async (client, selector: string) => { await client.waitForVisible(selector); return client.getText(selector); }; /** * Wait until a condition is true in the app state. */ export const waitUntilCondition = async function (conditionFn: () => boolean) { await this.client.waitUntil( async () => { const result = await this.client.execute(conditionFn); return result.value === true; }, DEFAULT_TIMEOUT ); }; /** * Navigate to a specific route in the app. */ export const navigateTo = async function (route: string) { await this.client.execute((r) => { daedalus.actions.router.goToRoute.trigger({ route: r }); }, route); }; /** * Wait until a specific URL is reached. */ export const waitUntilUrlEquals = async function (expectedUrl: string) { const maxWait = DEFAULT_TIMEOUT; const freq = 500; let attempts = maxWait / freq; const check = async () => { const url = await this.client.url(); if (url.value.includes(expectedUrl)) return true; if (--attempts <= 0) throw new Error(`URL never reached: ${expectedUrl}`); await new Promise((r) => setTimeout(r, freq)); return check(); }; await check(); }; ``` ### Helper Conventions 1. **Export all helpers as named exports** — never use default exports. 2. **Accept `client` as first parameter** for functions that don't need `this` context. 3. **Use `function` (not arrow) for helpers that need `this` binding** — they'll be called with `.call(this, ...)`. 4. **Import `DEFAULT_TIMEOUT`** from `tests/common/e2e/steps/config.ts` for consistent timeouts. 5. **Keep helpers focused** — one responsibility per function. --- ## Step 4: Common Available Helpers ### From `tests/common/e2e/steps/helpers.ts` | Helper | Purpose | |-------------------------------|--------------------------------------------| | `waitAndClick(selector)` | Wait visible + enabled, then click | | `waitAndGetText(selector)` | Wait for text content, then retrieve it |
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기