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で見る