screenci 0.0.70 → 0.0.72
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 +13 -13
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +184 -5
- package/dist/cli.js.map +1 -1
- package/dist/docs/manifest.d.ts +34 -34
- package/dist/docs/manifest.d.ts.map +1 -1
- package/dist/docs/manifest.js +20 -12
- package/dist/docs/manifest.js.map +1 -1
- package/dist/docs/video-sources/installation.screenci.js +13 -12
- package/dist/docs/video-sources/installation.screenci.js.map +1 -1
- package/dist/docs/video-sources/landing.screenci.js +16 -9
- package/dist/docs/video-sources/landing.screenci.js.map +1 -1
- package/dist/docs/video-sources/locator-highlight-animated.screenci.js +14 -26
- package/dist/docs/video-sources/locator-highlight-animated.screenci.js.map +1 -1
- package/dist/docs/video-sources/locator-highlight-still.screenci.js +5 -10
- package/dist/docs/video-sources/locator-highlight-still.screenci.js.map +1 -1
- package/dist/docs/video-sources/product-pitch.screenci.js +97 -32
- package/dist/docs/video-sources/product-pitch.screenci.js.map +1 -1
- package/dist/docs/video-sources/public-urls.screenci.js +16 -9
- package/dist/docs/video-sources/public-urls.screenci.js.map +1 -1
- package/dist/docs/video-sources/studio.screenci.js +42 -27
- package/dist/docs/video-sources/studio.screenci.js.map +1 -1
- package/dist/docs/videos.d.ts +4 -15
- package/dist/docs/videos.d.ts.map +1 -1
- package/dist/docs/videos.js +6 -22
- package/dist/docs/videos.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/src/asset.d.ts +8 -23
- package/dist/src/asset.d.ts.map +1 -1
- package/dist/src/asset.js +10 -32
- package/dist/src/asset.js.map +1 -1
- package/dist/src/audio.d.ts +6 -24
- package/dist/src/audio.d.ts.map +1 -1
- package/dist/src/audio.js +10 -35
- package/dist/src/audio.js.map +1 -1
- package/dist/src/builder.d.ts +214 -0
- package/dist/src/builder.d.ts.map +1 -0
- package/dist/src/builder.js +212 -0
- package/dist/src/builder.js.map +1 -0
- package/dist/src/cue.d.ts +40 -123
- package/dist/src/cue.d.ts.map +1 -1
- package/dist/src/cue.js +190 -30
- package/dist/src/cue.js.map +1 -1
- package/dist/src/events.d.ts +80 -15
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/events.js +72 -6
- package/dist/src/events.js.map +1 -1
- package/dist/src/init.d.ts.map +1 -1
- package/dist/src/init.js +26 -14
- package/dist/src/init.js.map +1 -1
- package/dist/src/locales.d.ts +19 -0
- package/dist/src/locales.d.ts.map +1 -0
- package/dist/src/locales.js +60 -0
- package/dist/src/locales.js.map +1 -0
- package/dist/src/localize.d.ts +135 -0
- package/dist/src/localize.d.ts.map +1 -0
- package/dist/src/localize.js +195 -0
- package/dist/src/localize.js.map +1 -0
- package/dist/src/localizeRuntime.d.ts +53 -0
- package/dist/src/localizeRuntime.d.ts.map +1 -0
- package/dist/src/localizeRuntime.js +79 -0
- package/dist/src/localizeRuntime.js.map +1 -0
- package/dist/src/recordingData.d.ts +5 -4
- package/dist/src/recordingData.d.ts.map +1 -1
- package/dist/src/runtimeContext.d.ts +3 -4
- package/dist/src/runtimeContext.d.ts.map +1 -1
- package/dist/src/runtimeContext.js.map +1 -1
- package/dist/src/runtimeMode.d.ts +47 -0
- package/dist/src/runtimeMode.d.ts.map +1 -1
- package/dist/src/runtimeMode.js +124 -0
- package/dist/src/runtimeMode.js.map +1 -1
- package/dist/src/screenshot.d.ts +56 -2
- package/dist/src/screenshot.d.ts.map +1 -1
- package/dist/src/screenshot.js +47 -10
- package/dist/src/screenshot.js.map +1 -1
- package/dist/src/stillCapture.d.ts +2 -3
- package/dist/src/stillCapture.d.ts.map +1 -1
- package/dist/src/stillCapture.js.map +1 -1
- package/dist/src/studio.d.ts +55 -22
- package/dist/src/studio.d.ts.map +1 -1
- package/dist/src/studio.js +38 -12
- package/dist/src/studio.js.map +1 -1
- package/dist/src/types.d.ts +18 -5
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/video.d.ts +149 -13
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +91 -24
- package/dist/src/video.js.map +1 -1
- package/dist/src/voiceConfig.d.ts +110 -0
- package/dist/src/voiceConfig.d.ts.map +1 -0
- package/dist/src/voiceConfig.js +2 -0
- package/dist/src/voiceConfig.js.map +1 -0
- package/dist/src/voices.d.ts +27 -23
- package/dist/src/voices.d.ts.map +1 -1
- package/dist/src/voices.js +7 -4
- package/dist/src/voices.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skills/screenci/SKILL.md +39 -30
- package/dist/docs/video-sources/cli.screenci.d.ts +0 -2
- package/dist/docs/video-sources/cli.screenci.d.ts.map +0 -1
- package/dist/docs/video-sources/cli.screenci.js +0 -36
- package/dist/docs/video-sources/cli.screenci.js.map +0 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ npx screenci record # render and upload the final video
|
|
|
31
31
|
Full docs:
|
|
32
32
|
|
|
33
33
|
- [Getting started](https://screenci.com/docs)
|
|
34
|
-
- [Writing scripts](https://screenci.com/docs/
|
|
34
|
+
- [Writing scripts](https://screenci.com/docs/video-script-basics)
|
|
35
35
|
- [CLI reference](https://screenci.com/docs/reference/cli)
|
|
36
36
|
|
|
37
37
|
## Write a video
|
|
@@ -71,18 +71,18 @@ but remap its rendered duration.
|
|
|
71
71
|
|
|
72
72
|
## Authoring helpers
|
|
73
73
|
|
|
74
|
-
| Export
|
|
75
|
-
|
|
|
76
|
-
| `defineConfig`
|
|
77
|
-
| `video`
|
|
78
|
-
| `
|
|
79
|
-
| `hide`
|
|
80
|
-
| `autoZoom`
|
|
81
|
-
| `zoomTo`
|
|
82
|
-
| `resetZoom`
|
|
83
|
-
| `createOverlays`
|
|
84
|
-
| `voices`
|
|
85
|
-
| `modelTypes`
|
|
74
|
+
| Export | What it does |
|
|
75
|
+
| ---------------- | ------------------------------------------------------------ |
|
|
76
|
+
| `defineConfig` | Wraps Playwright config with ScreenCI defaults |
|
|
77
|
+
| `video` | Declares a video recording test |
|
|
78
|
+
| `video.localize` | Declares localized narration / text and records per language |
|
|
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
86
|
|
|
87
87
|
See the [docs](https://screenci.com/docs) for configuration, narration,
|
|
88
88
|
camera, and CI setup.
|
package/dist/cli.d.ts
CHANGED
|
@@ -69,10 +69,19 @@ 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;
|
|
75
76
|
}): Promise<string | undefined>;
|
|
76
77
|
export declare function main(): Promise<void>;
|
|
78
|
+
export declare function parseRecordCliArgs(args: string[]): {
|
|
79
|
+
configPath: string | undefined;
|
|
80
|
+
verbose: boolean;
|
|
81
|
+
pollAuth: boolean;
|
|
82
|
+
remote: boolean;
|
|
83
|
+
languages: string | undefined;
|
|
84
|
+
otherArgs: string[];
|
|
85
|
+
};
|
|
77
86
|
export declare function logCliError(error: unknown): void;
|
|
78
87
|
//# sourceMappingURL=cli.d.ts.map
|
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;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Command, CommanderError } from 'commander';
|
|
|
10
10
|
import pc from 'picocolors';
|
|
11
11
|
import { logger } from './src/logger.js';
|
|
12
12
|
import { determinePackageManager, parsePackageManager, runInit, } from './src/init.js';
|
|
13
|
-
import { SCREENCI_DISABLE_RECORDING_TIMINGS_ENV, SCREENCI_MOCK_RECORD_ENV, } from './src/runtimeMode.js';
|
|
13
|
+
import { SCREENCI_DISABLE_RECORDING_TIMINGS_ENV, SCREENCI_MOCK_RECORD_ENV, SCREENCI_LANGUAGES_ENV, SCREENCI_TEXT_OVERRIDES_ENV, SCREENCI_RECORD_OPTIONS_ENV, } from './src/runtimeMode.js';
|
|
14
14
|
import { DEFAULT_RECORD_UPLOAD_POLICY } from './src/defaults.js';
|
|
15
15
|
import { findDuplicateTitles, formatDuplicateTitlesMessage, } from './src/titleValidation.js';
|
|
16
16
|
import { createLinkSessionSpec, deletePersistedLinkSessionSpec, getCliLinkSessionApiUrl, getDevBackendUrl, getDevFrontendUrl, getLinkSessionFilePath, getScreenCIEnvironment, isStoredLinkSessionReusable, readPersistedLinkSessionSpec, SCREENCI_LINK_SESSION_FILE, writePersistedLinkSessionSpec, } from './src/linkSession.js';
|
|
@@ -1004,7 +1004,7 @@ export function formatFailedVideoMessage(videoName, message) {
|
|
|
1004
1004
|
if (message.startsWith(EXPRESSIVE_TIER_ERROR_PREFIX)) {
|
|
1005
1005
|
return [
|
|
1006
1006
|
`${videoName}: ${message}`,
|
|
1007
|
-
"If you want to keep using the current tier, remove `voice.style` or `modelType: 'expressive'` from `
|
|
1007
|
+
"If you want to keep using the current tier, remove `voice.style` or `modelType: 'expressive'` from the localize `voice`.",
|
|
1008
1008
|
].join('\n');
|
|
1009
1009
|
}
|
|
1010
1010
|
return `${videoName}: ${message}`;
|
|
@@ -1425,6 +1425,54 @@ async function updateVideoVisibility(videoId, isPublic, configPath) {
|
|
|
1425
1425
|
}
|
|
1426
1426
|
logger.info(`${isPublic ? 'Made public' : 'Made private'}: ${videoId}`);
|
|
1427
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, languages) {
|
|
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
|
+
...(languages ? { languages } : {}),
|
|
1462
|
+
}),
|
|
1463
|
+
});
|
|
1464
|
+
if (!res.ok) {
|
|
1465
|
+
const text = await res.text();
|
|
1466
|
+
throw new Error(`Failed to trigger remote run: ${res.status} ${text}${hint401(res.status, secret)}`);
|
|
1467
|
+
}
|
|
1468
|
+
const filters = [
|
|
1469
|
+
...(grep ? [`filter: ${grep}`] : []),
|
|
1470
|
+
...(languages ? [`languages: ${languages}`] : []),
|
|
1471
|
+
];
|
|
1472
|
+
logger.info(filters.length > 0
|
|
1473
|
+
? `Triggered the remote recording workflow for "${screenciConfig.projectName}" (${filters.join(', ')}).`
|
|
1474
|
+
: `Triggered the remote recording workflow for "${screenciConfig.projectName}".`);
|
|
1475
|
+
}
|
|
1428
1476
|
function getLastRecordFilePath(screenciDir) {
|
|
1429
1477
|
return resolve(screenciDir, SCREENCI_LAST_RECORD_FILE);
|
|
1430
1478
|
}
|
|
@@ -1677,12 +1725,22 @@ export async function main() {
|
|
|
1677
1725
|
.description('Record videos using Playwright')
|
|
1678
1726
|
.option('-v, --verbose', 'verbose output')
|
|
1679
1727
|
.option('--poll-auth', 'wait for sign-in to complete (polling every 5s, up to 5 minutes) instead of exiting, then continue recording')
|
|
1728
|
+
.option('--remote', 'trigger the GitHub Actions recording workflow for this project remotely instead of recording locally')
|
|
1729
|
+
.option('--languages <langs>', 'record/render only these languages (comma-separated, e.g. fi,en)')
|
|
1680
1730
|
.allowUnknownOption(true)
|
|
1681
1731
|
.action(async () => {
|
|
1682
1732
|
const parsed = parseRecordCliArgs(getSubcommandArgv('record'));
|
|
1733
|
+
// `--remote` is a pure dispatch: it fires the project's GitHub Actions
|
|
1734
|
+
// recording workflow and exits, so there is no local Playwright run. A
|
|
1735
|
+
// pass-through `--grep` becomes the remote recording filter, and
|
|
1736
|
+
// `--languages` limits which language versions are recorded.
|
|
1737
|
+
if (parsed.remote) {
|
|
1738
|
+
await triggerRemoteRun(parsed.configPath, extractGrep(parsed.otherArgs), parsed.languages);
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1683
1741
|
let playwrightFailure = null;
|
|
1684
1742
|
try {
|
|
1685
|
-
await run('record', parsed.otherArgs, parsed.configPath, parsed.verbose, false, parsed.pollAuth);
|
|
1743
|
+
await run('record', parsed.otherArgs, parsed.configPath, parsed.verbose, false, parsed.pollAuth, parsed.languages);
|
|
1686
1744
|
}
|
|
1687
1745
|
catch (error) {
|
|
1688
1746
|
if (!(error instanceof Error))
|
|
@@ -1915,10 +1973,12 @@ function getSubcommandArgv(command) {
|
|
|
1915
1973
|
const commandIndex = argv.indexOf(command);
|
|
1916
1974
|
return commandIndex === -1 ? [] : argv.slice(commandIndex + 1);
|
|
1917
1975
|
}
|
|
1918
|
-
function parseRecordCliArgs(args) {
|
|
1976
|
+
export function parseRecordCliArgs(args) {
|
|
1919
1977
|
let configPath;
|
|
1920
1978
|
let verbose = false;
|
|
1921
1979
|
let pollAuth = false;
|
|
1980
|
+
let remote = false;
|
|
1981
|
+
let languages;
|
|
1922
1982
|
const otherArgs = [];
|
|
1923
1983
|
for (let i = 0; i < args.length; i++) {
|
|
1924
1984
|
const arg = args[i];
|
|
@@ -1933,12 +1993,29 @@ function parseRecordCliArgs(args) {
|
|
|
1933
1993
|
configPath = nextArg;
|
|
1934
1994
|
i++;
|
|
1935
1995
|
}
|
|
1996
|
+
else if (arg === '--languages' || arg === '--language') {
|
|
1997
|
+
// screenci-only flag: parsed out so it is not forwarded to Playwright.
|
|
1998
|
+
const nextArg = args[i + 1];
|
|
1999
|
+
if (nextArg === undefined) {
|
|
2000
|
+
logger.error('Error: --languages requires a comma-separated value');
|
|
2001
|
+
process.exit(1);
|
|
2002
|
+
}
|
|
2003
|
+
languages = nextArg;
|
|
2004
|
+
i++;
|
|
2005
|
+
}
|
|
2006
|
+
else if (arg.startsWith('--languages=') ||
|
|
2007
|
+
arg.startsWith('--language=')) {
|
|
2008
|
+
languages = arg.slice(arg.indexOf('=') + 1);
|
|
2009
|
+
}
|
|
1936
2010
|
else if (arg === '--verbose' || arg === '-v') {
|
|
1937
2011
|
verbose = true;
|
|
1938
2012
|
}
|
|
1939
2013
|
else if (arg === '--poll-auth') {
|
|
1940
2014
|
pollAuth = true;
|
|
1941
2015
|
}
|
|
2016
|
+
else if (arg === '--remote') {
|
|
2017
|
+
remote = true;
|
|
2018
|
+
}
|
|
1942
2019
|
else {
|
|
1943
2020
|
otherArgs.push(arg);
|
|
1944
2021
|
}
|
|
@@ -1947,6 +2024,8 @@ function parseRecordCliArgs(args) {
|
|
|
1947
2024
|
configPath,
|
|
1948
2025
|
verbose,
|
|
1949
2026
|
pollAuth,
|
|
2027
|
+
remote,
|
|
2028
|
+
languages,
|
|
1950
2029
|
otherArgs,
|
|
1951
2030
|
};
|
|
1952
2031
|
}
|
|
@@ -1997,7 +2076,85 @@ function validateArgs(args) {
|
|
|
1997
2076
|
}
|
|
1998
2077
|
}
|
|
1999
2078
|
}
|
|
2000
|
-
|
|
2079
|
+
/**
|
|
2080
|
+
* Fetch the project's current Studio text-field overrides so they can be
|
|
2081
|
+
* injected as SCREENCI_TEXT_OVERRIDES before a recording. On-screen `text`
|
|
2082
|
+
* fields render during the recording (they cannot be patched at render time),
|
|
2083
|
+
* so any Studio edits must be present here; code-declared seeds are the
|
|
2084
|
+
* fallback. Best-effort: any failure returns an empty env so the SDK uses the
|
|
2085
|
+
* code seeds, and a recording is never blocked by this fetch.
|
|
2086
|
+
*/
|
|
2087
|
+
async function fetchTextOverridesEnv(configPath, languages, verbose) {
|
|
2088
|
+
try {
|
|
2089
|
+
const { screenciConfig, secret, apiUrl } = await requireScreenCISecret(configPath, { interactive: false });
|
|
2090
|
+
const params = new URLSearchParams({
|
|
2091
|
+
projectName: screenciConfig.projectName,
|
|
2092
|
+
});
|
|
2093
|
+
if (languages)
|
|
2094
|
+
params.set('languages', languages);
|
|
2095
|
+
const res = await fetch(`${apiUrl}/cli/text-overrides?${params.toString()}`, { headers: { 'X-ScreenCI-Secret': secret } });
|
|
2096
|
+
if (!res.ok) {
|
|
2097
|
+
if (verbose) {
|
|
2098
|
+
logger.warn(`Could not fetch Studio text overrides (${res.status}); using code-declared values.`);
|
|
2099
|
+
}
|
|
2100
|
+
return {};
|
|
2101
|
+
}
|
|
2102
|
+
const body = (await res.json());
|
|
2103
|
+
const overrides = body.overrides;
|
|
2104
|
+
if (overrides === undefined ||
|
|
2105
|
+
overrides === null ||
|
|
2106
|
+
typeof overrides !== 'object' ||
|
|
2107
|
+
Object.keys(overrides).length === 0) {
|
|
2108
|
+
return {};
|
|
2109
|
+
}
|
|
2110
|
+
return { [SCREENCI_TEXT_OVERRIDES_ENV]: JSON.stringify(overrides) };
|
|
2111
|
+
}
|
|
2112
|
+
catch (error) {
|
|
2113
|
+
if (verbose) {
|
|
2114
|
+
logger.warn(`Could not fetch Studio text overrides; using code-declared values.${error instanceof Error ? ` (${error.message})` : ''}`);
|
|
2115
|
+
}
|
|
2116
|
+
return {};
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
/**
|
|
2120
|
+
* Fetch the project's current Studio record-option overrides so they can be
|
|
2121
|
+
* injected as SCREENCI_RECORD_OPTIONS before a recording. Record options (aspect
|
|
2122
|
+
* ratio, quality, fps) change the captured viewport/encode, so any Studio edits
|
|
2123
|
+
* must be present here; code-declared values are the fallback. Best-effort: any
|
|
2124
|
+
* failure returns an empty env so the SDK uses the code values, and a recording
|
|
2125
|
+
* is never blocked by this fetch.
|
|
2126
|
+
*/
|
|
2127
|
+
async function fetchRecordOptionsEnv(configPath, verbose) {
|
|
2128
|
+
try {
|
|
2129
|
+
const { screenciConfig, secret, apiUrl } = await requireScreenCISecret(configPath, { interactive: false });
|
|
2130
|
+
const params = new URLSearchParams({
|
|
2131
|
+
projectName: screenciConfig.projectName,
|
|
2132
|
+
});
|
|
2133
|
+
const res = await fetch(`${apiUrl}/cli/record-options?${params.toString()}`, { headers: { 'X-ScreenCI-Secret': secret } });
|
|
2134
|
+
if (!res.ok) {
|
|
2135
|
+
if (verbose) {
|
|
2136
|
+
logger.warn(`Could not fetch Studio record options (${res.status}); using code-declared values.`);
|
|
2137
|
+
}
|
|
2138
|
+
return {};
|
|
2139
|
+
}
|
|
2140
|
+
const body = (await res.json());
|
|
2141
|
+
const recordOptions = body.recordOptions;
|
|
2142
|
+
if (recordOptions === undefined ||
|
|
2143
|
+
recordOptions === null ||
|
|
2144
|
+
typeof recordOptions !== 'object' ||
|
|
2145
|
+
Object.keys(recordOptions).length === 0) {
|
|
2146
|
+
return {};
|
|
2147
|
+
}
|
|
2148
|
+
return { [SCREENCI_RECORD_OPTIONS_ENV]: JSON.stringify(recordOptions) };
|
|
2149
|
+
}
|
|
2150
|
+
catch (error) {
|
|
2151
|
+
if (verbose) {
|
|
2152
|
+
logger.warn(`Could not fetch Studio record options; using code-declared values.${error instanceof Error ? ` (${error.message})` : ''}`);
|
|
2153
|
+
}
|
|
2154
|
+
return {};
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
async function run(command, additionalArgs, customConfigPath, verbose = false, mockRecord = false, pollAuth = false, languages) {
|
|
2001
2158
|
const configPath = resolveScreenCIConfigPathOrExit(customConfigPath);
|
|
2002
2159
|
if (command === 'test' || process.env.SCREENCI_RECORDING !== 'true') {
|
|
2003
2160
|
await loadEnvFileFromConfigSource(configPath, false);
|
|
@@ -2014,11 +2171,25 @@ async function run(command, additionalArgs, customConfigPath, verbose = false, m
|
|
|
2014
2171
|
const screenciDir = resolve(dirname(configPath), '.screenci');
|
|
2015
2172
|
clearRecordingDirectories(screenciDir);
|
|
2016
2173
|
}
|
|
2174
|
+
// Studio text-field overrides are injected for the record command only: they
|
|
2175
|
+
// resolve in the SDK's `text` fixture before the recording runs. `test` (the
|
|
2176
|
+
// preview run) keeps code-declared values.
|
|
2177
|
+
const textOverridesEnv = command === 'record'
|
|
2178
|
+
? await fetchTextOverridesEnv(configPath, languages, verbose)
|
|
2179
|
+
: {};
|
|
2180
|
+
// Studio record-option overrides are likewise injected for record only: they
|
|
2181
|
+
// resolve in the SDK's `recordOptions` fixture before the capture runs.
|
|
2182
|
+
const recordOptionsEnv = command === 'record' ? await fetchRecordOptionsEnv(configPath, verbose) : {};
|
|
2017
2183
|
const envForChild = { ...process.env };
|
|
2018
2184
|
await validateUniqueDiscoveredTestTitles(configPath, additionalArgs, {
|
|
2019
2185
|
...envForChild,
|
|
2020
2186
|
SCREENCI_CONFIG_DIR: dirname(configPath),
|
|
2021
2187
|
...(command === 'record' ? { SCREENCI_RECORDING: 'true' } : {}),
|
|
2188
|
+
...(command === 'record' && languages
|
|
2189
|
+
? { [SCREENCI_LANGUAGES_ENV]: languages }
|
|
2190
|
+
: {}),
|
|
2191
|
+
...textOverridesEnv,
|
|
2192
|
+
...recordOptionsEnv,
|
|
2022
2193
|
...(command === 'test' && !mockRecord
|
|
2023
2194
|
? { [SCREENCI_DISABLE_RECORDING_TIMINGS_ENV]: 'true' }
|
|
2024
2195
|
: {}),
|
|
@@ -2045,6 +2216,14 @@ async function run(command, additionalArgs, customConfigPath, verbose = false, m
|
|
|
2045
2216
|
SCREENCI_CONFIG_DIR: dirname(configPath),
|
|
2046
2217
|
// Enable recording only for record command
|
|
2047
2218
|
...(command === 'record' ? { SCREENCI_RECORDING: 'true' } : {}),
|
|
2219
|
+
// Per-language filter: the builder records only these languages.
|
|
2220
|
+
...(command === 'record' && languages
|
|
2221
|
+
? { [SCREENCI_LANGUAGES_ENV]: languages }
|
|
2222
|
+
: {}),
|
|
2223
|
+
// Studio text-field overrides resolved from the backend (record only).
|
|
2224
|
+
...textOverridesEnv,
|
|
2225
|
+
// Studio record-option overrides resolved from the backend (record only).
|
|
2226
|
+
...recordOptionsEnv,
|
|
2048
2227
|
...(command === 'test' && !mockRecord
|
|
2049
2228
|
? { [SCREENCI_DISABLE_RECORDING_TIMINGS_ENV]: 'true' }
|
|
2050
2229
|
: {}),
|