flowstride 1.0.19 → 1.0.20-rc.2
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/NATIVE_CI.md +155 -0
- package/README.md +40 -33
- package/RELEASE_NOTES.md +40 -0
- package/dist/adapters/web/playwright.d.ts +1 -0
- package/dist/adapters/web/playwright.js +18 -14
- package/dist/ci/config.d.ts +3 -0
- package/dist/ci/config.js +76 -0
- package/dist/ci/deadline.d.ts +2 -0
- package/dist/ci/deadline.js +16 -0
- package/dist/ci/init.d.ts +1 -0
- package/dist/ci/init.js +21 -0
- package/dist/ci/runtime.d.ts +19 -0
- package/dist/ci/runtime.js +190 -0
- package/dist/cli/commands/run.js +15 -34
- package/dist/cli/index.js +44 -5
- package/dist/game/bridge.d.ts +13 -0
- package/dist/game/bridge.js +15 -0
- package/dist/game/sdk.d.ts +27 -0
- package/dist/game/sdk.js +255 -0
- package/dist/game/three-bridge.d.ts +18 -0
- package/dist/game/three-bridge.js +59 -0
- package/dist/runner/flow_orchestrator.d.ts +5 -1
- package/dist/runner/flow_orchestrator.js +31 -7
- package/dist/runner/flow_worker.js +28 -4
- package/dist/runner/plugin_manager.d.ts +1 -0
- package/dist/runner/plugin_manager.js +33 -1
- package/dist/types/index.d.ts +36 -0
- package/examples/native-game/flows/canvas.flow +13 -0
- package/examples/native-game/flowstride.config.json +12 -0
- package/examples/native-game/server.mjs +12 -0
- package/package.json +14 -5
package/NATIVE_CI.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Native CI and browser-game candidate
|
|
2
|
+
|
|
3
|
+
This candidate adds native CI and game APIs to both engines. Managed Cloud
|
|
4
|
+
execution remains retired. Existing npm OSS / private Pro archive distribution
|
|
5
|
+
and fallback remain supported. Naija Throne's current runner is unchanged;
|
|
6
|
+
its migration is a separate acceptance step. Native CI uses protocol 1.
|
|
7
|
+
The public CLI selects compatible Pro engines or logs the reason and falls back
|
|
8
|
+
to OSS. Older Pro CLIs may own the npm binary and do not understand the new
|
|
9
|
+
commands; install both candidates when testing the Pro path.
|
|
10
|
+
|
|
11
|
+
The candidates pin Playwright 1.59.1 in both engines; the native install command
|
|
12
|
+
installs that engine's matching Chromium.
|
|
13
|
+
|
|
14
|
+
## Local candidate check
|
|
15
|
+
|
|
16
|
+
Use a disposable project first. Install the OSS archive, then the Pro archive if
|
|
17
|
+
needed. Both candidates must be installed for testing the new Pro path. The
|
|
18
|
+
currently hosted Cloud download still serves the existing release; this local
|
|
19
|
+
candidate does not change that endpoint or its archive.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install --save-dev /absolute/path/flowstride-1.0.20-rc.2.tgz
|
|
23
|
+
# Optional: install the local matching Pro candidate after project dependencies.
|
|
24
|
+
npm install --no-save /absolute/path/flowstride-pro-1.0.15-rc.1.tgz
|
|
25
|
+
npx --no-install flowstride install
|
|
26
|
+
npx --no-install flowstride run --ci
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Pro continues to require your normal valid developer credentials for licensed
|
|
30
|
+
features. Installing Pro uses `--no-save`: another `npm ci` can remove it. Keep
|
|
31
|
+
project dependency installation before Pro installation. An installed Pro package
|
|
32
|
+
can own `node_modules/.bin/flowstride`; both that entry point and the public OSS
|
|
33
|
+
entry point must be covered when checking fallback.
|
|
34
|
+
|
|
35
|
+
The sample in `examples/native-game` uses a tiny local canvas game. Copy its
|
|
36
|
+
files into a disposable project containing the installed candidates, then run
|
|
37
|
+
the command above. `server.mjs` is the game fixture's HTTP server; it contains no
|
|
38
|
+
Flowstride runner, adapter, worker, or CI orchestration code. A real application
|
|
39
|
+
uses its normal development/start command instead.
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
`flowstride.config.json` supports `testDir`, `baseUrl`, `timeout`, `device`
|
|
44
|
+
(`desktop`, `phone`, `landscape`), optional `viewport`, `pluginsDir`,
|
|
45
|
+
`artifactsDir`, `recordVideo` (boolean), `browserErrors` (`fail` or `record`), and
|
|
46
|
+
`webServer` (`command`, local readiness `url`, optional `cwd` and `timeout`).
|
|
47
|
+
Malformed configuration and unsafe output paths fail before execution.
|
|
48
|
+
|
|
49
|
+
Native CI is selected by `--ci` or a recognized CI environment. It runs without
|
|
50
|
+
a dashboard server, returns nonzero for failed/incomplete/crashed execution,
|
|
51
|
+
and writes latest `summary.json`/`junit.xml` reports at the configured artifact
|
|
52
|
+
root. Media and attachments go into a unique run directory referenced by the
|
|
53
|
+
summary, so local reruns retain history without mixing evidence.
|
|
54
|
+
SIGINT/SIGTERM produce interrupted results. The run owns its optional local
|
|
55
|
+
server and refuses an already-serving readiness URL.
|
|
56
|
+
|
|
57
|
+
Cloud reporting is off by default in native CI. `cloudSync: true` explicitly
|
|
58
|
+
allows existing authenticated reporting; its existing server-side security and
|
|
59
|
+
storage limitations still apply. Normal interactive reporting and the restored
|
|
60
|
+
Cloud URL overrides remain separate from retired managed execution.
|
|
61
|
+
|
|
62
|
+
`npx flowstride ci init github` generates a workflow explicitly, never during
|
|
63
|
+
installation, and refuses to overwrite it. It assumes an npm project with a
|
|
64
|
+
committed lockfile. Other package managers and monorepos can use the composite
|
|
65
|
+
action's install-command and working-directory inputs. The composite action is
|
|
66
|
+
in the OSS repository's `action/` directory and becomes remotely usable only
|
|
67
|
+
once that source is pushed. Pin its commit when configuring a consumer workflow.
|
|
68
|
+
Pro jobs pass a developer token as a secret; dependency installation precedes
|
|
69
|
+
Pro login/install, matching-browser installation, execution, and artifact upload.
|
|
70
|
+
|
|
71
|
+
## Native game actions
|
|
72
|
+
|
|
73
|
+
Register the built-in alias with `let game = "flowstride:game"`. It requires no
|
|
74
|
+
custom plugin file. Available actions:
|
|
75
|
+
|
|
76
|
+
- `tap`: canvas selector, x/y; relative coordinates by default, or `unit: "pixels"`.
|
|
77
|
+
- `drag`/`swipe`: canvas, from/to coordinates, optional duration and input type.
|
|
78
|
+
- `key`: key name and optional hold duration.
|
|
79
|
+
- `checkpoint`: named screenshot, optionally of one canvas.
|
|
80
|
+
- `snapshot` and `expectVisual`: canvas comparison with explicit minChange/maxChange.
|
|
81
|
+
- `state`/`expectState`: exposed state, optional dotted path and expected equals.
|
|
82
|
+
- `snapshotState`/`expectPreserved`: compare selected state across actions/reloads.
|
|
83
|
+
- `fixture`: a named fixture registered by the application in explicit test mode.
|
|
84
|
+
- `select`: a stable object ID resolved by the game bridge, then real pointer input.
|
|
85
|
+
|
|
86
|
+
Visual checks establish pixel differences, not gameplay correctness; pair them
|
|
87
|
+
with state or UI assertions. Mobile profiles emulate Chromium devices, not
|
|
88
|
+
physical phones. Touch dragging currently uses Chromium CDP.
|
|
89
|
+
|
|
90
|
+
For a generic browser game, import `registerGame` from
|
|
91
|
+
`flowstride/dist/game/bridge.js` and supply readState and optional test-only
|
|
92
|
+
fixtures. The function manages the browser bridge; no global setup is required
|
|
93
|
+
in the test runner.
|
|
94
|
+
|
|
95
|
+
## Three.js integration
|
|
96
|
+
|
|
97
|
+
Import `registerThreeGame` from `flowstride/dist/game/three-bridge.js` in the game
|
|
98
|
+
bundle. Register the live scene, camera, canvas, the application's own Vector3
|
|
99
|
+
and Raycaster constructors, and an optional objects map keyed by stable IDs.
|
|
100
|
+
Alternatively, assign `userData.flowstrideId` to scene objects. Optional local
|
|
101
|
+
pick points use `userData.flowstridePickPoint: [x, y, z]`.
|
|
102
|
+
|
|
103
|
+
The bridge projects the live object and raycasts the live scene; hidden,
|
|
104
|
+
occluded, off-screen, covered or non-pickable objects fail explicitly. It creates
|
|
105
|
+
no renderer or animation loop. Dispose the bridge when the scene is replaced.
|
|
106
|
+
|
|
107
|
+
Optional `readState` provides observable state. `fixtures` are available only
|
|
108
|
+
when the application explicitly registers with `testMode: true`; use that
|
|
109
|
+
setting in dedicated test builds, never public production builds. Naija Throne
|
|
110
|
+
still defines campaign-specific fixtures/rules, using these supported APIs.
|
|
111
|
+
|
|
112
|
+
Custom plugins now receive `sdk.game`, `sdk.browser.page()`, and `sdk.run`
|
|
113
|
+
(device, artifact directory and attach method), alongside the existing SDK.
|
|
114
|
+
They need no global bridge or internal Flowstride class imports.
|
|
115
|
+
|
|
116
|
+
## Validation before release
|
|
117
|
+
|
|
118
|
+
Build both packages and run `npm run test:native`. For the optional live Three.js
|
|
119
|
+
fixture set `FLOWSTRIDE_THREE_BUILD` to a Three.js build directory containing
|
|
120
|
+
three.module.js and three.core.js. OSS CLI handoff tests use
|
|
121
|
+
`FLOWSTRIDE_PRO_SOURCE` pointing at the built Pro package.
|
|
122
|
+
|
|
123
|
+
Run the real archive install/handoff harness against both candidates, including
|
|
124
|
+
missing/broken Pro and revoked-token fallback. Validate GitHub's Linux execution,
|
|
125
|
+
real licensed download and Naija Throne migration after local acceptance.
|
|
126
|
+
No push, npm publication, Cloud archive replacement or Naija Throne migration
|
|
127
|
+
is performed by building these candidates.
|
|
128
|
+
|
|
129
|
+
## Candidate acceptance automation
|
|
130
|
+
|
|
131
|
+
The repository's `Native CI acceptance` workflow tests Node 22 and 24 on Linux.
|
|
132
|
+
It uses a checksum-pinned archive in `.github/fixtures`, checks its compiled engine
|
|
133
|
+
against a fresh build, checks referenced dashboard assets, then installs that exact
|
|
134
|
+
archive in a clean consumer. It exercises native browser installation, passing and
|
|
135
|
+
deliberately failing game execution, media, server cleanup and workflow preservation.
|
|
136
|
+
It also runs the native source suite, including delayed uploads and cancellation.
|
|
137
|
+
The Pro archive belongs only in the private Pro repository.
|
|
138
|
+
|
|
139
|
+
These jobs do not publish npm packages, replace the hosted Pro archive, deploy
|
|
140
|
+
Cloud, or migrate application repositories. Native Cloud reporting stays opt-in.
|
|
141
|
+
Qualification covers Chromium and emulated desktop/phone/landscape profiles.
|
|
142
|
+
Physical Android/iOS devices, Safari/Firefox and other game engines are outside
|
|
143
|
+
this candidate's verified support scope. Real Cloud licensing, reporting and
|
|
144
|
+
video storage require a separate live acceptance run.
|
|
145
|
+
|
|
146
|
+
## OSS npm preview release
|
|
147
|
+
|
|
148
|
+
OSS `1.0.20-rc.2` requires Node.js 22.12.0+. Publish the verified archive under
|
|
149
|
+
`next`; install with `npm install --save-dev flowstride@next` after publication.
|
|
150
|
+
Use the release archive and checksum rather than rebuilding from a dirty checkout.
|
|
151
|
+
The matching local Pro candidate remains `1.0.15-rc.1`; hosted Pro remains
|
|
152
|
+
`1.0.14`. Complete authentication before reinstalling the local Pro candidate.
|
|
153
|
+
The unchanged dashboard bundle is reused from the previously qualified rc.1
|
|
154
|
+
archive; the engine is rebuilt from this source. The release input manifest
|
|
155
|
+
records both that archive's checksum and the merged base commit.
|
package/README.md
CHANGED
|
@@ -6,6 +6,34 @@ Flowstride OS combines the human-readable structure of Behavior-Driven Developme
|
|
|
6
6
|
|
|
7
7
|
[Documentation](https://docs.flowstride.io/)
|
|
8
8
|
|
|
9
|
+
## Native CI and game testing preview — 1.0.20-rc.2
|
|
10
|
+
|
|
11
|
+
This is an opt-in preview intended for the npm `next` tag. The default stable
|
|
12
|
+
release remains available through `latest`. After the preview is published:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install --save-dev flowstride@next
|
|
16
|
+
npx --no-install flowstride install
|
|
17
|
+
npx --no-install flowstride run --ci
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Node.js 22.12.0+ is required; acceptance covers the minimum version and current
|
|
21
|
+
Node.js 22/24 on Linux, plus Node.js 24 on macOS. The preview adds native CI
|
|
22
|
+
exit codes, JSON/JUnit reports, videos/screenshots, device profiles, explicit
|
|
23
|
+
GitHub workflow generation, and supported canvas/Three.js testing APIs.
|
|
24
|
+
See [native CI setup](NATIVE_CI.md) and [release notes](RELEASE_NOTES.md).
|
|
25
|
+
|
|
26
|
+
For this preview, use OSS alone or manually install the matching private Pro
|
|
27
|
+
`1.0.15-rc.1` candidate after project dependencies. Normal Cloud login still
|
|
28
|
+
serves Pro `1.0.14`; signing in afterward can replace the preview's Pro package,
|
|
29
|
+
and that older CLI does not support the new native commands. Cloud reporting
|
|
30
|
+
and video-storage defects remain open. Native CI keeps Cloud reporting off by
|
|
31
|
+
default. Managed Cloud execution remains retired.
|
|
32
|
+
|
|
33
|
+
This release qualifies Chromium desktop and emulated phone/landscape profiles
|
|
34
|
+
and browser-game fixtures. It does not establish physical-phone, Safari,
|
|
35
|
+
Firefox, other game-engine, or complete application-game coverage.
|
|
36
|
+
|
|
9
37
|
---
|
|
10
38
|
|
|
11
39
|
## Why Flowstride?
|
|
@@ -21,7 +49,7 @@ Traditional E2E frameworks force you to choose between highly technical code (wh
|
|
|
21
49
|
|
|
22
50
|
## Installation & Quick Start
|
|
23
51
|
|
|
24
|
-
Flowstride requires Node.js
|
|
52
|
+
Flowstride requires Node.js 22.12.0 or higher. Use a current patched Node.js 22 or 24 release.
|
|
25
53
|
|
|
26
54
|
**1. Install globally (or as a dev dependency in your project):**
|
|
27
55
|
|
|
@@ -71,41 +99,20 @@ npx flowstride login
|
|
|
71
99
|
|
|
72
100
|
---
|
|
73
101
|
|
|
74
|
-
##
|
|
75
|
-
|
|
76
|
-
Flowstride is designed with a **zero-trust, local-first architecture**. Whether you are testing internal microservices, staging environments, or production APIs, your sensitive data, PHI, and credentials remain strictly on your infrastructure.
|
|
77
|
-
|
|
78
|
-
### Open Source Core: 100% Local Isolation
|
|
79
|
-
|
|
80
|
-
The Open Source version of Flowstride operates entirely as an ephemeral, local execution engine.
|
|
81
|
-
|
|
82
|
-
- **Air-Gapped Execution:** Flowstride does not "phone home." The OS runner executes strictly on your local machine or within your private CI/CD pipeline. There is no external Flowstride database storing your test payloads or network logs.
|
|
83
|
-
|
|
84
|
-
- **Ephemeral Memory:** Flowstride is inherently stateless. API request headers, response bodies, and DOM snapshots exist only in Node.js RAM during execution. The millisecond the execution process terminates, all memory is wiped.
|
|
85
|
-
|
|
86
|
-
- **The DevTools Analogy:** Our network interception works natively within Playwright's browser context routing. It is fundamentally identical to opening the Chrome DevTools "Network" tab locally. We do not act as an external Man-In-The-Middle (MITM) proxy.
|
|
87
|
-
|
|
88
|
-
- **Opt-In Local State:** Artifacts saved to disk—such as execution videos (`.flowstride/mock-cloud/`) or explicitly persisted browser states (`.flowstride/sessions/`)—are written strictly to your local file system to mirror the functionality of tools like Postman. _(Note: We strongly recommend adding `.flowstride/` to your `.gitignore` to prevent accidental credential commits)._
|
|
89
|
-
|
|
90
|
-
### Flowstride Enterprise: Secure Cloud Telemetry
|
|
91
|
-
|
|
92
|
-
For teams using Flowstride Enterprise to aggregate multi-flow metrics, we maintain a strict metadata-only boundary.
|
|
93
|
-
|
|
94
|
-
- **Metadata, Not Payloads:** When cloud sync is active, Flowstride routes only execution metadata to your team's dashboard (e.g., Workspace ID, execution duration, total steps, and pass/fail tallies). **Zero bytes** of request headers, bearer tokens, or JSON bodies are ever transmitted to Flowstride Cloud.
|
|
95
|
-
|
|
96
|
-
- **Deep Debugging Stays Local:** The rich, interactive visual dashboard, including the JSON explorer and API Replay tools, runs on a dynamic, locally bound port on your machine. You get the UI of a modern cloud app with the security of a local binary.
|
|
97
|
-
|
|
98
|
-
### CI/CD Leak Protection & Terminal Hygiene
|
|
99
|
-
|
|
100
|
-
Standard testing frameworks do dump failing API requests, and response bodies directly into the terminal so developers can debug them. In a CI/CD environment, this is a massive security liability, as those secrets are permanently burned into plaintext Jenkins, GitHub Actions, or GitLab CI logs.
|
|
101
|
-
|
|
102
|
-
Flowstride completely neutralizes this threat by architecturally separating execution output from diagnostic telemetry:
|
|
102
|
+
## Execution data and Cloud integration
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
Tests run on your machine or CI runner. Native CI writes reports, screenshots,
|
|
105
|
+
videos and attachments to the configured local artifact directory. Interactive
|
|
106
|
+
API diagnostics are available in the local dashboard. These outputs can contain
|
|
107
|
+
application data or credentials; automatic redaction is not guaranteed.
|
|
105
108
|
|
|
106
|
-
|
|
109
|
+
Cloud login/licensing, FlowMail OTP retrieval and explicitly enabled Cloud
|
|
110
|
+
reporting contact their configured services. Native CI leaves Cloud reporting
|
|
111
|
+
off unless `cloudSync: true` is explicitly configured. Existing Cloud reporting
|
|
112
|
+
and storage limitations remain documented in the preview release notes.
|
|
107
113
|
|
|
108
|
-
|
|
114
|
+
Use test credentials, keep runtime state and artifacts out of source control,
|
|
115
|
+
and choose CI artifact access and retention appropriate to the test data.
|
|
109
116
|
|
|
110
117
|
---
|
|
111
118
|
|
package/RELEASE_NOTES.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Flowstride 1.0.20-rc.2 — npm preview
|
|
2
|
+
|
|
3
|
+
Publish channel: `next`. This preview does not move `latest` and does not publish
|
|
4
|
+
or replace the private Pro engine. The matching Pro candidate is `1.0.15-rc.1`.
|
|
5
|
+
|
|
6
|
+
## Changes since rc.1
|
|
7
|
+
|
|
8
|
+
- Require Node.js 22.12.0+ and align package, lockfile, CLI version and guides.
|
|
9
|
+
- Add an explicit preview publication configuration targeting the public npm
|
|
10
|
+
registry and `next`, plus matching CLI-version/engine metadata checks.
|
|
11
|
+
- Clarify preview installation, native CI/game scope and Cloud/Pro limitations.
|
|
12
|
+
- Replace absolute privacy guarantees in the README with the actual data flow.
|
|
13
|
+
- Rebuild the engine from the merged OSS source; reuse the unchanged dashboard
|
|
14
|
+
from the checksum-verified rc.1 archive. No runner or game behaviour changes.
|
|
15
|
+
|
|
16
|
+
## Native features inherited from rc.1
|
|
17
|
+
|
|
18
|
+
`flowstride run --ci`, `flowstride install [--with-deps]`,
|
|
19
|
+
`flowstride ci init github`, supported plugin/game SDKs, canvas/Three.js bridges,
|
|
20
|
+
Chromium desktop/phone/landscape profiles, JSON/JUnit reports and failure media,
|
|
21
|
+
safe file-chooser handling and interrupted-run cleanup.
|
|
22
|
+
|
|
23
|
+
## Known limits
|
|
24
|
+
|
|
25
|
+
Cloud login still downloads Pro 1.0.14, which does not support these native
|
|
26
|
+
commands. Test OSS alone, or authenticate first and then manually install the
|
|
27
|
+
matching local Pro candidate. Project dependency installation can remove a
|
|
28
|
+
no-save Pro package; install Pro afterward. Public-CLI native fallback remains
|
|
29
|
+
available for absent, broken or incompatible Pro engines.
|
|
30
|
+
|
|
31
|
+
Live Cloud reporting and video-storage issues are unresolved. Native CI keeps
|
|
32
|
+
reporting off by default; this preview does not enable the retired managed
|
|
33
|
+
runner. Application-specific game rules/fixtures remain application code.
|
|
34
|
+
Physical phones, Safari/Firefox and other game engines are not qualified.
|
|
35
|
+
|
|
36
|
+
## Publication
|
|
37
|
+
|
|
38
|
+
Only the verified OSS archive should be published. Use the `next` tag explicitly;
|
|
39
|
+
then verify `flowstride@next` and reinstall from npm in a disposable consumer.
|
|
40
|
+
No npm publication is performed while preparing this candidate.
|
|
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.WebAdapter = void 0;
|
|
40
|
+
const playwright_1 = require("playwright");
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
41
42
|
const path = __importStar(require("path"));
|
|
42
43
|
const playwright_extra_1 = require("playwright-extra");
|
|
@@ -44,6 +45,7 @@ const puppeteer_extra_plugin_stealth_1 = __importDefault(require("puppeteer-extr
|
|
|
44
45
|
playwright_extra_1.chromium.use((0, puppeteer_extra_plugin_stealth_1.default)());
|
|
45
46
|
class WebAdapter {
|
|
46
47
|
config;
|
|
48
|
+
pageErrors = [];
|
|
47
49
|
browser = null;
|
|
48
50
|
context = null;
|
|
49
51
|
page = null;
|
|
@@ -75,20 +77,23 @@ class WebAdapter {
|
|
|
75
77
|
}
|
|
76
78
|
this.browser = await playwright_extra_1.chromium.launch({
|
|
77
79
|
headless: this.config.headless,
|
|
80
|
+
// Native CI finalizes videos before closing the browser on cancellation.
|
|
81
|
+
...(this.config.ci ? { handleSIGINT: false, handleSIGTERM: false } : {}),
|
|
78
82
|
args: launchArgs,
|
|
79
83
|
});
|
|
80
84
|
const videoDir = path.join(process.cwd(), ".flowstride", "videos");
|
|
81
85
|
if (!fs.existsSync(videoDir))
|
|
82
86
|
fs.mkdirSync(videoDir, { recursive: true });
|
|
83
|
-
const
|
|
87
|
+
const profile = this.config.device === 'phone' || this.config.device === 'landscape'
|
|
88
|
+
? { ...playwright_1.devices['Pixel 7'], viewport: this.config.device === 'landscape' ? { width: 844, height: 390 } : { width: 390, height: 844 } }
|
|
89
|
+
: { viewport: { width: 1280, height: 800 }, userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' };
|
|
90
|
+
const viewport = this.config.viewport || profile.viewport;
|
|
84
91
|
this.context = await this.browser.newContext({
|
|
85
|
-
viewport
|
|
86
|
-
|
|
87
|
-
recordVideo: {
|
|
88
|
-
dir: videoDir,
|
|
89
|
-
size: { width: 1280, height: 800 },
|
|
90
|
-
},
|
|
92
|
+
...profile, viewport,
|
|
93
|
+
...(this.config.recordVideo === false ? {} : { recordVideo: { dir: videoDir, size: viewport } }),
|
|
91
94
|
});
|
|
95
|
+
this.pageErrors = [];
|
|
96
|
+
this.context.on('page', page => page.on('pageerror', error => this.pageErrors.push(error.message)));
|
|
92
97
|
this.context.on("dialog", async (dialog) => {
|
|
93
98
|
if (this.dialogHandler) {
|
|
94
99
|
await this.dialogHandler(dialog);
|
|
@@ -479,10 +484,10 @@ class WebAdapter {
|
|
|
479
484
|
await targetLoc.evaluate((node) => node.blur());
|
|
480
485
|
let actualValue = "";
|
|
481
486
|
try {
|
|
482
|
-
actualValue = await targetLoc.inputValue({ timeout:
|
|
487
|
+
actualValue = await targetLoc.inputValue({ timeout: this.config.timeout });
|
|
483
488
|
}
|
|
484
489
|
catch {
|
|
485
|
-
actualValue = (await targetLoc.textContent({ timeout:
|
|
490
|
+
actualValue = (await targetLoc.textContent({ timeout: this.config.timeout })) || "";
|
|
486
491
|
}
|
|
487
492
|
const cleanActual = actualValue.replace(/[^a-zA-Z0-9]/g, "");
|
|
488
493
|
const cleanExpected = value.replace(/[^a-zA-Z0-9]/g, "");
|
|
@@ -614,11 +619,10 @@ class WebAdapter {
|
|
|
614
619
|
throw new Error(`File Upload Error: Could not find the file at "${absolutePath}". Please ensure the file exists in your project directory.`);
|
|
615
620
|
}
|
|
616
621
|
try {
|
|
617
|
-
const
|
|
618
|
-
timeout:
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
const fileChooser = await fileChooserPromise;
|
|
622
|
+
const [fileChooser] = await Promise.all([
|
|
623
|
+
this.page.waitForEvent('filechooser', { timeout: this.config.timeout }),
|
|
624
|
+
targetLoc.click({ timeout: this.config.timeout }),
|
|
625
|
+
]);
|
|
622
626
|
await fileChooser.setFiles(absolutePath);
|
|
623
627
|
}
|
|
624
628
|
catch (e) {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isCIEnvironment = isCIEnvironment;
|
|
7
|
+
exports.loadNativeConfig = loadNativeConfig;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
function isCIEnvironment() {
|
|
11
|
+
return ['FLOWSTRIDE_NATIVE_CI', 'CI', 'GITHUB_ACTIONS', 'GITLAB_CI', 'CIRCLECI', 'JENKINS_URL', 'FLOWSTRIDE_API_KEY']
|
|
12
|
+
.some(key => !!process.env[key] && process.env[key] !== 'false' && process.env[key] !== '0');
|
|
13
|
+
}
|
|
14
|
+
function loadNativeConfig(root, options) {
|
|
15
|
+
const file = node_path_1.default.join(root, options.config || 'flowstride.config.json');
|
|
16
|
+
let input = {};
|
|
17
|
+
if (node_fs_1.default.existsSync(file)) {
|
|
18
|
+
input = JSON.parse(node_fs_1.default.readFileSync(file, 'utf8'));
|
|
19
|
+
if (!input || typeof input !== 'object' || Array.isArray(input))
|
|
20
|
+
throw new Error('Flowstride configuration must be an object.');
|
|
21
|
+
}
|
|
22
|
+
else if (options.config)
|
|
23
|
+
throw new Error(`Configuration not found: ${file}`);
|
|
24
|
+
const config = { ...input, timeout: input.timeout ?? 30000, headless: input.headless ?? true,
|
|
25
|
+
ci: !!options.ci || isCIEnvironment(), device: options.device ?? input.device ?? 'desktop',
|
|
26
|
+
artifactsDir: input.artifactsDir ?? 'flowstride-artifacts', testDir: input.testDir ?? 'flows',
|
|
27
|
+
defaultDialogBehavior: input.defaultDialogBehavior ?? 'dismiss',
|
|
28
|
+
recordVideo: input.recordVideo ?? true, browserErrors: input.browserErrors ?? 'fail',
|
|
29
|
+
isCloudSyncActive: options.isCloudSyncActive ?? false, machineToken: options.machineToken ?? null,
|
|
30
|
+
workspaceId: options.workspaceId ?? null, manifest: options.manifest ?? null };
|
|
31
|
+
if (config.ci && input.cloudSync !== true)
|
|
32
|
+
config.isCloudSyncActive = false;
|
|
33
|
+
if (options.headless || config.ci)
|
|
34
|
+
config.headless = true;
|
|
35
|
+
else if (options.headed)
|
|
36
|
+
config.headless = false;
|
|
37
|
+
if (process.env.BASE_URL)
|
|
38
|
+
config.baseUrl = process.env.BASE_URL;
|
|
39
|
+
if (!Number.isSafeInteger(config.timeout) || config.timeout < 1)
|
|
40
|
+
throw new Error('timeout must be a positive integer.');
|
|
41
|
+
if (!['desktop', 'phone', 'landscape'].includes(config.device))
|
|
42
|
+
throw new Error('device must be desktop, phone, or landscape.');
|
|
43
|
+
for (const key of ['headless', 'recordVideo', 'bail'])
|
|
44
|
+
if (config[key] !== undefined && typeof config[key] !== 'boolean')
|
|
45
|
+
throw new Error(`${key} must be boolean.`);
|
|
46
|
+
if (!['fail', 'record'].includes(config.browserErrors))
|
|
47
|
+
throw new Error('browserErrors must be fail or record.');
|
|
48
|
+
for (const key of ['artifactsDir', 'testDir', 'pluginsDir']) {
|
|
49
|
+
if (config[key] === undefined)
|
|
50
|
+
continue;
|
|
51
|
+
if (typeof config[key] !== 'string' || !config[key].trim())
|
|
52
|
+
throw new Error(`${key} must be a nonempty path.`);
|
|
53
|
+
const resolved = node_path_1.default.resolve(root, config[key]);
|
|
54
|
+
const relative = node_path_1.default.relative(root, resolved);
|
|
55
|
+
if ((key === 'artifactsDir' && !relative) || relative === '..' || relative.startsWith(`..${node_path_1.default.sep}`) || node_path_1.default.isAbsolute(relative))
|
|
56
|
+
throw new Error(`${key} must be a directory inside the project.`);
|
|
57
|
+
config[key] = resolved;
|
|
58
|
+
}
|
|
59
|
+
if (config.viewport && (!Number.isSafeInteger(config.viewport.width) || config.viewport.width < 1 || !Number.isSafeInteger(config.viewport.height) || config.viewport.height < 1))
|
|
60
|
+
throw new Error('viewport requires positive integer width and height.');
|
|
61
|
+
if (config.webServer) {
|
|
62
|
+
const server = config.webServer;
|
|
63
|
+
if (typeof server.command !== 'string' || !server.command.trim() || typeof server.url !== 'string')
|
|
64
|
+
throw new Error('webServer requires command and url.');
|
|
65
|
+
const url = new URL(server.url);
|
|
66
|
+
if (!['http:', 'https:'].includes(url.protocol) || !['localhost', '127.0.0.1', '[::1]'].includes(url.hostname))
|
|
67
|
+
throw new Error('webServer readiness URL must use local HTTP(S).');
|
|
68
|
+
if (server.timeout !== undefined && (!Number.isSafeInteger(server.timeout) || server.timeout < 1))
|
|
69
|
+
throw new Error('webServer.timeout must be positive.');
|
|
70
|
+
server.cwd = node_path_1.default.resolve(root, server.cwd || '.');
|
|
71
|
+
const relative = node_path_1.default.relative(root, server.cwd);
|
|
72
|
+
if (relative === '..' || relative.startsWith(`..${node_path_1.default.sep}`) || node_path_1.default.isAbsolute(relative))
|
|
73
|
+
throw new Error('webServer.cwd must be inside the project.');
|
|
74
|
+
}
|
|
75
|
+
return config;
|
|
76
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withDeadline = withDeadline;
|
|
4
|
+
/** Bound asynchronous plugin work while observing late rejections safely. */
|
|
5
|
+
async function withDeadline(operation, timeout) {
|
|
6
|
+
let timer;
|
|
7
|
+
try {
|
|
8
|
+
return await Promise.race([Promise.resolve(operation), new Promise((_resolve, reject) => {
|
|
9
|
+
timer = setTimeout(() => reject(new Error(`Flowstride step exceeded its ${timeout}ms timeout.`)), timeout);
|
|
10
|
+
})]);
|
|
11
|
+
}
|
|
12
|
+
finally {
|
|
13
|
+
if (timer)
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initCI(provider: string): void;
|
package/dist/ci/init.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.initCI = initCI;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
function initCI(provider) {
|
|
11
|
+
if (provider !== 'github')
|
|
12
|
+
throw new Error('Supported provider: github.');
|
|
13
|
+
const file = node_path_1.default.resolve('.github/workflows/flowstride.yml');
|
|
14
|
+
if (node_fs_1.default.existsSync(file))
|
|
15
|
+
throw new Error('Flowstride workflow already exists; it was not overwritten.');
|
|
16
|
+
const artifacts = node_path_1.default.relative(process.cwd(), (0, config_1.loadNativeConfig)(process.cwd(), {}).artifactsDir);
|
|
17
|
+
const parent = node_path_1.default.dirname(file);
|
|
18
|
+
node_fs_1.default.mkdirSync(parent, { recursive: true });
|
|
19
|
+
node_fs_1.default.writeFileSync(file, `name: Flowstride\non:\n push:\n pull_request:\n workflow_dispatch:\npermissions:\n contents: read\njobs:\n test:\n runs-on: ubuntu-latest\n timeout-minutes: 20\n steps:\n - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262\n with:\n persist-credentials: false\n - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020\n with:\n node-version: 24\n cache: npm\n - run: npm ci\n - name: Install matching browser dependencies\n run: npx --no-install flowstride install --with-deps\n - name: Run native Flowstride\n run: npx --no-install flowstride run --ci\n - name: Retain test evidence\n if: always()\n uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02\n with:\n name: flowstride-results\n path: ${JSON.stringify(artifacts + "/")}\n retention-days: 14\n`, { flag: 'wx' });
|
|
20
|
+
console.log(`Created ${file}. Commit it when ready. This template uses the project-locked Flowstride installation.`);
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FlowstrideConfig } from '../types';
|
|
2
|
+
export interface NativeRunResult {
|
|
3
|
+
protocol: 1;
|
|
4
|
+
engine: string;
|
|
5
|
+
status: 'passed' | 'failed' | 'interrupted';
|
|
6
|
+
duration: number;
|
|
7
|
+
artifactsDir: string;
|
|
8
|
+
files: string[];
|
|
9
|
+
steps: any[];
|
|
10
|
+
errors: string[];
|
|
11
|
+
metrics: {
|
|
12
|
+
total: number;
|
|
13
|
+
passed: number;
|
|
14
|
+
failed: number;
|
|
15
|
+
skipped: number;
|
|
16
|
+
incomplete: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function runNativeCI(worker: any, orchestrator: any, target: string, config: FlowstrideConfig, engine: string): Promise<NativeRunResult>;
|