@nouhouari/conductor-e2e 0.0.0-stage → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +237 -0
- package/LICENSE +21 -0
- package/README.md +196 -2
- package/config/environments/default.ts +41 -0
- package/config/environments/dev.ts +16 -0
- package/config/environments/staging.ts +15 -0
- package/config/index.ts +70 -0
- package/config/types.ts +94 -0
- package/dist/config/environments/default.d.ts +3 -0
- package/dist/config/environments/default.d.ts.map +1 -0
- package/dist/config/environments/default.js +40 -0
- package/dist/config/environments/default.js.map +1 -0
- package/dist/config/environments/dev.d.ts +3 -0
- package/dist/config/environments/dev.d.ts.map +1 -0
- package/dist/config/environments/dev.js +18 -0
- package/dist/config/environments/dev.js.map +1 -0
- package/dist/config/environments/staging.d.ts +3 -0
- package/dist/config/environments/staging.d.ts.map +1 -0
- package/dist/config/environments/staging.js +17 -0
- package/dist/config/environments/staging.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +81 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +93 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +3 -0
- package/dist/config/types.js.map +1 -0
- package/dist/src/drivers/ApiDriver.d.ts +16 -0
- package/dist/src/drivers/ApiDriver.d.ts.map +1 -0
- package/dist/src/drivers/ApiDriver.js +48 -0
- package/dist/src/drivers/ApiDriver.js.map +1 -0
- package/dist/src/drivers/DatabaseDriver.d.ts +12 -0
- package/dist/src/drivers/DatabaseDriver.d.ts.map +1 -0
- package/dist/src/drivers/DatabaseDriver.js +15 -0
- package/dist/src/drivers/DatabaseDriver.js.map +1 -0
- package/dist/src/drivers/FlutterDesktopDriver.d.ts +139 -0
- package/dist/src/drivers/FlutterDesktopDriver.d.ts.map +1 -0
- package/dist/src/drivers/FlutterDesktopDriver.js +445 -0
- package/dist/src/drivers/FlutterDesktopDriver.js.map +1 -0
- package/dist/src/drivers/MaestroDriver.d.ts +19 -0
- package/dist/src/drivers/MaestroDriver.d.ts.map +1 -0
- package/dist/src/drivers/MaestroDriver.js +144 -0
- package/dist/src/drivers/MaestroDriver.js.map +1 -0
- package/dist/src/drivers/WebDriver.d.ts +21 -0
- package/dist/src/drivers/WebDriver.d.ts.map +1 -0
- package/dist/src/drivers/WebDriver.js +57 -0
- package/dist/src/drivers/WebDriver.js.map +1 -0
- package/dist/src/hooks/browser.hooks.d.ts +2 -0
- package/dist/src/hooks/browser.hooks.d.ts.map +1 -0
- package/dist/src/hooks/browser.hooks.js +15 -0
- package/dist/src/hooks/browser.hooks.js.map +1 -0
- package/dist/src/hooks/database.hooks.d.ts +2 -0
- package/dist/src/hooks/database.hooks.d.ts.map +1 -0
- package/dist/src/hooks/database.hooks.js +11 -0
- package/dist/src/hooks/database.hooks.js.map +1 -0
- package/dist/src/hooks/desktop.hooks.d.ts +2 -0
- package/dist/src/hooks/desktop.hooks.d.ts.map +1 -0
- package/dist/src/hooks/desktop.hooks.js +15 -0
- package/dist/src/hooks/desktop.hooks.js.map +1 -0
- package/dist/src/hooks/flutterDesktop.hooks.d.ts +2 -0
- package/dist/src/hooks/flutterDesktop.hooks.d.ts.map +1 -0
- package/dist/src/hooks/flutterDesktop.hooks.js +26 -0
- package/dist/src/hooks/flutterDesktop.hooks.js.map +1 -0
- package/dist/src/hooks/index.d.ts +6 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js +25 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/maestro.hooks.d.ts +2 -0
- package/dist/src/hooks/maestro.hooks.d.ts.map +1 -0
- package/dist/src/hooks/maestro.hooks.js +8 -0
- package/dist/src/hooks/maestro.hooks.js.map +1 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +30 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/pages/BasePage.d.ts +11 -0
- package/dist/src/pages/BasePage.d.ts.map +1 -0
- package/dist/src/pages/BasePage.js +22 -0
- package/dist/src/pages/BasePage.js.map +1 -0
- package/dist/src/scenarios/RemoteScenarioFetcher.d.ts +51 -0
- package/dist/src/scenarios/RemoteScenarioFetcher.d.ts.map +1 -0
- package/dist/src/scenarios/RemoteScenarioFetcher.js +162 -0
- package/dist/src/scenarios/RemoteScenarioFetcher.js.map +1 -0
- package/dist/src/scenarios/cli.d.ts +3 -0
- package/dist/src/scenarios/cli.d.ts.map +1 -0
- package/dist/src/scenarios/cli.js +26 -0
- package/dist/src/scenarios/cli.js.map +1 -0
- package/dist/src/support/logger.d.ts +3 -0
- package/dist/src/support/logger.d.ts.map +1 -0
- package/dist/src/support/logger.js +16 -0
- package/dist/src/support/logger.js.map +1 -0
- package/dist/src/support/retry.d.ts +7 -0
- package/dist/src/support/retry.d.ts.map +1 -0
- package/dist/src/support/retry.js +21 -0
- package/dist/src/support/retry.js.map +1 -0
- package/dist/src/world/ConductorWorld.d.ts +39 -0
- package/dist/src/world/ConductorWorld.d.ts.map +1 -0
- package/dist/src/world/ConductorWorld.js +112 -0
- package/dist/src/world/ConductorWorld.js.map +1 -0
- package/package.json +81 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`release-java.yml` no longer fails after a successful Central publish** — bumped `central-publishing-maven-plugin` 0.7.0 → 0.11.0. 0.7.0 could not parse the `warnings` field the Central Portal now returns while polling deployment status, so the job failed even though `conductor-java 0.2.0` was published.
|
|
12
|
+
|
|
13
|
+
## [conductor-e2e 0.1.9] — 2026-10-02
|
|
14
|
+
|
|
15
|
+
Docs-only release so the npmjs.com package page shows the current install instructions.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Docs: install from the public registries** — `README.md`, `docs/USER_GUIDE.md` and `java/README.md` now install `@nouhouari/conductor-e2e` / `@nouhouari/conductor-mcp` from npmjs.org and `io.github.nouhouari.conductor:conductor-core` from Maven Central (Maven + Gradle snippets), with no `.npmrc`, `settings.xml` or token. The user guide's install command, imports and hooks `require.resolve` path use the scoped `@nouhouari/conductor-e2e` name, and the README flags the unscoped `conductor-e2e` / `conductor-mcp` packages as the frozen pre-rename names.
|
|
20
|
+
- **Docs: MCP configuration and upgrading** — `README.md` gained "Configure the MCP server" and "Upgrading" sections (TypeScript, Java groupId move, MCP). MCP snippets in `README.md`, `mcp/README.md` and `docs/USER_GUIDE.md` use `@nouhouari/conductor-mcp@latest`, and `mcp/README.md` explains version pinning, the frozen unscoped package and the legacy `.npmrc` mapping.
|
|
21
|
+
|
|
22
|
+
## [conductor-e2e 0.1.8] — 2026-10-02
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **npmjs.org publish actually targets npmjs.org** — the second `setup-node` step in `release.yml` and `release-mcp.yml` now sets `scope: '@nouhouari'`. Without it, the `@nouhouari:registry=https://npm.pkg.github.com/` line written by the GitHub Packages step survived and overrode `--registry`, so the "Publish to npmjs.org" step re-published to GitHub Packages with the npm token and failed with E401. `conductor-e2e` 0.1.7 and `conductor-mcp` 0.2.1/0.2.2 therefore never reached npmjs.org; 0.1.8 and 0.2.3 are the first versions there.
|
|
27
|
+
|
|
28
|
+
## [conductor-java 0.2.0] — 2026-10-02
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Published to Maven Central under a new groupId** — the Java port is now `io.github.nouhouari.conductor:conductor-core` (was `com.nouhouari.conductor`, GitHub Packages only). Consumers no longer need a `<repositories>` entry or a `read:packages` token. Java package names (`com.nouhouari.conductor.*`) are unchanged.
|
|
33
|
+
- **`release-java.yml` publishes to Maven Central** — on `java-v*` tags it verifies the tag matches `java/pom.xml` (and refuses `-SNAPSHOT`), builds and tests `conductor-core`, attaches its jars to a GitHub Release, and runs `mvn -P release deploy`. Needs the `MAVEN_CENTRAL_USERNAME`, `MAVEN_CENTRAL_PASSWORD`, `MAVEN_GPG_PRIVATE_KEY` and `MAVEN_GPG_PASSPHRASE` repository secrets.
|
|
34
|
+
- `java/pom.xml` gained the metadata Central requires (url, license, developers, scm) and a `release` profile: sources + javadoc jars, GPG signing, and `central-publishing-maven-plugin`. The GitHub Packages `<distributionManagement>` was removed.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **CI coverage for the Java port** — `ci.yml` gained a `java` job (temurin JDK 21) that builds and unit-tests `conductor-core`, compiles `conductor-example`'s step definitions and page objects, and runs a Cucumber JVM `--dry-run` over `example/features` with the real glue so undefined/ambiguous steps fail the build. The example's suites need real browsers/devices and are never executed on a hosted runner.
|
|
39
|
+
- `java/README.md` gained a "CI & Releasing" section.
|
|
40
|
+
|
|
41
|
+
## [conductor-mcp 0.2.3] — 2026-10-02
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- `init_project` pins `@nouhouari/conductor-e2e` `^0.1.8`, the first version published to npmjs.org.
|
|
46
|
+
- `bin` path normalised to `dist/cli.js` (npm was auto-correcting `./dist/cli.js` on publish).
|
|
47
|
+
|
|
48
|
+
- **Java projects resolve `conductor-core` from Maven Central** — `init_project` with `language: "java"` now generates a `pom.xml` depending on `io.github.nouhouari.conductor:conductor-core:0.2.0`, with no GitHub Packages `<repositories>` block or `~/.m2/settings.xml` token step.
|
|
49
|
+
- **Docs: npm install** — `mcp/README.md` now installs `@nouhouari/conductor-mcp` from the public npm registry; the GitHub Packages `.npmrc` / `read:packages` setup is no longer required.
|
|
50
|
+
|
|
51
|
+
## [conductor-e2e 0.1.7] — 2026-10-01
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **Release workflow publishes to npmjs.org** — `release.yml` now publishes `@nouhouari/conductor-e2e` to the public npm registry (via the `NPM_TOKEN` repository secret) in addition to GitHub Packages.
|
|
56
|
+
- **Idempotent release publishes** — publish steps in `release.yml` and `release-mcp.yml` skip versions already present on the target registry, so a partially failed release can be re-run.
|
|
57
|
+
|
|
58
|
+
## [conductor-mcp 0.2.2] — 2026-10-01
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- Re-release to publish `@nouhouari/conductor-mcp` to npmjs.org with the idempotent release workflow.
|
|
63
|
+
|
|
64
|
+
## [conductor-mcp 0.2.1] — 2026-09-04
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- **Release workflow publishes to npmjs.org** — `release-mcp.yml` now publishes `@nouhouari/conductor-mcp` to the public npm registry (via the `NPM_TOKEN` repository secret) in addition to GitHub Packages, so `npx @nouhouari/conductor-mcp` works without a GitHub Packages `.npmrc`.
|
|
69
|
+
|
|
70
|
+
- **Docs: GitHub Copilot CLI wire-up for `conductor-mcp`** — `mcp/README.md` and `docs/USER_GUIDE.md` now document the `~/.copilot/mcp-config.json` entry (including the Copilot-specific `"type": "stdio"` and `"tools"` allow-list) alongside the existing Claude Code / Cursor / Continue instructions. The user guide's setup snippet was also corrected to the published package name `@nouhouari/conductor-mcp` and now notes the GitHub Packages `.npmrc` requirement and Java target-project support.
|
|
71
|
+
|
|
72
|
+
## [conductor-java 0.1.0] — 2026-07-30
|
|
73
|
+
|
|
74
|
+
First release of the Java port. Published to GitHub Packages as `com.nouhouari.conductor:conductor-core`.
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **`conductor-core`** — Java/Cucumber-JVM/Playwright port of the framework, developed alongside the TypeScript implementation and driving the *same* Gherkin feature files under `example/features`.
|
|
79
|
+
- `ConductorWorld` with lazily-instantiated drivers, shared test data, and picocontainer-based injection into step definitions.
|
|
80
|
+
- Drivers: `WebDriver` (Playwright), `ApiDriver` (Playwright `APIRequestContext`), `MaestroDriver` (Maestro CLI), `JavaFxDriver` (fxagent HTTP API), `FlutterDesktopDriver` (Dart VM service), and the abstract `DatabaseDriver`.
|
|
81
|
+
- Tag-driven Cucumber-JVM hooks mirroring the TS `@web` / `@mobile` / `@flutter-desktop` / `@database` lifecycle, plus `BasePage`.
|
|
82
|
+
- YAML `ConfigLoader` with the TS precedence order (`config/default.yml` → `config/local-overrides.yml` → `config/${TEST_ENV}.yml` → environment variables) and the same env-var names.
|
|
83
|
+
- **`conductor-example`** (not published) — ported step definitions and page objects, plus one JUnit 5 `@Suite` class per platform tag, the Java equivalent of `example/cucumber.js`'s named profiles.
|
|
84
|
+
|
|
85
|
+
### Notes
|
|
86
|
+
|
|
87
|
+
- Validated end-to-end against the real apps, no mocks: `ApiSuiteTest` 8/8, `WebSuiteTest` 9/9, `DesktopSuiteTest` 8/8, `FlutterDesktopSuiteTest` 5/5, `MobileSuiteTest` 8/8, `CrossPlatformSuiteTest` 10/10.
|
|
88
|
+
- `JavaFxDriver` targets `fxagent.jar` v1.0.0 (package `com.hin.fxagent`); waits are polled client-side via `POST /api/v1/elements/query` because the agent's `/elements/wait` cannot express `hidden`/absent states.
|
|
89
|
+
- The `RemoteScenarioFetcher` / `conductor-fetch-features` CLI equivalent is intentionally not ported.
|
|
90
|
+
|
|
91
|
+
## [conductor-mcp 0.2.0] — 2026-07-30
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
- **Java project support** — every project-aware tool now detects whether the working directory belongs to a TypeScript project (`cucumber.js`) or a Java/Maven project (`pom.xml` + Cucumber glue under `src/test/java`) and adapts accordingly. When both are found, the closest project to the working directory wins.
|
|
96
|
+
- `list_steps` parses `@Given` / `@When` / `@Then` annotations in `.java` sources.
|
|
97
|
+
- `list_page_objects` parses Java page-object classes and their public methods.
|
|
98
|
+
- `scaffold_step_def` / `scaffold_page_object` emit Java sources into the resolved glue and page packages.
|
|
99
|
+
- `dry_run_scenario` runs the Cucumber JVM CLI (`mvn test-compile` → `dependency:build-classpath` → `io.cucumber.core.cli.Main --dry-run`) and reports undefined/ambiguous steps. The CLI is used instead of `mvn test` because JUnit Platform suite `@ConfigurationParameter` values override `-D` system properties.
|
|
100
|
+
- `get_conductor_api` accepts `language: "java"` and returns the Java API surface for all 8 surfaces.
|
|
101
|
+
- **`init_project` accepts `language: "java"`** — bootstraps a Maven project (pom, JUnit Platform suites, `conductor.yml`, sample feature/step-def/page object, README, `.gitignore`) with configurable `groupId`, `artifactId` and `basePackage`. The `flutter-desktop` platform is now selectable.
|
|
102
|
+
|
|
103
|
+
### Fixed
|
|
104
|
+
|
|
105
|
+
- **`wait_for_desktop_element` with `state: "hidden"` (or `disabled`) always errored** — it called the fxagent `POST /api/v1/elements/wait` endpoint, which returns HTTP 500 when nothing matches the selector, making "wait for this to disappear" impossible. The tool now polls `POST /api/v1/elements/query` client-side and reports the last-seen element on timeout. Supported states: `visible`, `hidden`, `enabled`, `disabled`, `exists`.
|
|
106
|
+
|
|
107
|
+
## [0.1.6] — 2026-06-21
|
|
108
|
+
|
|
109
|
+
### Added
|
|
110
|
+
|
|
111
|
+
- **Remote scenario sourcing** — opt-in mode to run Cucumber scenarios fetched from the requ scenario API (`GET /api/scenarios`) instead of the local `features/` folder. The local filesystem flow is unchanged and remains the default.
|
|
112
|
+
- `fetchScenarios()` / `reconstructFeatureFiles()` (exported from `src/scenarios/RemoteScenarioFetcher.ts`) — fetch scenarios with server-side filters (`project`, `story`, `requirement`, `phase`, `mode`, `tags`, `feature`, `q`, `valid`) and reconstruct them into `.feature` files grouped by feature, recovering feature-level tags, inserting `Background:`, and preserving `Examples:`.
|
|
113
|
+
- `conductor-fetch-features` bin (`src/scenarios/cli.ts`) — prefetch step that writes the reconstructed features to disk.
|
|
114
|
+
- New optional `remoteScenarios` config block with `REMOTE_SCENARIOS_*` env-var overrides.
|
|
115
|
+
- Example: new `remote` cucumber profile and `fetch:remote` / `test:remote` / `test:remote:dry` scripts.
|
|
116
|
+
- **New exported types**: `RemoteScenariosConfig`, `RemoteScenariosFilters`, `RemoteScenarioSummary`, `ReconstructResult`
|
|
117
|
+
|
|
118
|
+
## [conductor-mcp 0.1.6] — 2026-06-21
|
|
119
|
+
|
|
120
|
+
### Added
|
|
121
|
+
|
|
122
|
+
- **`init_project` scaffolds the remote scenario mode (opt-in)** — newly bootstrapped projects now include the `remote` cucumber profile, the `fetch:remote` / `test:remote` / `test:remote:dry` scripts, a commented `REMOTE_SCENARIOS_*` section in `.env.example`, `.remote-features/` in `.gitignore`, and a "Remote Scenarios" section in the README. Local feature files remain the default; the remote profile is inert until `npm run test:remote` is run. No new init parameter or prompt.
|
|
123
|
+
- Bootstrapped projects now depend on `@nouhouari/conductor-e2e@^0.1.6` (provides the `conductor-fetch-features` bin used by `fetch:remote`).
|
|
124
|
+
|
|
125
|
+
## [0.1.5] — 2026-06-19
|
|
126
|
+
|
|
127
|
+
### Added
|
|
128
|
+
|
|
129
|
+
- **`FlutterDesktopDriver`** — 9 new methods expanding the gesture and query API:
|
|
130
|
+
- `doubleTap(finder, timeoutMs?)` — double-tap gesture (`double_tap` command)
|
|
131
|
+
- `longPress(finder, timeoutMs?)` — long-press gesture (`long_press` command)
|
|
132
|
+
- `scroll(finder, dx, dy, durationMs?, frequency?, timeoutMs?)` — scroll a `Scrollable` widget by pixel offset
|
|
133
|
+
- `scrollIntoView(finder, alignment?, timeoutMs?)` — scroll until a widget is visible; `alignment` 0.0 = top, 0.5 = center, 1.0 = bottom
|
|
134
|
+
- `clearText(finder, timeoutMs?)` — clear a text field (enables text-entry emulation, taps to focus, sends empty string)
|
|
135
|
+
- `isVisible(finder, timeoutMs?)` → `boolean` — non-throwing visibility probe; returns `true`/`false` instead of throwing (default probe timeout 500 ms)
|
|
136
|
+
- `getOffset(finder, offsetType?, timeoutMs?)` → `{ dx, dy }` — get widget screen coordinates (`get_offset` command); `offsetType`: `'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'center'`
|
|
137
|
+
- `waitForCondition(condition, timeoutMs?)` — wait for the app to reach a quiescent state, the Flutter equivalent of `page.waitForLoadState()`; conditions: `'NoPendingFrames'`, `'NoTransientCallbacks'`, `'FirstFrameRasterized'`
|
|
138
|
+
- `setFrameSync(enabled, timeoutMs?)` — pause/resume Flutter's frame-sync during heavy animations, mirrors disabling CSS transitions in web tests
|
|
139
|
+
- **`FlutterDesktopDriver.connect(vmServiceUrl, timeoutMs?)`** — connect to an already-running Flutter app via its Dart VM service URL, without spawning a process. Enables use on Android (after `adb forward`), iOS (after `iproxy`), and Windows/Linux desktop. Accepts both HTTP (`http://localhost:PORT/TOKEN/`) and WebSocket (`ws://localhost:PORT/TOKEN/ws`) form. `isLaunched` now returns `true` after `connect()` as well as `launch()`.
|
|
140
|
+
- **New exported types**: `OffsetType`, `Offset`, `WaitCondition`
|
|
141
|
+
- **Example step definitions** for all new `FlutterDesktopDriver` methods
|
|
142
|
+
|
|
143
|
+
## [conductor-mcp 0.1.4] — 2026-06-19
|
|
144
|
+
|
|
145
|
+
### Added
|
|
146
|
+
|
|
147
|
+
- **`@nouhouari/conductor-mcp`**: Added `flutter` API surface to the built-in reference (`get_api_reference`, `list_steps` surface filter). Documents the full `FlutterDesktopDriver` public API — lifecycle (`launch`, `connect`, `close`), finders, gesture/interaction methods (`tap`, `doubleTap`, `longPress`, `enterText`, `clearText`, `scroll`, `scrollIntoView`), query methods (`getText`, `getOffset`, `isVisible`), wait methods (`waitFor`, `waitForAbsent`, `waitForCondition`), and advanced utilities (`requestData`, `setFrameSync`, `takeScreenshot`). Also documents the `@flutter-desktop` tag hook and `FlutterDesktopConfig`. Updated the `world` surface to include the `flutterDesktop` and `isFlutterDesktopLaunched` getters.
|
|
148
|
+
|
|
149
|
+
## [conductor-mcp 0.1.2] — 2026-06-19
|
|
150
|
+
|
|
151
|
+
### Changed
|
|
152
|
+
|
|
153
|
+
- **`@nouhouari/conductor-mcp`**: Renamed package from `conductor-mcp` to `@nouhouari/conductor-mcp` and switched registry from npm to GitHub Packages. Consumers should update their install command to `npm install @nouhouari/conductor-mcp` and configure `@nouhouari:registry=https://npm.pkg.github.com` in their `.npmrc`.
|
|
154
|
+
|
|
155
|
+
## [0.1.3] — 2026-06-19
|
|
156
|
+
|
|
157
|
+
### Changed
|
|
158
|
+
|
|
159
|
+
- **`conductor-e2e`**: Renamed package to `@nouhouari/conductor-e2e` and switched registry from npm to GitHub Packages. Update installs to `npm install @nouhouari/conductor-e2e` with `@nouhouari:registry=https://npm.pkg.github.com` in `.npmrc`.
|
|
160
|
+
|
|
161
|
+
### Added
|
|
162
|
+
|
|
163
|
+
- **`FlutterDesktopDriver`** — new TypeScript driver that launches a Flutter macOS `.app` binary, connects to its Dart VM service via WebSocket, and issues `ext.flutter.driver` JSON-RPC commands. Public API: `launch()`, `close()`, `tap(finder)`, `enterText(finder, text)`, `getText(finder)`, `waitFor(finder)`, `waitForAbsent(finder)`, `requestData(message)`, `takeScreenshot(name)`.
|
|
164
|
+
- **`requestData()` action dispatch pattern** — primary mechanism for invoking app-side actions (toggle, edit, delete, etc.) via `enableFlutterDriverExtension`. Bypasses `hitTestable()` which never resolves for widgets inside a `ListView` on macOS desktop.
|
|
165
|
+
- **`@flutter-desktop` tag** — new tag-driven hook that logs driver availability before the scenario and takes a failure screenshot + closes the driver after. Lives in `src/hooks/flutterDesktop.hooks.ts`.
|
|
166
|
+
- **`FlutterDesktopConfig`** — new config key controlling the macOS `.app` binary path, Dart VM service port, and screenshot output directory.
|
|
167
|
+
- **`apps/mobile/lib/main_test.dart`** — Flutter test entry point that wires `enableFlutterDriverExtension` to the action registry via JSON dispatch.
|
|
168
|
+
- **`apps/mobile/lib/driver_actions.dart`** — app-side action registry for `toggleTodo`, `editTodoTitle`, `deleteTodo`, `setDialogText`, `refresh`, and `waitUntilLoaded`.
|
|
169
|
+
- **macOS `DebugProfile.entitlements`** — adds `network.client` entitlement so the sandboxed profile build can reach the local API server.
|
|
170
|
+
- **Flutter Desktop example scenarios** — feature file, step definitions, cucumber profile (`flutter-desktop`), and `npm run test:flutter-desktop` script covering home screen, create, toggle, edit, and delete (5 scenarios / 24 steps).
|
|
171
|
+
- **`flutter:build:macos` script** — builds the Flutter macOS app in profile mode with `main_test.dart` as the entry point and injects `API_BASE_URL` and `DISABLE_SWIPE_GESTURES` dart-defines.
|
|
172
|
+
|
|
173
|
+
### Fixed
|
|
174
|
+
|
|
175
|
+
- macOS sandbox blocked outgoing HTTP from the profile build (missing `network.client` entitlement).
|
|
176
|
+
- Race condition between `initState _loadTodos()` and test setup — resolved via `waitUntilLoaded` requestData action.
|
|
177
|
+
- Flutter driver touch events do not fire `onPressed` on macOS desktop — bypassed via `requestData`.
|
|
178
|
+
- `TestTextInput` mock `_client` is null on macOS — text is now set directly on the controller.
|
|
179
|
+
|
|
180
|
+
## [conductor-mcp 0.1.1] — 2026-05-29
|
|
181
|
+
|
|
182
|
+
### Fixed
|
|
183
|
+
|
|
184
|
+
- **conductor-mcp**: Quote handling in `scaffold_page_object`, `scaffold_step_def`, and `scaffold_maestro_flow`. Locator selectors like `role=textbox[name='Title']`, step patterns containing apostrophes (`I don't see {string}`), and Maestro values containing `"` were dropped verbatim into single-quoted JS literals or YAML scalars, producing broken source. All three sites now use `JSON.stringify`, which selects the quote style and escapes special characters. Fixes #1.
|
|
185
|
+
|
|
186
|
+
### Added
|
|
187
|
+
|
|
188
|
+
- **conductor-mcp@0.1.0** — Model Context Protocol (stdio) server for AI-assisted E2E test authoring. Exposes 12 tools across discovery (list steps, page objects, flows, features), scaffolding (bootstrap new projects, create features/steps/pages/flows), and validation (dry-run scenarios). Lets Claude Code, Cursor, and Continue users bootstrap a Conductor project or add tests without leaving their editor. Versioned independently on `mcp-v*` tag prefix.
|
|
189
|
+
- **conductor-mcp**: Discovery/scaffolding/validation tools now accept an optional `projectPath` argument so the AI can point the server at a specific Conductor project. When omitted, `resolveProjectContext` now also walks **down** one level through common subdirs (`tests/`, `e2e/`, `e2e-conductor/`, `qa/`, …) — makes the server usable in monorepos where the test project lives below the workspace root.
|
|
190
|
+
- **conductor-mcp**: Bootstrap template `cucumber.js` now includes the `summary` formatter alongside `progress-bar`, so a failing run prints the actual error instead of three opaque lines.
|
|
191
|
+
- **conductor-mcp**: Scaffolded `api.steps.ts` now ships a working `POST` example demonstrating the correct `ApiDriver.post(url, body, options?)` signature — preempts users reaching for the Playwright `{ data: ... }` shape.
|
|
192
|
+
- **conductor-mcp**: `scaffold_step_def` now derives parameter identifiers correctly via a three-tier strategy: (1) explicit `paramNames` if supplied, (2) **TypeScript AST walk** of the body — parses with `ts.createSourceFile` and classifies each `Identifier` by its parent node, so property keys in object literals (`{ email: ... }`), member-access names (`account.email`), declarations (`const`/`let`/`var`/parameters/destructuring), labels, and type-position references are all correctly excluded; shorthand property assignments (`{ password }`) are recognized as references; (3) generic `value`/`value2`/`count` as last resort. The earlier regex heuristic mistook property keys for references and dropped identifiers inside template-literal interpolations — both blockers reported in dogfood. `typescript` is now a runtime dep of `conductor-mcp` (added to the published package).
|
|
193
|
+
- **conductor-mcp**: `scaffold_page_object` accepts per-method `body` (in addition to `signature`), so users can ship working method implementations instead of `// TODO` stubs. The renderer no longer adds `async` to methods with non-Promise return types like `Locator`, fixing TS2326 errors on common page-object patterns.
|
|
194
|
+
- **conductor-mcp**: Scaffolded `tsconfig.json` now includes `"DOM"` in `lib`, so step definitions reaching `localStorage` / `Storage` / `Window` via `page.evaluate(...)` type-check correctly.
|
|
195
|
+
- **conductor-mcp**: Scaffolded `cucumber.js` now loads `support/**/*.ts` between the framework hooks and step definitions, and `init_project` scaffolds a `support/timeout.ts` that bumps Cucumber's default 5-second step timeout to 30 seconds — fixes spurious timeouts on real browser/app/HTTP steps.
|
|
196
|
+
- **conductor-mcp**: `init_project.includeSamples` now defaults to `false`. The previous `true` default left placeholder `example.feature` files in every project, surfacing as undefined-scenario noise in `npm test`. Real scenarios should come from `scaffold_feature` / `scaffold_step_def`. Set `includeSamples: true` explicitly only when the user wants a runnable demo.
|
|
197
|
+
- **conductor-mcp**: `init_project` accepts an optional `webBaseUrl` so the user's target URL ends up in `.env.example` directly. The tool description teaches the AI to ask the user for this during setup; if no URL is provided, the project uses `http://localhost:3000` as a safe default.
|
|
198
|
+
- **conductor-mcp**: New tool `remove_samples` — deletes the bootstrap placeholder files (`example.feature`, sample step-defs, `ExamplePage.ts`, sample Maestro flows). Only deletes files whose content is byte-equivalent to the original sample, so user edits are preserved (pass `force: true` to override). Optional `platforms` arg restricts the scope. Empty directories left behind are removed too. Total tool count is now **13**.
|
|
199
|
+
|
|
200
|
+
## [0.1.2] — 2026-05-29
|
|
201
|
+
|
|
202
|
+
### Changed
|
|
203
|
+
|
|
204
|
+
- **`conductor-e2e`**: `loadConfig()` now calls `import 'dotenv/config'` at module load, so a project's `.env` file is read automatically. Previously the scaffold wrote `.env.example` and the README referenced it, but nothing actually loaded it.
|
|
205
|
+
- **`conductor-e2e`**: Default `api.baseUrl` is now `http://localhost:3000` (no `/api` suffix). Step definitions write the full path including any `/api/...` prefix, avoiding the previous `/api/api/...` collision. `.env.example` updated accordingly. **Migration**: if your step defs wrote `${baseUrl}/todos` expecting `/api/` to be baked into baseUrl, prepend `/api/` to those URLs (or set `API_BASE_URL=http://localhost:3000/api` in your `.env` to keep the old behavior).
|
|
206
|
+
|
|
207
|
+
### Added
|
|
208
|
+
|
|
209
|
+
- **`conductor-e2e`**: `dotenv` is now a runtime dependency.
|
|
210
|
+
|
|
211
|
+
## [0.1.1] — 2026-05-29
|
|
212
|
+
|
|
213
|
+
### Changed
|
|
214
|
+
|
|
215
|
+
- **Renamed npm package from `conductor` to `conductor-e2e`.** The name `conductor` is taken on the npm registry by an unrelated project; v0.1.0 was tagged but could not be published. v0.1.1 is the first version published to npm. Consumers should `npm install conductor-e2e` and import from `'conductor-e2e'`.
|
|
216
|
+
|
|
217
|
+
## [0.1.0] — 2026-05-29
|
|
218
|
+
|
|
219
|
+
### Added
|
|
220
|
+
|
|
221
|
+
- `ConductorWorld` — Cucumber `World` subclass that lazily instantiates web/api/maestro/desktop/database drivers
|
|
222
|
+
- `WebDriver` — Playwright browser lifecycle (chromium / firefox / webkit), failure screenshots
|
|
223
|
+
- `ApiDriver` — Playwright `APIRequestContext` wrapper, optional cookie sharing with web context
|
|
224
|
+
- `MaestroDriver` — spawns Maestro CLI for Flutter / native mobile testing; live stdout streaming via `DEBUG_MAESTRO`; auto-retry with `--reinstall-driver` on gRPC `UNAVAILABLE`; adb-based screenshots
|
|
225
|
+
- `JavaFxDriver` integration via `javafx-driver@^0.2.2` for desktop JavaFX app automation
|
|
226
|
+
- `DatabaseDriver` — abstract class; users register their own adapter via `world.setDb(adapter)`
|
|
227
|
+
- `BasePage` — Playwright page object base class with `navigate()`, `waitForLoad()`, `getTitle()`
|
|
228
|
+
- Tag-driven hooks: `@web`, `@mobile`, `@desktop`, `@database`, `@cross-platform`
|
|
229
|
+
- Environment config system with overlays (`default` / `dev` / `staging`) and env var overrides (`WEB_BASE_URL`, `API_BASE_URL`, `HEADLESS`, `BROWSER`, `MAESTRO_DEVICE`)
|
|
230
|
+
- Pino-based structured logger
|
|
231
|
+
- `retry()` helper with fixed/exponential backoff
|
|
232
|
+
- Allure reporting integration via `allure-cucumberjs`
|
|
233
|
+
- Example project (`example/`) demonstrating web + API + mobile + desktop + cross-platform scenarios
|
|
234
|
+
- Sample apps under test (`apps/mobile`, `apps/desktop`, `apps/server`)
|
|
235
|
+
- GitHub Actions CI (build, type-check, dry-run scenarios)
|
|
236
|
+
- GitHub Actions release pipeline (tag-triggered, builds, creates GitHub release, publishes to npm with provenance)
|
|
237
|
+
- [User Guide](docs/USER_GUIDE.md) covering project bootstrap, configuration, all platforms, Allure, CI/CD, troubleshooting
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nourreddine Houari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,197 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Conductor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/nouhouari/conductor/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/conductor-e2e)
|
|
5
|
+
[](https://www.npmjs.com/package/conductor-mcp)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
A multi-platform E2E test framework where **one Cucumber scenario** can drive a **web browser**, a **REST API**, a **Flutter mobile app**, a **Flutter desktop app** (macOS), a **JavaFX desktop app**, and a **database** — all from TypeScript.
|
|
9
|
+
|
|
10
|
+
```gherkin
|
|
11
|
+
@cross-platform
|
|
12
|
+
Scenario: Todo created on web appears on the Flutter mobile app
|
|
13
|
+
Given I am on the todo web application
|
|
14
|
+
When I log in as "user@example.com" with password "secret"
|
|
15
|
+
And I create a todo titled "E2E Cross Platform"
|
|
16
|
+
Then the todo "E2E Cross Platform" appears on the web dashboard
|
|
17
|
+
And the API should return the todo "E2E Cross Platform" with status "open"
|
|
18
|
+
And the Flutter app should display "E2E Cross Platform" in the todo list
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Stack
|
|
22
|
+
|
|
23
|
+
| Concern | Technology |
|
|
24
|
+
|---|---|
|
|
25
|
+
| BDD runner | [`@cucumber/cucumber`](https://github.com/cucumber/cucumber-js) v11 |
|
|
26
|
+
| Web automation | [Playwright](https://playwright.dev) |
|
|
27
|
+
| API testing | Playwright `APIRequestContext` |
|
|
28
|
+
| Mobile automation | [Maestro CLI](https://maestro.mobile.dev) (Flutter / native) |
|
|
29
|
+
| Desktop automation (Flutter) | Dart VM service / `ext.flutter.driver` JSON-RPC (macOS) |
|
|
30
|
+
| Desktop automation (JavaFX) | [`javafx-driver`](https://www.npmjs.com/package/javafx-driver) |
|
|
31
|
+
| Database | Plugin interface (bring your own adapter) |
|
|
32
|
+
| Reporting | [Allure](https://allurereport.org/) (`allure-cucumberjs`) |
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
Conductor is published to the public registries — no `.npmrc`, `settings.xml` or tokens needed.
|
|
37
|
+
|
|
38
|
+
**TypeScript** — [`@nouhouari/conductor-e2e`](https://www.npmjs.com/package/@nouhouari/conductor-e2e) on npmjs.org:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @nouhouari/conductor-e2e
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Java** — [`io.github.nouhouari.conductor:conductor-core`](https://central.sonatype.com/artifact/io.github.nouhouari.conductor/conductor-core) on Maven Central (see [java/README.md](java/README.md)):
|
|
45
|
+
|
|
46
|
+
```xml
|
|
47
|
+
<dependency>
|
|
48
|
+
<groupId>io.github.nouhouari.conductor</groupId>
|
|
49
|
+
<artifactId>conductor-core</artifactId>
|
|
50
|
+
<version>0.2.0</version>
|
|
51
|
+
<scope>test</scope>
|
|
52
|
+
</dependency>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**AI-assisted authoring** — the [`@nouhouari/conductor-mcp`](https://www.npmjs.com/package/@nouhouari/conductor-mcp) MCP server runs straight from npmjs.org with `npx -y @nouhouari/conductor-mcp` (see [mcp/README.md](mcp/README.md)).
|
|
56
|
+
|
|
57
|
+
> The unscoped npm packages [`conductor-e2e`](https://www.npmjs.com/package/conductor-e2e) (last: 0.1.2) and
|
|
58
|
+
> [`conductor-mcp`](https://www.npmjs.com/package/conductor-mcp) (last: 0.1.1) are the pre-rename names and are no
|
|
59
|
+
> longer updated — use the `@nouhouari/` scoped packages above.
|
|
60
|
+
|
|
61
|
+
### Configure the MCP server
|
|
62
|
+
|
|
63
|
+
For Claude Code or Cursor, add `.mcp.json` to your project root:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"conductor": {
|
|
69
|
+
"command": "npx",
|
|
70
|
+
"args": ["-y", "@nouhouari/conductor-mcp@latest"]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
GitHub Copilot CLI (`~/.copilot/mcp-config.json`) additionally needs `"type": "stdio"` and `"tools": ["*"]`; Continue uses `~/.continue/config.json`. Start the client from inside your Conductor project and restart it after changing the config. See [mcp/README.md → Wire Up](mcp/README.md#wire-up) for every client, version pinning and alternatives to `npx`.
|
|
77
|
+
|
|
78
|
+
See the [**User Guide**](docs/USER_GUIDE.md) for a step-by-step walkthrough of bootstrapping a new E2E project from scratch.
|
|
79
|
+
|
|
80
|
+
## Why Conductor?
|
|
81
|
+
|
|
82
|
+
Most E2E frameworks pick one platform. When your product lives on multiple platforms — a web dashboard, a mobile app, a REST API, a desktop client — you end up with **N parallel test suites** that can't share scenarios, page objects, or data lifecycle.
|
|
83
|
+
|
|
84
|
+
Conductor unifies them behind a single [`ConductorWorld`](src/world/ConductorWorld.ts):
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
async function (this: ConductorWorld) {
|
|
88
|
+
await this.web.launch(); // Playwright browser
|
|
89
|
+
await this.page.goto('/login'); // active page
|
|
90
|
+
await this.api.post('/todos', { title: ... }); // shared HTTP client
|
|
91
|
+
await this.maestro.runOrThrow('verify-todo'); // Flutter mobile flow
|
|
92
|
+
await this.flutterDesktop.tap('addButton'); // Flutter desktop (macOS)
|
|
93
|
+
await this.fx.locator('#save-btn').click(); // JavaFX desktop
|
|
94
|
+
await this.db.query('SELECT ...'); // your adapter
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Drivers are lazily instantiated. Tag-driven hooks manage their lifecycle:
|
|
99
|
+
|
|
100
|
+
| Tag | Effect |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `@web` / `@cross-platform` | Launches browser, screenshots failures, closes |
|
|
103
|
+
| `@mobile` / `@cross-platform` | Targets the configured Maestro device |
|
|
104
|
+
| `@flutter-desktop` | Launches Flutter macOS app, screenshots failures, closes |
|
|
105
|
+
| `@desktop` / `@cross-platform` | Launches JavaFX app via agent JAR, closes |
|
|
106
|
+
| `@database` / `@cross-platform` | Connects DB before, disconnects after |
|
|
107
|
+
|
|
108
|
+
## Project Structure
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
conductor/
|
|
112
|
+
├── src/ Framework library
|
|
113
|
+
│ ├── drivers/ WebDriver, ApiDriver, MaestroDriver, DatabaseDriver
|
|
114
|
+
│ ├── hooks/ Tag-driven Before/After hooks
|
|
115
|
+
│ ├── pages/ BasePage to extend
|
|
116
|
+
│ ├── world/ ConductorWorld (Cucumber World subclass)
|
|
117
|
+
│ └── support/ Logger, retry helpers
|
|
118
|
+
├── config/ Environment configs (default/dev/staging)
|
|
119
|
+
├── example/ Working multi-platform example project
|
|
120
|
+
├── apps/ Sample apps under test
|
|
121
|
+
│ ├── mobile/ Flutter todo app (Android + macOS desktop)
|
|
122
|
+
│ ├── desktop/ JavaFX todo app
|
|
123
|
+
│ └── server/ Express server + web UI + REST API
|
|
124
|
+
├── docs/ User guide, API docs
|
|
125
|
+
└── docker-compose.yml PostgreSQL for the example server
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Running the Example
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# 1. Start PostgreSQL + Express server
|
|
132
|
+
docker compose up -d
|
|
133
|
+
cd apps/server && npm start &
|
|
134
|
+
|
|
135
|
+
# 2. Run all scenarios except mobile (no device required)
|
|
136
|
+
cd example
|
|
137
|
+
npx cucumber-js --tags 'not @mobile' \
|
|
138
|
+
--require-module ts-node/register \
|
|
139
|
+
--require-module tsconfig-paths/register \
|
|
140
|
+
--require '../src/hooks/index.ts' \
|
|
141
|
+
--require 'step-definitions/**/*.ts' \
|
|
142
|
+
--format progress --format allure-cucumberjs/reporter \
|
|
143
|
+
features/**/*.feature
|
|
144
|
+
|
|
145
|
+
# 3. Open the Allure report
|
|
146
|
+
npm run report && npm run report:open
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
For mobile, Flutter Desktop, and JavaFX desktop, see [docs/USER_GUIDE.md](docs/USER_GUIDE.md).
|
|
150
|
+
|
|
151
|
+
### Flutter Desktop (macOS)
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Build the test entry point (from repo root)
|
|
155
|
+
npm run flutter:build:macos
|
|
156
|
+
|
|
157
|
+
# Run Flutter Desktop scenarios
|
|
158
|
+
cd example
|
|
159
|
+
npm run test:flutter-desktop
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The build uses `lib/main_test.dart` as the entry point (which wires `enableFlutterDriverExtension`) and sets `DISABLE_SWIPE_GESTURES=true` to enable the AppBar Add button and the app-side action registry used by `requestData()`.
|
|
163
|
+
|
|
164
|
+
## Upgrading
|
|
165
|
+
|
|
166
|
+
No code APIs changed; only package names, the Java groupId and where packages are hosted.
|
|
167
|
+
|
|
168
|
+
**TypeScript (`@nouhouari/conductor-e2e`)**
|
|
169
|
+
|
|
170
|
+
- *Coming from GitHub Packages:* delete `@nouhouari:registry=https://npm.pkg.github.com` (and its `_authToken` line) from `.npmrc`, then `npm install @nouhouari/conductor-e2e@latest`.
|
|
171
|
+
- *Coming from the unscoped `conductor-e2e` (≤ 0.1.2):* `npm uninstall conductor-e2e && npm install @nouhouari/conductor-e2e`, change imports to `from '@nouhouari/conductor-e2e'`, and point the hooks in `cucumber.js` at `require.resolve('@nouhouari/conductor-e2e/dist/src/hooks/index')`.
|
|
172
|
+
|
|
173
|
+
**Java (`conductor-core`)**
|
|
174
|
+
|
|
175
|
+
- Change the dependency to `io.github.nouhouari.conductor:conductor-core:0.2.0` (was `com.nouhouari.conductor`).
|
|
176
|
+
- Delete the `maven.pkg.github.com` `<repository>` from `pom.xml` and the matching `<server>` token from `~/.m2/settings.xml` (projects generated by `conductor-mcp` before 0.2.3 have both).
|
|
177
|
+
- Java package names stay `com.nouhouari.conductor.*`, so imports and the glue (`com.nouhouari.conductor.hooks`) are unchanged.
|
|
178
|
+
|
|
179
|
+
**MCP server (`@nouhouari/conductor-mcp`)**
|
|
180
|
+
|
|
181
|
+
- Use `["-y", "@nouhouari/conductor-mcp@latest"]` in your client config (not the unscoped `conductor-mcp`), remove any GitHub Packages `.npmrc` mapping, and restart the client.
|
|
182
|
+
- Upgrading the server only changes newly scaffolded projects; update existing ones with the TypeScript / Java steps above.
|
|
183
|
+
|
|
184
|
+
## Documentation
|
|
185
|
+
|
|
186
|
+
- [**User Guide**](docs/USER_GUIDE.md) — bootstrap a new E2E project, write scenarios, run tests
|
|
187
|
+
- [**conductor-mcp**](mcp/README.md) — AI-assisted test authoring for GitHub Copilot CLI, Claude Code, Cursor, Continue
|
|
188
|
+
- [**Architecture**](CLAUDE.md) — framework internals (driver lifecycle, hooks, config)
|
|
189
|
+
- [Example project README](example/README.md) — guided tour of the working example
|
|
190
|
+
|
|
191
|
+
## Contributing
|
|
192
|
+
|
|
193
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
[MIT](LICENSE) © Nourreddine Houari
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { EnvironmentConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
export const defaultConfig: EnvironmentConfig = {
|
|
4
|
+
name: 'default',
|
|
5
|
+
web: {
|
|
6
|
+
baseUrl: 'http://localhost:3000',
|
|
7
|
+
headless: true,
|
|
8
|
+
browserName: 'chromium'
|
|
9
|
+
},
|
|
10
|
+
api: {
|
|
11
|
+
baseUrl: 'http://localhost:3000',
|
|
12
|
+
defaultCredentials: { username: 'test@example.com', password: 'password' },
|
|
13
|
+
timeoutMs: 30000
|
|
14
|
+
},
|
|
15
|
+
mobile: {
|
|
16
|
+
flowsDir: 'flows/mobile',
|
|
17
|
+
timeoutMs: 120000
|
|
18
|
+
},
|
|
19
|
+
database: {
|
|
20
|
+
enabled: false
|
|
21
|
+
},
|
|
22
|
+
desktop: {
|
|
23
|
+
agentJar: require('path').resolve(__dirname, '../../apps/desktop/agent/fxagent.jar'),
|
|
24
|
+
defaultTimeoutMs: 10000,
|
|
25
|
+
screenshotDir: 'reports/screenshots'
|
|
26
|
+
},
|
|
27
|
+
flutterDesktop: {
|
|
28
|
+
appPath: require('path').resolve(
|
|
29
|
+
__dirname,
|
|
30
|
+
'../../apps/mobile/build/macos/Build/Products/Profile/todoapp.app/Contents/MacOS/todoapp'
|
|
31
|
+
),
|
|
32
|
+
defaultTimeoutMs: 10000,
|
|
33
|
+
launchTimeoutMs: 30000,
|
|
34
|
+
screenshotDir: 'reports/screenshots'
|
|
35
|
+
},
|
|
36
|
+
remoteScenarios: {
|
|
37
|
+
baseUrl: 'http://localhost:8788/api',
|
|
38
|
+
outputDir: '.remote-features',
|
|
39
|
+
filters: {}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EnvironmentConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
export const devConfig: Partial<EnvironmentConfig> = {
|
|
4
|
+
name: 'dev',
|
|
5
|
+
web: {
|
|
6
|
+
baseUrl: 'http://localhost:3000',
|
|
7
|
+
headless: false,
|
|
8
|
+
browserName: 'chromium',
|
|
9
|
+
slowMo: 100
|
|
10
|
+
},
|
|
11
|
+
api: {
|
|
12
|
+
baseUrl: 'http://localhost:3000/api',
|
|
13
|
+
defaultCredentials: { username: 'dev@example.com', password: 'devpassword' },
|
|
14
|
+
timeoutMs: 60000
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EnvironmentConfig } from '../types';
|
|
2
|
+
|
|
3
|
+
export const stagingConfig: Partial<EnvironmentConfig> = {
|
|
4
|
+
name: 'staging',
|
|
5
|
+
web: {
|
|
6
|
+
baseUrl: 'https://staging.example.com',
|
|
7
|
+
headless: true,
|
|
8
|
+
browserName: 'chromium'
|
|
9
|
+
},
|
|
10
|
+
api: {
|
|
11
|
+
baseUrl: 'https://staging.example.com/api',
|
|
12
|
+
defaultCredentials: { username: 'staging@example.com', password: 'stagingpassword' },
|
|
13
|
+
timeoutMs: 45000
|
|
14
|
+
}
|
|
15
|
+
};
|
package/config/index.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
|
|
3
|
+
import { defaultConfig } from './environments/default';
|
|
4
|
+
import type { EnvironmentConfig } from './types';
|
|
5
|
+
|
|
6
|
+
function loadConfig(): EnvironmentConfig {
|
|
7
|
+
const env = process.env.TEST_ENV ?? 'default';
|
|
8
|
+
let base = defaultConfig;
|
|
9
|
+
|
|
10
|
+
if (env !== 'default') {
|
|
11
|
+
try {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
13
|
+
const envModule = require(`./environments/${env}`);
|
|
14
|
+
const envConfig = envModule[`${env}Config`] as EnvironmentConfig;
|
|
15
|
+
base = deepMerge(base, envConfig);
|
|
16
|
+
} catch {
|
|
17
|
+
// fall back to default
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// env-var overrides
|
|
22
|
+
if (process.env.WEB_BASE_URL) base.web.baseUrl = process.env.WEB_BASE_URL;
|
|
23
|
+
if (process.env.API_BASE_URL) base.api.baseUrl = process.env.API_BASE_URL;
|
|
24
|
+
if (process.env.HEADLESS) base.web.headless = process.env.HEADLESS !== 'false';
|
|
25
|
+
if (process.env.BROWSER) base.web.browserName = process.env.BROWSER as EnvironmentConfig['web']['browserName'];
|
|
26
|
+
if (process.env.MAESTRO_DEVICE) base.mobile.deviceId = process.env.MAESTRO_DEVICE;
|
|
27
|
+
if (process.env.FLUTTER_DESKTOP_APP_PATH && base.flutterDesktop) {
|
|
28
|
+
base.flutterDesktop.appPath = process.env.FLUTTER_DESKTOP_APP_PATH;
|
|
29
|
+
}
|
|
30
|
+
if (process.env.FLUTTER_DESKTOP_VM_PORT && base.flutterDesktop) {
|
|
31
|
+
base.flutterDesktop.vmServicePort = Number(process.env.FLUTTER_DESKTOP_VM_PORT);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (base.remoteScenarios) {
|
|
35
|
+
const rs = base.remoteScenarios;
|
|
36
|
+
if (process.env.REMOTE_SCENARIOS_URL) rs.baseUrl = process.env.REMOTE_SCENARIOS_URL;
|
|
37
|
+
if (process.env.REMOTE_SCENARIOS_PROJECT) rs.project = process.env.REMOTE_SCENARIOS_PROJECT;
|
|
38
|
+
if (process.env.REMOTE_SCENARIOS_OUTPUT_DIR) rs.outputDir = process.env.REMOTE_SCENARIOS_OUTPUT_DIR;
|
|
39
|
+
rs.filters = rs.filters ?? {};
|
|
40
|
+
if (process.env.REMOTE_SCENARIOS_STORY) rs.filters.story = process.env.REMOTE_SCENARIOS_STORY;
|
|
41
|
+
if (process.env.REMOTE_SCENARIOS_REQUIREMENT) rs.filters.requirement = process.env.REMOTE_SCENARIOS_REQUIREMENT;
|
|
42
|
+
if (process.env.REMOTE_SCENARIOS_PHASE) rs.filters.phase = process.env.REMOTE_SCENARIOS_PHASE;
|
|
43
|
+
if (process.env.REMOTE_SCENARIOS_MODE) {
|
|
44
|
+
rs.filters.mode = process.env.REMOTE_SCENARIOS_MODE as 'cumulative' | 'strict';
|
|
45
|
+
}
|
|
46
|
+
if (process.env.REMOTE_SCENARIOS_TAGS) rs.filters.tags = process.env.REMOTE_SCENARIOS_TAGS;
|
|
47
|
+
if (process.env.REMOTE_SCENARIOS_FEATURE) rs.filters.feature = process.env.REMOTE_SCENARIOS_FEATURE;
|
|
48
|
+
if (process.env.REMOTE_SCENARIOS_Q) rs.filters.q = process.env.REMOTE_SCENARIOS_Q;
|
|
49
|
+
if (process.env.REMOTE_SCENARIOS_VALID) rs.filters.valid = process.env.REMOTE_SCENARIOS_VALID === 'true';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return base;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function deepMerge<T>(base: T, override: Partial<T>): T {
|
|
56
|
+
const result = { ...base };
|
|
57
|
+
for (const key of Object.keys(override ?? {}) as (keyof T)[]) {
|
|
58
|
+
const ov = override[key];
|
|
59
|
+
if (ov !== null && typeof ov === 'object' && !Array.isArray(ov)) {
|
|
60
|
+
result[key] = deepMerge(result[key] as object, ov as object) as T[typeof key];
|
|
61
|
+
} else if (ov !== undefined) {
|
|
62
|
+
result[key] = ov as T[typeof key];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const config = loadConfig();
|
|
69
|
+
export { loadConfig };
|
|
70
|
+
export type { EnvironmentConfig };
|