screenci 0.0.69 → 0.0.71
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/README.md +45 -122
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +68 -4
- package/dist/cli.js.map +1 -1
- package/dist/docs/manifest.d.ts +81 -50
- package/dist/docs/manifest.d.ts.map +1 -1
- package/dist/docs/manifest.js +36 -17
- package/dist/docs/manifest.js.map +1 -1
- package/dist/docs/video-sources/installation.screenci.d.ts +2 -0
- package/dist/docs/video-sources/installation.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/{installation.video.js → installation.screenci.js} +1 -1
- package/dist/docs/video-sources/installation.screenci.js.map +1 -0
- package/dist/docs/video-sources/landing.screenci.d.ts +2 -0
- package/dist/docs/video-sources/landing.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/{landing.video.js → landing.screenci.js} +1 -1
- package/dist/docs/video-sources/landing.screenci.js.map +1 -0
- package/dist/docs/video-sources/locator-highlight-animated.screenci.d.ts +2 -0
- package/dist/docs/video-sources/locator-highlight-animated.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/locator-highlight-animated.screenci.js +41 -0
- package/dist/docs/video-sources/locator-highlight-animated.screenci.js.map +1 -0
- package/dist/docs/video-sources/locator-highlight-still.screenci.d.ts +2 -0
- package/dist/docs/video-sources/locator-highlight-still.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/locator-highlight-still.screenci.js +19 -0
- package/dist/docs/video-sources/locator-highlight-still.screenci.js.map +1 -0
- package/dist/docs/video-sources/product-pitch.screenci.d.ts +2 -0
- package/dist/docs/video-sources/product-pitch.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/product-pitch.screenci.js +136 -0
- package/dist/docs/video-sources/product-pitch.screenci.js.map +1 -0
- package/dist/docs/video-sources/public-urls.screenci.d.ts +2 -0
- package/dist/docs/video-sources/public-urls.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/public-urls.screenci.js +34 -0
- package/dist/docs/video-sources/public-urls.screenci.js.map +1 -0
- package/dist/docs/video-sources/studio.screenci.d.ts +2 -0
- package/dist/docs/video-sources/studio.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/{studio.video.js → studio.screenci.js} +1 -1
- package/dist/docs/video-sources/studio.screenci.js.map +1 -0
- package/dist/docs/videos.d.ts +28 -18
- package/dist/docs/videos.d.ts.map +1 -1
- package/dist/docs/videos.js +14 -5
- package/dist/docs/videos.js.map +1 -1
- package/dist/e2e/screenshot.e2e.d.ts +2 -0
- package/dist/e2e/screenshot.e2e.d.ts.map +1 -0
- package/dist/e2e/screenshot.e2e.js +73 -0
- package/dist/e2e/screenshot.e2e.js.map +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/src/asset.d.ts +119 -15
- package/dist/src/asset.d.ts.map +1 -1
- package/dist/src/asset.js +310 -124
- package/dist/src/asset.js.map +1 -1
- package/dist/src/audio.d.ts +14 -0
- package/dist/src/audio.d.ts.map +1 -1
- package/dist/src/audio.js +4 -1
- package/dist/src/audio.js.map +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +9 -3
- package/dist/src/config.js.map +1 -1
- package/dist/src/contextOptions.d.ts +48 -0
- package/dist/src/contextOptions.d.ts.map +1 -0
- package/dist/src/contextOptions.js +71 -0
- package/dist/src/contextOptions.js.map +1 -0
- package/dist/src/crop.d.ts +93 -0
- package/dist/src/crop.d.ts.map +1 -0
- package/dist/src/crop.js +133 -0
- package/dist/src/crop.js.map +1 -0
- package/dist/src/cue.d.ts +17 -4
- package/dist/src/cue.d.ts.map +1 -1
- package/dist/src/cue.js +31 -3
- package/dist/src/cue.js.map +1 -1
- package/dist/src/defaults.d.ts +6 -0
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +6 -0
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/dimensions.d.ts +6 -0
- package/dist/src/dimensions.d.ts.map +1 -1
- package/dist/src/dimensions.js +15 -0
- package/dist/src/dimensions.js.map +1 -1
- package/dist/src/events.d.ts +152 -10
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/events.js +61 -3
- package/dist/src/events.js.map +1 -1
- package/dist/src/htmlRasterizer.d.ts +43 -0
- package/dist/src/htmlRasterizer.d.ts.map +1 -1
- package/dist/src/htmlRasterizer.js +32 -10
- package/dist/src/htmlRasterizer.js.map +1 -1
- package/dist/src/init.d.ts.map +1 -1
- package/dist/src/init.js +19 -8
- package/dist/src/init.js.map +1 -1
- package/dist/src/overlayFlush.d.ts +18 -0
- package/dist/src/overlayFlush.d.ts.map +1 -0
- package/dist/src/overlayFlush.js +56 -0
- package/dist/src/overlayFlush.js.map +1 -0
- package/dist/src/overlayRect.d.ts +69 -0
- package/dist/src/overlayRect.d.ts.map +1 -0
- package/dist/src/overlayRect.js +55 -0
- package/dist/src/overlayRect.js.map +1 -0
- package/dist/src/recording.d.ts +3 -2
- package/dist/src/recording.d.ts.map +1 -1
- package/dist/src/recordingData.d.ts +20 -0
- package/dist/src/recordingData.d.ts.map +1 -1
- package/dist/src/runtimeContext.d.ts +19 -1
- package/dist/src/runtimeContext.d.ts.map +1 -1
- package/dist/src/runtimeContext.js +15 -0
- package/dist/src/runtimeContext.js.map +1 -1
- package/dist/src/screenshot.d.ts +105 -0
- package/dist/src/screenshot.d.ts.map +1 -0
- package/dist/src/screenshot.js +188 -0
- package/dist/src/screenshot.js.map +1 -0
- package/dist/src/stillCapture.d.ts +68 -0
- package/dist/src/stillCapture.d.ts.map +1 -0
- package/dist/src/stillCapture.js +119 -0
- package/dist/src/stillCapture.js.map +1 -0
- package/dist/src/studio.d.ts +8 -11
- package/dist/src/studio.d.ts.map +1 -1
- package/dist/src/studio.js +2 -25
- package/dist/src/studio.js.map +1 -1
- package/dist/src/types.d.ts +100 -8
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/video.d.ts +20 -0
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +71 -24
- package/dist/src/video.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skills/screenci/SKILL.md +10 -10
- package/skills/screenci/references/init.md +1 -1
- package/skills/screenci/references/record.md +2 -2
- package/dist/docs/video-sources/cli.video.d.ts +0 -2
- package/dist/docs/video-sources/cli.video.d.ts.map +0 -1
- package/dist/docs/video-sources/cli.video.js +0 -36
- package/dist/docs/video-sources/cli.video.js.map +0 -1
- package/dist/docs/video-sources/installation.video.d.ts +0 -2
- package/dist/docs/video-sources/installation.video.d.ts.map +0 -1
- package/dist/docs/video-sources/installation.video.js.map +0 -1
- package/dist/docs/video-sources/landing.video.d.ts +0 -2
- package/dist/docs/video-sources/landing.video.d.ts.map +0 -1
- package/dist/docs/video-sources/landing.video.js.map +0 -1
- package/dist/docs/video-sources/studio.video.d.ts +0 -2
- package/dist/docs/video-sources/studio.video.d.ts.map +0 -1
- package/dist/docs/video-sources/studio.video.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,53 +3,45 @@
|
|
|
3
3
|
Your UI changed. Your demo videos didn't. screenci fixes that.
|
|
4
4
|
|
|
5
5
|
Record product walkthroughs as code. When the UI ships, run
|
|
6
|
-
`npx screenci record` and your videos regenerate.
|
|
6
|
+
`npx screenci record` and your videos regenerate. You keep the Playwright
|
|
7
|
+
mental model, but the output is a maintainable product video instead of a
|
|
8
|
+
test report.
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
product video instead of a test report.
|
|
10
|
+
Learn more at [screenci.com](https://screenci.com).
|
|
10
11
|
|
|
11
|
-
##
|
|
12
|
+
## Get started
|
|
12
13
|
|
|
13
14
|
```bash
|
|
14
15
|
npm init screenci@latest
|
|
16
|
+
# or
|
|
15
17
|
pnpm create screenci
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`npm init screenci@latest -- --yes --package-manager pnpm`.
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
screenci/
|
|
27
|
-
screenci.config.ts
|
|
28
|
-
package.json
|
|
29
|
-
package-lock.json # or pnpm-lock.yaml / yarn.lock
|
|
30
|
-
.gitignore
|
|
31
|
-
videos/
|
|
32
|
-
example.video.ts
|
|
33
|
-
.github/workflows/screenci.yaml # at the repo root, scoped to screenci/
|
|
34
|
-
```
|
|
20
|
+
This scaffolds a self-contained `screenci/` directory with its own
|
|
21
|
+
dependencies and installs Chromium. The directory is isolated from the
|
|
22
|
+
surrounding workspace, which keeps installation reliable inside monorepos.
|
|
23
|
+
|
|
24
|
+
Then write a video, run it locally, and record the final output:
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
```bash
|
|
27
|
+
npx screenci test # author the video
|
|
28
|
+
npx screenci record # render and upload the final video
|
|
29
|
+
```
|
|
38
30
|
|
|
39
|
-
|
|
31
|
+
Full docs:
|
|
40
32
|
|
|
41
|
-
- Getting started
|
|
42
|
-
- Writing scripts
|
|
43
|
-
- CLI reference
|
|
33
|
+
- [Getting started](https://screenci.com/docs)
|
|
34
|
+
- [Writing scripts](https://screenci.com/docs/video-script-basics)
|
|
35
|
+
- [CLI reference](https://screenci.com/docs/reference/cli)
|
|
44
36
|
|
|
45
37
|
## Write a video
|
|
46
38
|
|
|
47
|
-
Video scripts are Playwright-style files with a `.
|
|
39
|
+
Video scripts are Playwright-style files with a `.screenci.ts` extension. If you
|
|
48
40
|
already know Playwright locators, navigation, and waiting, you already know
|
|
49
41
|
most of the automation layer.
|
|
50
42
|
|
|
51
43
|
```ts
|
|
52
|
-
// videos/onboarding.
|
|
44
|
+
// videos/onboarding.screenci.ts
|
|
53
45
|
import { hide, speed, time, video } from 'screenci'
|
|
54
46
|
|
|
55
47
|
video('Onboarding flow', async ({ page }) => {
|
|
@@ -70,101 +62,32 @@ video('Onboarding flow', async ({ page }) => {
|
|
|
70
62
|
```
|
|
71
63
|
|
|
72
64
|
Each `video()` call becomes one output video. The title becomes the filename
|
|
73
|
-
and the remote video identity.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
cursor movement and visible typing layered on top of normal Playwright
|
|
77
|
-
behavior.
|
|
65
|
+
and the remote video identity. Inside `video()`, `page` is a `ScreenCIPage`: a
|
|
66
|
+
Playwright `Page` with animated cursor movement and visible typing layered on
|
|
67
|
+
top of normal Playwright behavior.
|
|
78
68
|
|
|
79
69
|
`hide()` removes setup entirely. `speed()` and `time()` keep a section visible
|
|
80
|
-
but remap its rendered duration.
|
|
81
|
-
playback speed.
|
|
82
|
-
|
|
83
|
-
## Run locally
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
npx screenci test
|
|
87
|
-
npx screenci test --ui
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Use `test` for the normal authoring loop. It runs the video scripts through
|
|
91
|
-
Playwright without starting the final recording and upload path.
|
|
92
|
-
|
|
93
|
-
## Record the final output
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
npx screenci record
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
On the first run without `SCREENCI_SECRET` in an interactive terminal, `record`
|
|
100
|
-
prints a one-time ScreenCI link, waits for you to finish sign-in in the browser,
|
|
101
|
-
saves the secret into the project env file, and then continues. In a
|
|
102
|
-
non-interactive session (no terminal, or `SCREENCI_NONINTERACTIVE=1`) `record`
|
|
103
|
-
does not wait: it prints the sign-in link and exits, so you can open the link,
|
|
104
|
-
sign in, choose a plan, and rerun `record`, which then detects the completed
|
|
105
|
-
session and continues. Set `SCREENCI_SECRET` ahead of time to skip sign-in
|
|
106
|
-
entirely. Pending auth state is cached in `.screenci/link-session.json`, so
|
|
107
|
-
rerunning `record` reuses the same link until it expires or completes. Recorded
|
|
108
|
-
artifacts still live in `.screenci/<video-name>/`.
|
|
109
|
-
|
|
110
|
-
## Configure
|
|
111
|
-
|
|
112
|
-
```ts
|
|
113
|
-
// screenci.config.ts
|
|
114
|
-
import { defineConfig } from 'screenci'
|
|
115
|
-
|
|
116
|
-
export default defineConfig({
|
|
117
|
-
projectName: 'my-project',
|
|
118
|
-
envFile: '.env',
|
|
119
|
-
videoDir: './videos',
|
|
120
|
-
use: {
|
|
121
|
-
baseURL: 'https://app.example.com',
|
|
122
|
-
recordOptions: {
|
|
123
|
-
aspectRatio: '16:9',
|
|
124
|
-
quality: '1080p',
|
|
125
|
-
fps: 60,
|
|
126
|
-
},
|
|
127
|
-
trace: 'retain-on-failure',
|
|
128
|
-
},
|
|
129
|
-
projects: [{ name: 'chromium' }],
|
|
130
|
-
})
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
ScreenCI manages `testDir`, `testMatch`, and `retries` for you. Most other
|
|
134
|
-
Playwright config still passes through.
|
|
135
|
-
|
|
136
|
-
If you keep local runtime secrets in an env file, point `envFile` at it or use
|
|
137
|
-
the project `.env`. ScreenCI loads that file automatically for CLI commands.
|
|
138
|
-
That is also the right place for BYOK-style secrets such as
|
|
139
|
-
`ELEVENLABS_API_KEY`. ScreenCI does not store raw API keys from your env file.
|
|
70
|
+
but remap its rendered duration.
|
|
140
71
|
|
|
141
72
|
## Authoring helpers
|
|
142
73
|
|
|
143
|
-
| Export | What it does
|
|
144
|
-
| ----------------- |
|
|
145
|
-
| `defineConfig` | Wraps Playwright config with ScreenCI defaults
|
|
146
|
-
| `video` | Declares a video recording test
|
|
147
|
-
| `createNarration` | Creates typed narration controllers
|
|
148
|
-
| `hide` | Cuts setup or cleanup out of the visible recording
|
|
149
|
-
| `autoZoom` | Smooth camera follow for an interaction block
|
|
150
|
-
| `zoomTo` | Manual camera framing for a locator or point
|
|
151
|
-
| `resetZoom` | Returns from manual framing to the full viewport
|
|
152
|
-
| `createOverlays` | Inserts timed media overlays into the recording timeline
|
|
153
|
-
| `voices` | Available voice constants such as `voices.Ava`
|
|
154
|
-
| `modelTypes` | Narration model constants
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
When `SCREENCI_SECRET` is configured, `screenci record` uploads the output to
|
|
166
|
-
ScreenCI for rendering, narration generation, and hosted delivery.
|
|
167
|
-
|
|
168
|
-
For narration authoring, keep cues short and usually one sentence at a time.
|
|
169
|
-
That makes overlap timing easier to manage and should reduce TTS regeneration
|
|
170
|
-
cost when only one line changes.
|
|
74
|
+
| Export | What it does |
|
|
75
|
+
| ----------------- | -------------------------------------------------------- |
|
|
76
|
+
| `defineConfig` | Wraps Playwright config with ScreenCI defaults |
|
|
77
|
+
| `video` | Declares a video recording test |
|
|
78
|
+
| `createNarration` | Creates typed narration controllers |
|
|
79
|
+
| `hide` | Cuts setup or cleanup out of the visible recording |
|
|
80
|
+
| `autoZoom` | Smooth camera follow for an interaction block |
|
|
81
|
+
| `zoomTo` | Manual camera framing for a locator or point |
|
|
82
|
+
| `resetZoom` | Returns from manual framing to the full viewport |
|
|
83
|
+
| `createOverlays` | Inserts timed media overlays into the recording timeline |
|
|
84
|
+
| `voices` | Available voice constants such as `voices.Ava` |
|
|
85
|
+
| `modelTypes` | Narration model constants |
|
|
86
|
+
|
|
87
|
+
See the [docs](https://screenci.com/docs) for configuration, narration,
|
|
88
|
+
camera, and CI setup.
|
|
89
|
+
|
|
90
|
+
## Community
|
|
91
|
+
|
|
92
|
+
Questions, ideas, or want to show off your videos? Join us on
|
|
93
|
+
[Discord](https://discord.gg/DyjSRFzeBc).
|
package/dist/cli.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ export declare function extractConfigStringLiteral(configSource: string, propert
|
|
|
69
69
|
export declare function extractRecordUploadPolicyLiteral(configSource: string): RecordUploadPolicy | undefined;
|
|
70
70
|
export declare function extractMockRecordLiteral(configSource: string): boolean | undefined;
|
|
71
71
|
export declare function getConfigModuleSpecifier(resolvedConfigPath: string): string;
|
|
72
|
+
export declare function extractGrep(args: string[]): string | undefined;
|
|
72
73
|
export declare function ensureScreenciSecret(resolvedConfigPath?: string, opts?: {
|
|
73
74
|
interactive?: boolean;
|
|
74
75
|
pollAuth?: boolean;
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EACV,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAA;AAMxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,gBAAgB,CAAA;AAKxE,OAAO,EAGL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EAOlB,MAAM,sBAAsB,CAAA;AAQ7B,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAA;AAqBvE,KAAK,yBAAyB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAChC,MAAM,CAAC,EAAE,yBAAyB,EAAE,CAAA;CACrC,CAAA;AAUD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,MAAM,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAmB,EAC5C,KAAK,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAkB,GACzC,OAAO,CAIT;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,yBAAyB,EAAE,GAC3C,MAAM,EAAE,CAiBV;AAiND,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AASD,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAAA;AAEjE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAER;AAED,KAAK,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAA;AAkE9C,wBAAsB,eAAe,CAAC,CAAC,EACrC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,WAAW,SAAI,GACd,OAAO,CAAC,CAAC,CAAC,CAWZ;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -342,7 +342,15 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
|
|
|
342
342
|
let plan = null;
|
|
343
343
|
try {
|
|
344
344
|
uploadAbort.throwIfAborted();
|
|
345
|
-
|
|
345
|
+
// A screenshot recording uploads its raw page capture (always a PNG) through
|
|
346
|
+
// the same recording endpoint a video uses; the renderer reads those bytes as
|
|
347
|
+
// the capture. Videos upload recording.mp4. Output kind defaults to 'video'.
|
|
348
|
+
const isScreenshot = data.output === 'screenshot';
|
|
349
|
+
const recordingFileName = isScreenshot
|
|
350
|
+
? (data.screenshot?.path ?? 'screenshot.png')
|
|
351
|
+
: 'recording.mp4';
|
|
352
|
+
const recordingContentType = isScreenshot ? 'image/png' : 'video/mp4';
|
|
353
|
+
const recordingPath = resolve(screenciDir, entry, recordingFileName);
|
|
346
354
|
if (!existsSync(recordingPath)) {
|
|
347
355
|
progressReporter.complete(progressIndex, 'failure');
|
|
348
356
|
return {
|
|
@@ -350,7 +358,7 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
|
|
|
350
358
|
videoId: null,
|
|
351
359
|
hadFailure: true,
|
|
352
360
|
videoName,
|
|
353
|
-
failureMessage: `Missing
|
|
361
|
+
failureMessage: `Missing ${recordingFileName} for "${videoName}"`,
|
|
354
362
|
recordId,
|
|
355
363
|
};
|
|
356
364
|
}
|
|
@@ -408,7 +416,7 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
|
|
|
408
416
|
uploadAbort.throwIfAborted();
|
|
409
417
|
const fileStat = await stat(recordingPath);
|
|
410
418
|
if (verbose) {
|
|
411
|
-
logger.info(`Uploading
|
|
419
|
+
logger.info(`Uploading ${recordingFileName} size=${(fileStat.size / 1024 / 1024).toFixed(1)}MB`);
|
|
412
420
|
}
|
|
413
421
|
const recordingResponse = await withUploadRetry(async () => {
|
|
414
422
|
const stream = createReadStream(recordingPath);
|
|
@@ -420,7 +428,7 @@ async function uploadRecordingCandidate(candidate, screenciDir, projectName, api
|
|
|
420
428
|
return await fetch(`${apiUrl}/cli/upload/${recordingId}/recording`, {
|
|
421
429
|
method: 'PUT',
|
|
422
430
|
headers: {
|
|
423
|
-
'Content-Type':
|
|
431
|
+
'Content-Type': recordingContentType,
|
|
424
432
|
'Content-Length': String(fileStat.size),
|
|
425
433
|
'X-ScreenCI-Secret': secret,
|
|
426
434
|
...(elevenLabsApiKey
|
|
@@ -1417,6 +1425,49 @@ async function updateVideoVisibility(videoId, isPublic, configPath) {
|
|
|
1417
1425
|
}
|
|
1418
1426
|
logger.info(`${isPublic ? 'Made public' : 'Made private'}: ${videoId}`);
|
|
1419
1427
|
}
|
|
1428
|
+
// Extract a `--grep <value>` / `--grep=<value>` from the pass-through args so a
|
|
1429
|
+
// remote trigger can forward it as a filter (records only matching videos).
|
|
1430
|
+
export function extractGrep(args) {
|
|
1431
|
+
for (let i = 0; i < args.length; i++) {
|
|
1432
|
+
const arg = args[i];
|
|
1433
|
+
if (arg === undefined)
|
|
1434
|
+
continue;
|
|
1435
|
+
if (arg === '--grep' || arg === '-g') {
|
|
1436
|
+
return args[i + 1];
|
|
1437
|
+
}
|
|
1438
|
+
if (arg.startsWith('--grep=')) {
|
|
1439
|
+
return arg.slice('--grep='.length);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return undefined;
|
|
1443
|
+
}
|
|
1444
|
+
// `screenci record --remote` triggers the project's GitHub Actions recording
|
|
1445
|
+
// workflow instead of recording locally. The project is resolved from the
|
|
1446
|
+
// existing SCREENCI_SECRET + config `projectName`, exactly like the other
|
|
1447
|
+
// authenticated commands; the backend dispatches the workflow using the GitHub
|
|
1448
|
+
// token stored for the project. An optional `--grep` records only matching
|
|
1449
|
+
// videos/screenshots.
|
|
1450
|
+
async function triggerRemoteRun(configPath, grep) {
|
|
1451
|
+
const { screenciConfig, secret, apiUrl } = await requireScreenCISecret(configPath, { interactive: detectInteractiveSession() });
|
|
1452
|
+
const res = await fetch(`${apiUrl}/cli/trigger-run`, {
|
|
1453
|
+
method: 'POST',
|
|
1454
|
+
headers: {
|
|
1455
|
+
'X-ScreenCI-Secret': secret,
|
|
1456
|
+
'Content-Type': 'application/json',
|
|
1457
|
+
},
|
|
1458
|
+
body: JSON.stringify({
|
|
1459
|
+
projectName: screenciConfig.projectName,
|
|
1460
|
+
...(grep ? { grep } : {}),
|
|
1461
|
+
}),
|
|
1462
|
+
});
|
|
1463
|
+
if (!res.ok) {
|
|
1464
|
+
const text = await res.text();
|
|
1465
|
+
throw new Error(`Failed to trigger remote run: ${res.status} ${text}${hint401(res.status, secret)}`);
|
|
1466
|
+
}
|
|
1467
|
+
logger.info(grep
|
|
1468
|
+
? `Triggered the remote recording workflow for "${screenciConfig.projectName}" (filter: ${grep}).`
|
|
1469
|
+
: `Triggered the remote recording workflow for "${screenciConfig.projectName}".`);
|
|
1470
|
+
}
|
|
1420
1471
|
function getLastRecordFilePath(screenciDir) {
|
|
1421
1472
|
return resolve(screenciDir, SCREENCI_LAST_RECORD_FILE);
|
|
1422
1473
|
}
|
|
@@ -1669,9 +1720,17 @@ export async function main() {
|
|
|
1669
1720
|
.description('Record videos using Playwright')
|
|
1670
1721
|
.option('-v, --verbose', 'verbose output')
|
|
1671
1722
|
.option('--poll-auth', 'wait for sign-in to complete (polling every 5s, up to 5 minutes) instead of exiting, then continue recording')
|
|
1723
|
+
.option('--remote', 'trigger the GitHub Actions recording workflow for this project remotely instead of recording locally')
|
|
1672
1724
|
.allowUnknownOption(true)
|
|
1673
1725
|
.action(async () => {
|
|
1674
1726
|
const parsed = parseRecordCliArgs(getSubcommandArgv('record'));
|
|
1727
|
+
// `--remote` is a pure dispatch: it fires the project's GitHub Actions
|
|
1728
|
+
// recording workflow and exits, so there is no local Playwright run. A
|
|
1729
|
+
// pass-through `--grep` becomes the remote recording filter.
|
|
1730
|
+
if (parsed.remote) {
|
|
1731
|
+
await triggerRemoteRun(parsed.configPath, extractGrep(parsed.otherArgs));
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1675
1734
|
let playwrightFailure = null;
|
|
1676
1735
|
try {
|
|
1677
1736
|
await run('record', parsed.otherArgs, parsed.configPath, parsed.verbose, false, parsed.pollAuth);
|
|
@@ -1911,6 +1970,7 @@ function parseRecordCliArgs(args) {
|
|
|
1911
1970
|
let configPath;
|
|
1912
1971
|
let verbose = false;
|
|
1913
1972
|
let pollAuth = false;
|
|
1973
|
+
let remote = false;
|
|
1914
1974
|
const otherArgs = [];
|
|
1915
1975
|
for (let i = 0; i < args.length; i++) {
|
|
1916
1976
|
const arg = args[i];
|
|
@@ -1931,6 +1991,9 @@ function parseRecordCliArgs(args) {
|
|
|
1931
1991
|
else if (arg === '--poll-auth') {
|
|
1932
1992
|
pollAuth = true;
|
|
1933
1993
|
}
|
|
1994
|
+
else if (arg === '--remote') {
|
|
1995
|
+
remote = true;
|
|
1996
|
+
}
|
|
1934
1997
|
else {
|
|
1935
1998
|
otherArgs.push(arg);
|
|
1936
1999
|
}
|
|
@@ -1939,6 +2002,7 @@ function parseRecordCliArgs(args) {
|
|
|
1939
2002
|
configPath,
|
|
1940
2003
|
verbose,
|
|
1941
2004
|
pollAuth,
|
|
2005
|
+
remote,
|
|
1942
2006
|
otherArgs,
|
|
1943
2007
|
};
|
|
1944
2008
|
}
|