@midscene/testing-framework 0.0.0

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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/es/builtin-steps.mjs +30 -0
  4. package/dist/es/builtin-steps.mjs.map +1 -0
  5. package/dist/es/config.mjs +83 -0
  6. package/dist/es/config.mjs.map +1 -0
  7. package/dist/es/dotenv.mjs +50 -0
  8. package/dist/es/dotenv.mjs.map +1 -0
  9. package/dist/es/index.mjs +5 -0
  10. package/dist/es/runner-worker.mjs +86 -0
  11. package/dist/es/runner-worker.mjs.map +1 -0
  12. package/dist/es/runner.mjs +165 -0
  13. package/dist/es/runner.mjs.map +1 -0
  14. package/dist/es/runtime/index.mjs +86 -0
  15. package/dist/es/runtime/index.mjs.map +1 -0
  16. package/dist/es/runtime/setup.mjs +59 -0
  17. package/dist/es/runtime/setup.mjs.map +1 -0
  18. package/dist/es/runtime/source.mjs +27 -0
  19. package/dist/es/runtime/source.mjs.map +1 -0
  20. package/dist/es/runtime/yaml.mjs +88 -0
  21. package/dist/es/runtime/yaml.mjs.map +1 -0
  22. package/dist/es/types.mjs +0 -0
  23. package/dist/lib/builtin-steps.js +67 -0
  24. package/dist/lib/builtin-steps.js.map +1 -0
  25. package/dist/lib/config.js +126 -0
  26. package/dist/lib/config.js.map +1 -0
  27. package/dist/lib/dotenv.js +97 -0
  28. package/dist/lib/dotenv.js.map +1 -0
  29. package/dist/lib/index.js +65 -0
  30. package/dist/lib/index.js.map +1 -0
  31. package/dist/lib/runner-worker.js +92 -0
  32. package/dist/lib/runner-worker.js.map +1 -0
  33. package/dist/lib/runner.js +199 -0
  34. package/dist/lib/runner.js.map +1 -0
  35. package/dist/lib/runtime/index.js +141 -0
  36. package/dist/lib/runtime/index.js.map +1 -0
  37. package/dist/lib/runtime/setup.js +96 -0
  38. package/dist/lib/runtime/setup.js.map +1 -0
  39. package/dist/lib/runtime/source.js +61 -0
  40. package/dist/lib/runtime/source.js.map +1 -0
  41. package/dist/lib/runtime/yaml.js +138 -0
  42. package/dist/lib/runtime/yaml.js.map +1 -0
  43. package/dist/lib/types.js +20 -0
  44. package/dist/lib/types.js.map +1 -0
  45. package/dist/types/builtin-steps.d.ts +11 -0
  46. package/dist/types/config.d.ts +18 -0
  47. package/dist/types/dotenv.d.ts +28 -0
  48. package/dist/types/index.d.ts +5 -0
  49. package/dist/types/runner-worker.d.ts +1 -0
  50. package/dist/types/runner.d.ts +33 -0
  51. package/dist/types/runtime/index.d.ts +24 -0
  52. package/dist/types/runtime/setup.d.ts +14 -0
  53. package/dist/types/runtime/source.d.ts +21 -0
  54. package/dist/types/runtime/yaml.d.ts +30 -0
  55. package/dist/types/types.d.ts +105 -0
  56. package/package.json +76 -0
@@ -0,0 +1,86 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, relative } from "node:path";
3
+ import { createDefaultSetup, setupFrameworkAgent } from "./setup.mjs";
4
+ import { normalizeYamlCase, runBuiltinYamlCase, runYamlFlowWithCustomSteps } from "./yaml.mjs";
5
+ import { createYamlFrameworkSuiteSource } from "./source.mjs";
6
+ function _define_property(obj, key, value) {
7
+ if (key in obj) Object.defineProperty(obj, key, {
8
+ value: value,
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true
12
+ });
13
+ else obj[key] = value;
14
+ return obj;
15
+ }
16
+ const errorMessageOf = (error)=>error instanceof Error ? error.message : String(error);
17
+ const writeResultFile = (resultFile, result)=>{
18
+ mkdirSync(dirname(resultFile), {
19
+ recursive: true
20
+ });
21
+ writeFileSync(resultFile, JSON.stringify(result, null, 2));
22
+ };
23
+ class FrameworkSuiteRuntime {
24
+ async setup() {
25
+ this.setupResult = await setupFrameworkAgent(this.config, {
26
+ projectDir: this.projectDir,
27
+ agentOptions: this.config.agentOptions || {}
28
+ });
29
+ }
30
+ async teardown() {
31
+ await this.setupResult?.teardown?.();
32
+ this.setupResult = void 0;
33
+ }
34
+ async runCase(filePath, resultFile) {
35
+ if (!this.setupResult) throw new Error('Suite agent is not ready; setup() must run first');
36
+ const agent = this.setupResult.agent;
37
+ const startTime = Date.now();
38
+ const testName = relative(this.projectDir, filePath) || filePath;
39
+ const normalizedCase = normalizeYamlCase(readFileSync(filePath, 'utf8'), filePath);
40
+ try {
41
+ if (this.config.yamlSteps) await runYamlFlowWithCustomSteps({
42
+ agent,
43
+ filePath,
44
+ caseName: normalizedCase.name,
45
+ flow: normalizedCase.flow,
46
+ yamlSteps: this.config.yamlSteps,
47
+ state: this.state
48
+ });
49
+ else await runBuiltinYamlCase({
50
+ agent,
51
+ normalizedCase
52
+ });
53
+ if (resultFile) writeResultFile(resultFile, {
54
+ file: filePath,
55
+ testName,
56
+ success: true,
57
+ duration: Date.now() - startTime,
58
+ report: 'string' == typeof agent.reportFile ? agent.reportFile : void 0
59
+ });
60
+ } catch (error) {
61
+ if (resultFile) writeResultFile(resultFile, {
62
+ file: filePath,
63
+ testName,
64
+ success: false,
65
+ duration: Date.now() - startTime,
66
+ error: errorMessageOf(error),
67
+ report: 'string' == typeof agent.reportFile ? agent.reportFile : void 0
68
+ });
69
+ throw error;
70
+ }
71
+ }
72
+ constructor(options){
73
+ _define_property(this, "config", void 0);
74
+ _define_property(this, "projectDir", void 0);
75
+ _define_property(this, "state", {});
76
+ _define_property(this, "setupResult", void 0);
77
+ this.config = options.config;
78
+ this.projectDir = options.projectDir;
79
+ }
80
+ }
81
+ function createSuiteRuntime(options) {
82
+ return new FrameworkSuiteRuntime(options);
83
+ }
84
+ export { FrameworkSuiteRuntime, createDefaultSetup, createSuiteRuntime, createYamlFrameworkSuiteSource, normalizeYamlCase, runBuiltinYamlCase, runYamlFlowWithCustomSteps, setupFrameworkAgent };
85
+
86
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime/index.mjs","sources":["../../../src/runtime/index.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, relative } from 'node:path';\nimport type {\n FrameworkCaseResult,\n FrameworkSetupResult,\n MidsceneFrameworkConfig,\n} from '../types';\nimport { setupFrameworkAgent } from './setup';\nimport {\n normalizeYamlCase,\n runBuiltinYamlCase,\n runYamlFlowWithCustomSteps,\n} from './yaml';\n\nexport { createYamlFrameworkSuiteSource } from './source';\nexport {\n normalizeYamlCase,\n runBuiltinYamlCase,\n runYamlFlowWithCustomSteps,\n} from './yaml';\nexport { createDefaultSetup, setupFrameworkAgent } from './setup';\n\nexport interface SuiteRuntimeOptions {\n config: MidsceneFrameworkConfig;\n projectDir: string;\n}\n\nconst errorMessageOf = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nconst writeResultFile = (resultFile: string, result: FrameworkCaseResult) => {\n mkdirSync(dirname(resultFile), { recursive: true });\n writeFileSync(resultFile, JSON.stringify(result, null, 2));\n};\n\n/**\n * Suite-level runtime shared by every generated case in a single Rstest module.\n * The agent and `state` are created once (`beforeAll`) and torn down once\n * (`afterAll`), so seeded data and cross-step state stay shared across cases.\n */\nexport class FrameworkSuiteRuntime {\n private readonly config: MidsceneFrameworkConfig;\n private readonly projectDir: string;\n private readonly state: Record<string, unknown> = {};\n private setupResult?: FrameworkSetupResult;\n\n constructor(options: SuiteRuntimeOptions) {\n this.config = options.config;\n this.projectDir = options.projectDir;\n }\n\n async setup(): Promise<void> {\n this.setupResult = await setupFrameworkAgent(this.config, {\n projectDir: this.projectDir,\n agentOptions: this.config.agentOptions || {},\n });\n }\n\n async teardown(): Promise<void> {\n await this.setupResult?.teardown?.();\n this.setupResult = undefined;\n }\n\n async runCase(filePath: string, resultFile?: string): Promise<void> {\n if (!this.setupResult) {\n throw new Error('Suite agent is not ready; setup() must run first');\n }\n\n const agent = this.setupResult.agent;\n const startTime = Date.now();\n const testName = relative(this.projectDir, filePath) || filePath;\n const normalizedCase = normalizeYamlCase(\n readFileSync(filePath, 'utf8'),\n filePath,\n );\n\n try {\n if (this.config.yamlSteps) {\n await runYamlFlowWithCustomSteps({\n agent,\n filePath,\n caseName: normalizedCase.name,\n flow: normalizedCase.flow,\n yamlSteps: this.config.yamlSteps,\n state: this.state,\n });\n } else {\n await runBuiltinYamlCase({ agent, normalizedCase });\n }\n\n if (resultFile) {\n writeResultFile(resultFile, {\n file: filePath,\n testName,\n success: true,\n duration: Date.now() - startTime,\n report:\n typeof agent.reportFile === 'string' ? agent.reportFile : undefined,\n });\n }\n } catch (error) {\n if (resultFile) {\n writeResultFile(resultFile, {\n file: filePath,\n testName,\n success: false,\n duration: Date.now() - startTime,\n error: errorMessageOf(error),\n report:\n typeof agent.reportFile === 'string' ? agent.reportFile : undefined,\n });\n }\n throw error;\n }\n }\n}\n\nexport function createSuiteRuntime(\n options: SuiteRuntimeOptions,\n): FrameworkSuiteRuntime {\n return new FrameworkSuiteRuntime(options);\n}\n"],"names":["errorMessageOf","error","Error","String","writeResultFile","resultFile","result","mkdirSync","dirname","writeFileSync","JSON","FrameworkSuiteRuntime","setupFrameworkAgent","undefined","filePath","agent","startTime","Date","testName","relative","normalizedCase","normalizeYamlCase","readFileSync","runYamlFlowWithCustomSteps","runBuiltinYamlCase","options","createSuiteRuntime"],"mappings":";;;;;;;;;;;;;;;AA2BA,MAAMA,iBAAiB,CAACC,QACtBA,iBAAiBC,QAAQD,MAAM,OAAO,GAAGE,OAAOF;AAElD,MAAMG,kBAAkB,CAACC,YAAoBC;IAC3CC,UAAUC,QAAQH,aAAa;QAAE,WAAW;IAAK;IACjDI,cAAcJ,YAAYK,KAAK,SAAS,CAACJ,QAAQ,MAAM;AACzD;AAOO,MAAMK;IAWX,MAAM,QAAuB;QAC3B,IAAI,CAAC,WAAW,GAAG,MAAMC,oBAAoB,IAAI,CAAC,MAAM,EAAE;YACxD,YAAY,IAAI,CAAC,UAAU;YAC3B,cAAc,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC;QAC7C;IACF;IAEA,MAAM,WAA0B;QAC9B,MAAM,IAAI,CAAC,WAAW,EAAE;QACxB,IAAI,CAAC,WAAW,GAAGC;IACrB;IAEA,MAAM,QAAQC,QAAgB,EAAET,UAAmB,EAAiB;QAClE,IAAI,CAAC,IAAI,CAAC,WAAW,EACnB,MAAM,IAAIH,MAAM;QAGlB,MAAMa,QAAQ,IAAI,CAAC,WAAW,CAAC,KAAK;QACpC,MAAMC,YAAYC,KAAK,GAAG;QAC1B,MAAMC,WAAWC,SAAS,IAAI,CAAC,UAAU,EAAEL,aAAaA;QACxD,MAAMM,iBAAiBC,kBACrBC,aAAaR,UAAU,SACvBA;QAGF,IAAI;YACF,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EACvB,MAAMS,2BAA2B;gBAC/BR;gBACAD;gBACA,UAAUM,eAAe,IAAI;gBAC7B,MAAMA,eAAe,IAAI;gBACzB,WAAW,IAAI,CAAC,MAAM,CAAC,SAAS;gBAChC,OAAO,IAAI,CAAC,KAAK;YACnB;iBAEA,MAAMI,mBAAmB;gBAAET;gBAAOK;YAAe;YAGnD,IAAIf,YACFD,gBAAgBC,YAAY;gBAC1B,MAAMS;gBACNI;gBACA,SAAS;gBACT,UAAUD,KAAK,GAAG,KAAKD;gBACvB,QACE,AAA4B,YAA5B,OAAOD,MAAM,UAAU,GAAgBA,MAAM,UAAU,GAAGF;YAC9D;QAEJ,EAAE,OAAOZ,OAAO;YACd,IAAII,YACFD,gBAAgBC,YAAY;gBAC1B,MAAMS;gBACNI;gBACA,SAAS;gBACT,UAAUD,KAAK,GAAG,KAAKD;gBACvB,OAAOhB,eAAeC;gBACtB,QACE,AAA4B,YAA5B,OAAOc,MAAM,UAAU,GAAgBA,MAAM,UAAU,GAAGF;YAC9D;YAEF,MAAMZ;QACR;IACF;IApEA,YAAYwB,OAA4B,CAAE;QAL1C,uBAAiB,UAAjB;QACA,uBAAiB,cAAjB;QACA,uBAAiB,SAAiC,CAAC;QACnD,uBAAQ,eAAR;QAGE,IAAI,CAAC,MAAM,GAAGA,QAAQ,MAAM;QAC5B,IAAI,CAAC,UAAU,GAAGA,QAAQ,UAAU;IACtC;AAkEF;AAEO,SAASC,mBACdD,OAA4B;IAE5B,OAAO,IAAId,sBAAsBc;AACnC"}
@@ -0,0 +1,59 @@
1
+ async function createAndroidSetup(config, context) {
2
+ const { agentFromAdbDevice } = await import("@midscene/android");
3
+ const options = config.target?.options || {};
4
+ const { deviceId, launch, ...deviceOptions } = options;
5
+ const agent = await agentFromAdbDevice('string' == typeof deviceId ? deviceId : void 0, {
6
+ ...context.agentOptions,
7
+ ...deviceOptions
8
+ });
9
+ if ('string' == typeof launch && launch) await agent.launch(launch);
10
+ return {
11
+ agent,
12
+ async teardown () {
13
+ if ('string' == typeof launch && launch && !launch.startsWith('http')) await agent.terminate(launch);
14
+ }
15
+ };
16
+ }
17
+ async function createWebSetup(config, context) {
18
+ const [{ PlaywrightAgent }, { chromium }] = await Promise.all([
19
+ import("@midscene/web/playwright"),
20
+ import("playwright")
21
+ ]);
22
+ const options = config.target?.options || {};
23
+ if (!options.url) throw new Error('target.options.url is required for the web target');
24
+ const browser = await chromium.launch({
25
+ headless: options.headless ?? true,
26
+ args: [
27
+ '--no-sandbox',
28
+ '--disable-setuid-sandbox'
29
+ ]
30
+ });
31
+ const browserContext = await browser.newContext({
32
+ viewport: options.viewport
33
+ });
34
+ const page = await browserContext.newPage();
35
+ await page.goto(options.url);
36
+ return {
37
+ agent: new PlaywrightAgent(page, context.agentOptions),
38
+ browser,
39
+ context: browserContext,
40
+ page,
41
+ async teardown () {
42
+ await browserContext.close();
43
+ await browser.close();
44
+ }
45
+ };
46
+ }
47
+ async function createDefaultSetup(config, context) {
48
+ if (!config.target) throw new Error('midscene config must provide "target" or "setup"');
49
+ if ('android' === config.target.type) return createAndroidSetup(config, context);
50
+ if ('web' === config.target.type) return createWebSetup(config, context);
51
+ throw new Error(`Unsupported target.type: ${config.target.type}`);
52
+ }
53
+ async function setupFrameworkAgent(config, context) {
54
+ if (config.setup) return config.setup(context);
55
+ return createDefaultSetup(config, context);
56
+ }
57
+ export { createDefaultSetup, setupFrameworkAgent };
58
+
59
+ //# sourceMappingURL=setup.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime/setup.mjs","sources":["../../../src/runtime/setup.ts"],"sourcesContent":["import type {\n FrameworkSetupResult,\n MidsceneFrameworkConfig,\n SetupContext,\n} from '../types';\n\ninterface DefaultSetupContext extends SetupContext {}\n\nasync function createAndroidSetup(\n config: MidsceneFrameworkConfig,\n context: DefaultSetupContext,\n): Promise<FrameworkSetupResult> {\n const { agentFromAdbDevice } = await import('@midscene/android');\n const options = (config.target?.options || {}) as Record<string, unknown>;\n const { deviceId, launch, ...deviceOptions } = options;\n\n const agent = await agentFromAdbDevice(\n typeof deviceId === 'string' ? deviceId : undefined,\n {\n ...context.agentOptions,\n ...deviceOptions,\n },\n );\n\n if (typeof launch === 'string' && launch) {\n await agent.launch(launch);\n }\n\n return {\n agent,\n async teardown() {\n // Only terminate launched apps, not URLs opened in the default browser.\n if (typeof launch === 'string' && launch && !launch.startsWith('http')) {\n await agent.terminate(launch);\n }\n },\n };\n}\n\nasync function createWebSetup(\n config: MidsceneFrameworkConfig,\n context: DefaultSetupContext,\n): Promise<FrameworkSetupResult> {\n const [{ PlaywrightAgent }, { chromium }] = await Promise.all([\n import('@midscene/web/playwright'),\n import('playwright'),\n ]);\n\n const options = (config.target?.options || {}) as {\n url?: string;\n viewport?: { width: number; height: number };\n headless?: boolean;\n };\n\n if (!options.url) {\n throw new Error('target.options.url is required for the web target');\n }\n\n const browser = await chromium.launch({\n headless: options.headless ?? true,\n args: ['--no-sandbox', '--disable-setuid-sandbox'],\n });\n const browserContext = await browser.newContext({\n viewport: options.viewport,\n });\n const page = await browserContext.newPage();\n await page.goto(options.url);\n\n return {\n agent: new PlaywrightAgent(page, context.agentOptions),\n browser,\n context: browserContext,\n page,\n async teardown() {\n await browserContext.close();\n await browser.close();\n },\n };\n}\n\n/**\n * Create an agent from the default `target` config. Only the `web` and\n * `android` targets documented for the first version are supported.\n */\nexport async function createDefaultSetup(\n config: MidsceneFrameworkConfig,\n context: DefaultSetupContext,\n): Promise<FrameworkSetupResult> {\n if (!config.target) {\n throw new Error('midscene config must provide \"target\" or \"setup\"');\n }\n\n if (config.target.type === 'android') {\n return createAndroidSetup(config, context);\n }\n\n if (config.target.type === 'web') {\n return createWebSetup(config, context);\n }\n\n throw new Error(`Unsupported target.type: ${config.target.type}`);\n}\n\n/**\n * Resolve the suite agent. A custom `setup` takes precedence; otherwise the\n * default target setup is used.\n */\nexport async function setupFrameworkAgent(\n config: MidsceneFrameworkConfig,\n context: DefaultSetupContext,\n): Promise<FrameworkSetupResult> {\n if (config.setup) {\n return config.setup(context);\n }\n\n return createDefaultSetup(config, context);\n}\n"],"names":["createAndroidSetup","config","context","agentFromAdbDevice","options","deviceId","launch","deviceOptions","agent","undefined","createWebSetup","PlaywrightAgent","chromium","Promise","Error","browser","browserContext","page","createDefaultSetup","setupFrameworkAgent"],"mappings":"AAQA,eAAeA,mBACbC,MAA+B,EAC/BC,OAA4B;IAE5B,MAAM,EAAEC,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC;IAC5C,MAAMC,UAAWH,OAAO,MAAM,EAAE,WAAW,CAAC;IAC5C,MAAM,EAAEI,QAAQ,EAAEC,MAAM,EAAE,GAAGC,eAAe,GAAGH;IAE/C,MAAMI,QAAQ,MAAML,mBAClB,AAAoB,YAApB,OAAOE,WAAwBA,WAAWI,QAC1C;QACE,GAAGP,QAAQ,YAAY;QACvB,GAAGK,aAAa;IAClB;IAGF,IAAI,AAAkB,YAAlB,OAAOD,UAAuBA,QAChC,MAAME,MAAM,MAAM,CAACF;IAGrB,OAAO;QACLE;QACA,MAAM;YAEJ,IAAI,AAAkB,YAAlB,OAAOF,UAAuBA,UAAU,CAACA,OAAO,UAAU,CAAC,SAC7D,MAAME,MAAM,SAAS,CAACF;QAE1B;IACF;AACF;AAEA,eAAeI,eACbT,MAA+B,EAC/BC,OAA4B;IAE5B,MAAM,CAAC,EAAES,eAAe,EAAE,EAAE,EAAEC,QAAQ,EAAE,CAAC,GAAG,MAAMC,QAAQ,GAAG,CAAC;QAC5D,MAAM,CAAC;QACP,MAAM,CAAC;KACR;IAED,MAAMT,UAAWH,OAAO,MAAM,EAAE,WAAW,CAAC;IAM5C,IAAI,CAACG,QAAQ,GAAG,EACd,MAAM,IAAIU,MAAM;IAGlB,MAAMC,UAAU,MAAMH,SAAS,MAAM,CAAC;QACpC,UAAUR,QAAQ,QAAQ,IAAI;QAC9B,MAAM;YAAC;YAAgB;SAA2B;IACpD;IACA,MAAMY,iBAAiB,MAAMD,QAAQ,UAAU,CAAC;QAC9C,UAAUX,QAAQ,QAAQ;IAC5B;IACA,MAAMa,OAAO,MAAMD,eAAe,OAAO;IACzC,MAAMC,KAAK,IAAI,CAACb,QAAQ,GAAG;IAE3B,OAAO;QACL,OAAO,IAAIO,gBAAgBM,MAAMf,QAAQ,YAAY;QACrDa;QACA,SAASC;QACTC;QACA,MAAM;YACJ,MAAMD,eAAe,KAAK;YAC1B,MAAMD,QAAQ,KAAK;QACrB;IACF;AACF;AAMO,eAAeG,mBACpBjB,MAA+B,EAC/BC,OAA4B;IAE5B,IAAI,CAACD,OAAO,MAAM,EAChB,MAAM,IAAIa,MAAM;IAGlB,IAAIb,AAAuB,cAAvBA,OAAO,MAAM,CAAC,IAAI,EACpB,OAAOD,mBAAmBC,QAAQC;IAGpC,IAAID,AAAuB,UAAvBA,OAAO,MAAM,CAAC,IAAI,EACpB,OAAOS,eAAeT,QAAQC;IAGhC,MAAM,IAAIY,MAAM,CAAC,yBAAyB,EAAEb,OAAO,MAAM,CAAC,IAAI,EAAE;AAClE;AAMO,eAAekB,oBACpBlB,MAA+B,EAC/BC,OAA4B;IAE5B,IAAID,OAAO,KAAK,EACd,OAAOA,OAAO,KAAK,CAACC;IAGtB,OAAOgB,mBAAmBjB,QAAQC;AACpC"}
@@ -0,0 +1,27 @@
1
+ function createYamlFrameworkSuiteSource(options) {
2
+ const runtimeImport = options.runtimeImport || '@midscene/testing-framework/runtime';
3
+ const rstestImport = options.rstestImport || '@rstest/core';
4
+ const tests = options.cases.map((item)=>`test(${JSON.stringify(item.testName)}, async () => {\n await runtime.runCase(${JSON.stringify(item.filePath)}, ${JSON.stringify(item.resultFile)});\n});`).join('\n\n');
5
+ return `import { afterAll, beforeAll, test } from ${JSON.stringify(rstestImport)};
6
+ import config from ${JSON.stringify(options.configPath)};
7
+ import { createSuiteRuntime } from ${JSON.stringify(runtimeImport)};
8
+
9
+ const runtime = createSuiteRuntime({
10
+ config,
11
+ projectDir: ${JSON.stringify(options.projectDir)},
12
+ });
13
+
14
+ beforeAll(async () => {
15
+ await runtime.setup();
16
+ });
17
+
18
+ afterAll(async () => {
19
+ await runtime.teardown();
20
+ });
21
+
22
+ ${tests}
23
+ `;
24
+ }
25
+ export { createYamlFrameworkSuiteSource };
26
+
27
+ //# sourceMappingURL=source.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime/source.mjs","sources":["../../../src/runtime/source.ts"],"sourcesContent":["export interface FrameworkSuiteSourceCase {\n filePath: string;\n testName: string;\n resultFile: string;\n}\n\nexport interface CreateYamlFrameworkSuiteSourceOptions {\n configPath: string;\n projectDir: string;\n cases: FrameworkSuiteSourceCase[];\n /** Import specifier for `@midscene/testing-framework/runtime`. */\n runtimeImport?: string;\n /** Import specifier for the Rstest test API. */\n rstestImport?: string;\n}\n\n/**\n * Generate the source of a single Rstest module that owns the whole YAML suite:\n * it sets the shared agent up once, registers one `test()` per YAML case, and\n * tears the agent down once. Each YAML case therefore maps to one Rstest test,\n * while suite-level setup/teardown and shared `state` are preserved.\n */\nexport function createYamlFrameworkSuiteSource(\n options: CreateYamlFrameworkSuiteSourceOptions,\n): string {\n const runtimeImport =\n options.runtimeImport || '@midscene/testing-framework/runtime';\n const rstestImport = options.rstestImport || '@rstest/core';\n\n const tests = options.cases\n .map(\n (item) =>\n `test(${JSON.stringify(item.testName)}, async () => {\\n await runtime.runCase(${JSON.stringify(\n item.filePath,\n )}, ${JSON.stringify(item.resultFile)});\\n});`,\n )\n .join('\\n\\n');\n\n return `import { afterAll, beforeAll, test } from ${JSON.stringify(rstestImport)};\nimport config from ${JSON.stringify(options.configPath)};\nimport { createSuiteRuntime } from ${JSON.stringify(runtimeImport)};\n\nconst runtime = createSuiteRuntime({\n config,\n projectDir: ${JSON.stringify(options.projectDir)},\n});\n\nbeforeAll(async () => {\n await runtime.setup();\n});\n\nafterAll(async () => {\n await runtime.teardown();\n});\n\n${tests}\n`;\n}\n"],"names":["createYamlFrameworkSuiteSource","options","runtimeImport","rstestImport","tests","item","JSON"],"mappings":"AAsBO,SAASA,+BACdC,OAA8C;IAE9C,MAAMC,gBACJD,QAAQ,aAAa,IAAI;IAC3B,MAAME,eAAeF,QAAQ,YAAY,IAAI;IAE7C,MAAMG,QAAQH,QAAQ,KAAK,CACxB,GAAG,CACF,CAACI,OACC,CAAC,KAAK,EAAEC,KAAK,SAAS,CAACD,KAAK,QAAQ,EAAE,yCAAyC,EAAEC,KAAK,SAAS,CAC7FD,KAAK,QAAQ,EACb,EAAE,EAAEC,KAAK,SAAS,CAACD,KAAK,UAAU,EAAE,OAAO,CAAC,EAEjD,IAAI,CAAC;IAER,OAAO,CAAC,0CAA0C,EAAEC,KAAK,SAAS,CAACH,cAAc;mBAChE,EAAEG,KAAK,SAAS,CAACL,QAAQ,UAAU,EAAE;mCACrB,EAAEK,KAAK,SAAS,CAACJ,eAAe;;;;cAIrD,EAAEI,KAAK,SAAS,CAACL,QAAQ,UAAU,EAAE;;;;;;;;;;;AAWnD,EAAEG,MAAM;AACR,CAAC;AACD"}
@@ -0,0 +1,88 @@
1
+ import { basename, extname } from "node:path";
2
+ import js_yaml from "js-yaml";
3
+ import { BUILTIN_YAML_STEP_NAMES } from "../builtin-steps.mjs";
4
+ const caseNameFromPath = (filePath)=>basename(filePath, extname(filePath)) || 'case';
5
+ function normalizeYamlCase(content, filePath) {
6
+ const parsed = js_yaml.load(content);
7
+ if (!parsed || 'object' != typeof parsed || Array.isArray(parsed)) throw new Error(`${filePath} must be a YAML object`);
8
+ if (Array.isArray(parsed.flow)) {
9
+ const { flow, ...rest } = parsed;
10
+ return {
11
+ name: caseNameFromPath(filePath),
12
+ flow: flow,
13
+ raw: rest
14
+ };
15
+ }
16
+ if (Array.isArray(parsed.tasks)) throw new Error(`${filePath} uses a full "tasks" document; framework cases must use a top-level "flow"`);
17
+ throw new Error(`${filePath} must include a top-level "flow" array`);
18
+ }
19
+ const dumpSingleStepTask = (caseName, step)=>js_yaml.dump({
20
+ tasks: [
21
+ {
22
+ name: caseName,
23
+ flow: [
24
+ step
25
+ ]
26
+ }
27
+ ]
28
+ }, {
29
+ lineWidth: -1,
30
+ noRefs: true
31
+ });
32
+ const resolveStepName = (step, yamlSteps)=>{
33
+ const keys = Object.keys(step);
34
+ const customKey = keys.find((key)=>yamlSteps?.[key]);
35
+ if (customKey) return {
36
+ stepName: customKey,
37
+ kind: 'custom'
38
+ };
39
+ const builtinKey = keys.find((key)=>BUILTIN_YAML_STEP_NAMES.has(key));
40
+ if (builtinKey) return {
41
+ stepName: builtinKey,
42
+ kind: 'builtin'
43
+ };
44
+ return {
45
+ stepName: keys[0] ?? '',
46
+ kind: 'unknown'
47
+ };
48
+ };
49
+ async function runYamlFlowWithCustomSteps(options) {
50
+ for (const [stepIndex, step] of options.flow.entries()){
51
+ if (!step || 'object' != typeof step || Array.isArray(step)) throw new Error(`${options.filePath} step ${stepIndex + 1} must be an object`);
52
+ const stepRecord = step;
53
+ const { stepName, kind } = resolveStepName(stepRecord, options.yamlSteps);
54
+ if ('custom' === kind) {
55
+ const handler = options.yamlSteps[stepName];
56
+ await handler(stepRecord[stepName], {
57
+ agent: options.agent,
58
+ state: options.state,
59
+ filePath: options.filePath,
60
+ stepIndex,
61
+ stepName
62
+ });
63
+ continue;
64
+ }
65
+ if ('builtin' === kind) {
66
+ await options.agent.runYaml(dumpSingleStepTask(`${options.caseName}:${stepName}`, step));
67
+ continue;
68
+ }
69
+ throw new Error(`${options.filePath} step ${stepIndex + 1} uses unknown step "${stepName}"`);
70
+ }
71
+ }
72
+ async function runBuiltinYamlCase(options) {
73
+ await options.agent.runYaml(js_yaml.dump({
74
+ ...options.normalizedCase.raw,
75
+ tasks: [
76
+ {
77
+ name: options.normalizedCase.name,
78
+ flow: options.normalizedCase.flow
79
+ }
80
+ ]
81
+ }, {
82
+ lineWidth: -1,
83
+ noRefs: true
84
+ }));
85
+ }
86
+ export { normalizeYamlCase, runBuiltinYamlCase, runYamlFlowWithCustomSteps };
87
+
88
+ //# sourceMappingURL=yaml.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime/yaml.mjs","sources":["../../../src/runtime/yaml.ts"],"sourcesContent":["import { basename, extname } from 'node:path';\nimport type { MidsceneYamlFlowItem } from '@midscene/core';\nimport yaml from 'js-yaml';\nimport { BUILTIN_YAML_STEP_NAMES } from '../builtin-steps';\nimport type {\n CustomYamlStepHandler,\n FrameworkAgent,\n NormalizedYamlCase,\n} from '../types';\n\nconst caseNameFromPath = (filePath: string): string =>\n basename(filePath, extname(filePath)) || 'case';\n\n/**\n * Convert a framework YAML case (top-level `flow`) into a normalized shape. The\n * first version intentionally requires the documented top-level `flow` form and\n * rejects full `tasks` documents.\n */\nexport function normalizeYamlCase(\n content: string,\n filePath: string,\n): NormalizedYamlCase {\n const parsed = yaml.load(content) as Record<string, unknown> | undefined;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`${filePath} must be a YAML object`);\n }\n\n if (Array.isArray(parsed.flow)) {\n const { flow, ...rest } = parsed;\n return {\n name: caseNameFromPath(filePath),\n flow: flow as MidsceneYamlFlowItem[],\n raw: rest,\n };\n }\n\n if (Array.isArray((parsed as { tasks?: unknown }).tasks)) {\n throw new Error(\n `${filePath} uses a full \"tasks\" document; framework cases must use a top-level \"flow\"`,\n );\n }\n\n throw new Error(`${filePath} must include a top-level \"flow\" array`);\n}\n\nconst dumpSingleStepTask = (caseName: string, step: unknown): string =>\n yaml.dump(\n {\n tasks: [\n {\n name: caseName,\n flow: [step],\n },\n ],\n },\n { lineWidth: -1, noRefs: true },\n );\n\nconst resolveStepName = (\n step: Record<string, unknown>,\n yamlSteps: Record<string, CustomYamlStepHandler> | undefined,\n): { stepName: string; kind: 'custom' | 'builtin' | 'unknown' } => {\n const keys = Object.keys(step);\n\n // A custom step is identified by the registered key; built-in steps may carry\n // extra sibling keys (e.g. `aiInput` + `value`), so we look the action key up\n // instead of requiring exactly one key.\n const customKey = keys.find((key) => yamlSteps?.[key]);\n if (customKey) {\n return { stepName: customKey, kind: 'custom' };\n }\n\n const builtinKey = keys.find((key) => BUILTIN_YAML_STEP_NAMES.has(key));\n if (builtinKey) {\n return { stepName: builtinKey, kind: 'builtin' };\n }\n\n return { stepName: keys[0] ?? '', kind: 'unknown' };\n};\n\n/**\n * Run a flow step by step so built-in steps and custom `yamlSteps` interleave in\n * the authored order. Built-in steps are forwarded to `agent.runYaml`; custom\n * steps invoke their handler with the YAML value and the current context.\n */\nexport async function runYamlFlowWithCustomSteps(options: {\n agent: FrameworkAgent;\n filePath: string;\n caseName: string;\n flow: MidsceneYamlFlowItem[];\n yamlSteps?: Record<string, CustomYamlStepHandler>;\n state: Record<string, unknown>;\n}): Promise<void> {\n for (const [stepIndex, step] of options.flow.entries()) {\n if (!step || typeof step !== 'object' || Array.isArray(step)) {\n throw new Error(\n `${options.filePath} step ${stepIndex + 1} must be an object`,\n );\n }\n\n const stepRecord = step as Record<string, unknown>;\n const { stepName, kind } = resolveStepName(stepRecord, options.yamlSteps);\n\n if (kind === 'custom') {\n const handler = options.yamlSteps![stepName];\n await handler(stepRecord[stepName], {\n agent: options.agent,\n state: options.state,\n filePath: options.filePath,\n stepIndex,\n stepName,\n });\n continue;\n }\n\n if (kind === 'builtin') {\n await options.agent.runYaml(\n dumpSingleStepTask(`${options.caseName}:${stepName}`, step),\n );\n continue;\n }\n\n throw new Error(\n `${options.filePath} step ${stepIndex + 1} uses unknown step \"${stepName}\"`,\n );\n }\n}\n\n/**\n * Run a whole built-in case in a single `agent.runYaml` invocation. Used when no\n * custom `yamlSteps` are registered, so the existing YAML runner handles the\n * complete flow at once.\n */\nexport async function runBuiltinYamlCase(options: {\n agent: FrameworkAgent;\n normalizedCase: NormalizedYamlCase;\n}): Promise<void> {\n await options.agent.runYaml(\n yaml.dump(\n {\n ...options.normalizedCase.raw,\n tasks: [\n {\n name: options.normalizedCase.name,\n flow: options.normalizedCase.flow,\n },\n ],\n },\n { lineWidth: -1, noRefs: true },\n ),\n );\n}\n"],"names":["caseNameFromPath","filePath","basename","extname","normalizeYamlCase","content","parsed","yaml","Array","Error","flow","rest","dumpSingleStepTask","caseName","step","resolveStepName","yamlSteps","keys","Object","customKey","key","builtinKey","BUILTIN_YAML_STEP_NAMES","runYamlFlowWithCustomSteps","options","stepIndex","stepRecord","stepName","kind","handler","runBuiltinYamlCase"],"mappings":"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"}
File without changes
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ BUILTIN_YAML_STEP_NAMES: ()=>BUILTIN_YAML_STEP_NAMES,
28
+ isBuiltinYamlStep: ()=>isBuiltinYamlStep
29
+ });
30
+ const BUILTIN_YAML_STEP_NAMES = new Set([
31
+ 'ai',
32
+ 'aiAct',
33
+ 'aiAction',
34
+ 'aiAssert',
35
+ 'aiQuery',
36
+ 'aiBoolean',
37
+ 'aiNumber',
38
+ 'aiString',
39
+ 'aiLocate',
40
+ 'aiInput',
41
+ 'aiTap',
42
+ 'aiRightClick',
43
+ 'aiDoubleClick',
44
+ 'aiHover',
45
+ 'aiScroll',
46
+ 'aiKeyboardPress',
47
+ 'aiWaitFor',
48
+ 'sleep',
49
+ "javascript",
50
+ 'logScreenshot',
51
+ 'launch',
52
+ 'terminate',
53
+ 'runAdbShell',
54
+ 'runWdaRequest'
55
+ ]);
56
+ const isBuiltinYamlStep = (stepName)=>BUILTIN_YAML_STEP_NAMES.has(stepName);
57
+ exports.BUILTIN_YAML_STEP_NAMES = __webpack_exports__.BUILTIN_YAML_STEP_NAMES;
58
+ exports.isBuiltinYamlStep = __webpack_exports__.isBuiltinYamlStep;
59
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
60
+ "BUILTIN_YAML_STEP_NAMES",
61
+ "isBuiltinYamlStep"
62
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
63
+ Object.defineProperty(exports, '__esModule', {
64
+ value: true
65
+ });
66
+
67
+ //# sourceMappingURL=builtin-steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-steps.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/builtin-steps.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","/**\n * Built-in Midscene YAML step keys. When a `flow` step's action key matches one\n * of these, the framework hands the step to the existing `agent.runYaml`\n * runner. Custom `yamlSteps` are not allowed to reuse these names.\n *\n * The set covers the keys documented for #2509 plus the action keys that the\n * example cases actually use (e.g. `aiWaitFor`, `value`-bearing steps). Extend\n * it together with a test when a missing built-in key surfaces.\n */\nexport const BUILTIN_YAML_STEP_NAMES: ReadonlySet<string> = new Set([\n 'ai',\n 'aiAct',\n 'aiAction',\n 'aiAssert',\n 'aiQuery',\n 'aiBoolean',\n 'aiNumber',\n 'aiString',\n 'aiLocate',\n 'aiInput',\n 'aiTap',\n 'aiRightClick',\n 'aiDoubleClick',\n 'aiHover',\n 'aiScroll',\n 'aiKeyboardPress',\n 'aiWaitFor',\n 'sleep',\n 'javascript',\n 'logScreenshot',\n 'launch',\n 'terminate',\n 'runAdbShell',\n 'runWdaRequest',\n]);\n\nexport const isBuiltinYamlStep = (stepName: string): boolean =>\n BUILTIN_YAML_STEP_NAMES.has(stepName);\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","BUILTIN_YAML_STEP_NAMES","Set","isBuiltinYamlStep","stepName"],"mappings":";;;IAAAA,oBAAoB,CAAC,GAAG,CAAC,UAASC;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGD,oBAAoB,CAAC,CAACC,YAAYC,QAAQ,CAACF,oBAAoB,CAAC,CAAC,UAASE,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAF,oBAAoB,CAAC,GAAG,CAACI,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFL,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOM,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;ACGO,MAAMI,0BAA+C,IAAIC,IAAI;IAClE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAEM,MAAMC,oBAAoB,CAACC,WAChCH,wBAAwB,GAAG,CAACG"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ collectFrameworkTestFiles: ()=>collectFrameworkTestFiles,
28
+ defineMidsceneConfig: ()=>defineMidsceneConfig,
29
+ loadMidsceneConfig: ()=>loadMidsceneConfig,
30
+ validateMidsceneConfig: ()=>validateMidsceneConfig
31
+ });
32
+ const external_node_fs_namespaceObject = require("node:fs");
33
+ const external_node_path_namespaceObject = require("node:path");
34
+ const external_glob_namespaceObject = require("glob");
35
+ const external_jiti_namespaceObject = require("jiti");
36
+ const external_builtin_steps_js_namespaceObject = require("./builtin-steps.js");
37
+ function defineMidsceneConfig(config) {
38
+ return config;
39
+ }
40
+ const DEFAULT_CONFIG_BASENAMES = [
41
+ 'midscene.config.ts',
42
+ 'midscene.config.mts',
43
+ 'midscene.config.cts',
44
+ 'midscene.config.js',
45
+ 'midscene.config.mjs',
46
+ 'midscene.config.cjs'
47
+ ];
48
+ const resolveConfigPath = (configPath)=>{
49
+ if (configPath) return (0, external_node_path_namespaceObject.resolve)(configPath);
50
+ const cwd = process.cwd();
51
+ const matched = DEFAULT_CONFIG_BASENAMES.map((name)=>(0, external_node_path_namespaceObject.resolve)(cwd, name)).find((candidate)=>(0, external_node_fs_namespaceObject.existsSync)(candidate));
52
+ return matched || (0, external_node_path_namespaceObject.resolve)(cwd, 'midscene.config.ts');
53
+ };
54
+ async function loadMidsceneConfig(configPath) {
55
+ const resolvedPath = resolveConfigPath(configPath);
56
+ if (!(0, external_node_fs_namespaceObject.existsSync)(resolvedPath)) throw new Error(`midscene config not found: ${resolvedPath}`);
57
+ const jiti = (0, external_jiti_namespaceObject.createJiti)(resolvedPath, {
58
+ moduleCache: false
59
+ });
60
+ const config = await jiti.import(resolvedPath, {
61
+ default: true
62
+ });
63
+ if (!config || 'object' != typeof config) throw new Error(`midscene config must export a default object: ${resolvedPath}`);
64
+ validateMidsceneConfig(config, resolvedPath);
65
+ return {
66
+ path: resolvedPath,
67
+ root: (0, external_node_path_namespaceObject.dirname)(resolvedPath),
68
+ config
69
+ };
70
+ }
71
+ function validateMidsceneConfig(config, source = 'midscene.config.ts') {
72
+ if (!config.testDir) throw new Error(`${source} must define "testDir"`);
73
+ if (!Array.isArray(config.include) || 0 === config.include.length) throw new Error(`${source} must define a non-empty "include" array`);
74
+ if (config.target && config.setup) throw new Error(`${source} cannot define both "target" and "setup"; pick one runtime target definition`);
75
+ if (config.yamlSteps) {
76
+ const overridden = Object.keys(config.yamlSteps).filter((name)=>external_builtin_steps_js_namespaceObject.BUILTIN_YAML_STEP_NAMES.has(name));
77
+ if (overridden.length > 0) throw new Error(`${source} custom yamlSteps cannot override built-in steps: ${overridden.join(', ')}`);
78
+ }
79
+ }
80
+ const toPosixPath = (value)=>value.split('\\').join('/');
81
+ const uniqueSorted = (files)=>Array.from(new Set(files)).sort((a, b)=>a.localeCompare(b));
82
+ const inferFileType = (filePath)=>filePath.endsWith('.yaml') || filePath.endsWith('.yml') ? 'yaml' : 'test';
83
+ async function collectFrameworkTestFiles(input) {
84
+ const testDir = input.config.testDir || './e2e';
85
+ const include = input.config.include && input.config.include.length > 0 ? input.config.include : [
86
+ '**/*.yaml',
87
+ '**/*.yml',
88
+ '**/*.test.ts'
89
+ ];
90
+ const exclude = input.config.exclude || [];
91
+ const cwd = (0, external_node_path_namespaceObject.resolve)(input.root, testDir);
92
+ const files = [];
93
+ for (const pattern of include){
94
+ const matched = await (0, external_glob_namespaceObject.glob)(pattern, {
95
+ cwd,
96
+ absolute: true,
97
+ ignore: exclude,
98
+ nodir: true,
99
+ dot: true
100
+ });
101
+ files.push(...matched);
102
+ }
103
+ return uniqueSorted(files).map((filePath)=>{
104
+ const relativeToRoot = toPosixPath(filePath.startsWith(input.root) ? filePath.slice(input.root.length + 1) : filePath);
105
+ return {
106
+ filePath,
107
+ relativePath: relativeToRoot,
108
+ type: inferFileType(filePath)
109
+ };
110
+ });
111
+ }
112
+ exports.collectFrameworkTestFiles = __webpack_exports__.collectFrameworkTestFiles;
113
+ exports.defineMidsceneConfig = __webpack_exports__.defineMidsceneConfig;
114
+ exports.loadMidsceneConfig = __webpack_exports__.loadMidsceneConfig;
115
+ exports.validateMidsceneConfig = __webpack_exports__.validateMidsceneConfig;
116
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
117
+ "collectFrameworkTestFiles",
118
+ "defineMidsceneConfig",
119
+ "loadMidsceneConfig",
120
+ "validateMidsceneConfig"
121
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
122
+ Object.defineProperty(exports, '__esModule', {
123
+ value: true
124
+ });
125
+
126
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/config.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { glob } from 'glob';\nimport { createJiti } from 'jiti';\nimport { BUILTIN_YAML_STEP_NAMES } from './builtin-steps';\nimport type {\n FrameworkTestFile,\n LoadedMidsceneConfig,\n MidsceneFrameworkConfig,\n} from './types';\n\n/**\n * Type-only helper. It returns the config unchanged so the runtime stays\n * explicit and the project keeps full control over its own configuration.\n */\nexport function defineMidsceneConfig<T extends MidsceneFrameworkConfig>(\n config: T,\n): T {\n return config;\n}\n\nconst DEFAULT_CONFIG_BASENAMES = [\n 'midscene.config.ts',\n 'midscene.config.mts',\n 'midscene.config.cts',\n 'midscene.config.js',\n 'midscene.config.mjs',\n 'midscene.config.cjs',\n];\n\nconst resolveConfigPath = (configPath?: string): string => {\n if (configPath) {\n return resolve(configPath);\n }\n\n const cwd = process.cwd();\n const matched = DEFAULT_CONFIG_BASENAMES.map((name) =>\n resolve(cwd, name),\n ).find((candidate) => existsSync(candidate));\n\n return matched || resolve(cwd, 'midscene.config.ts');\n};\n\nexport async function loadMidsceneConfig(\n configPath?: string,\n): Promise<LoadedMidsceneConfig> {\n const resolvedPath = resolveConfigPath(configPath);\n if (!existsSync(resolvedPath)) {\n throw new Error(`midscene config not found: ${resolvedPath}`);\n }\n\n const jiti = createJiti(resolvedPath, {\n moduleCache: false,\n });\n const config = await jiti.import<MidsceneFrameworkConfig | undefined>(\n resolvedPath,\n {\n default: true,\n },\n );\n if (!config || typeof config !== 'object') {\n throw new Error(\n `midscene config must export a default object: ${resolvedPath}`,\n );\n }\n\n validateMidsceneConfig(config, resolvedPath);\n\n return {\n path: resolvedPath,\n root: dirname(resolvedPath),\n config,\n };\n}\n\n/**\n * Validate the documented invariants of a `midscene.config.ts`:\n * - `testDir` and `include` are required.\n * - `target` and `setup` cannot be defined together (two runtime targets).\n * - custom `yamlSteps` must not override built-in step names.\n */\nexport function validateMidsceneConfig(\n config: MidsceneFrameworkConfig,\n source = 'midscene.config.ts',\n): void {\n if (!config.testDir) {\n throw new Error(`${source} must define \"testDir\"`);\n }\n\n if (!Array.isArray(config.include) || config.include.length === 0) {\n throw new Error(`${source} must define a non-empty \"include\" array`);\n }\n\n if (config.target && config.setup) {\n throw new Error(\n `${source} cannot define both \"target\" and \"setup\"; pick one runtime target definition`,\n );\n }\n\n if (config.yamlSteps) {\n const overridden = Object.keys(config.yamlSteps).filter((name) =>\n BUILTIN_YAML_STEP_NAMES.has(name),\n );\n if (overridden.length > 0) {\n throw new Error(\n `${source} custom yamlSteps cannot override built-in steps: ${overridden.join(', ')}`,\n );\n }\n }\n}\n\nconst toPosixPath = (value: string): string => value.split('\\\\').join('/');\n\nconst uniqueSorted = (files: string[]): string[] =>\n Array.from(new Set(files)).sort((a, b) => a.localeCompare(b));\n\nconst inferFileType = (filePath: string): FrameworkTestFile['type'] =>\n filePath.endsWith('.yaml') || filePath.endsWith('.yml') ? 'yaml' : 'test';\n\nexport async function collectFrameworkTestFiles(input: {\n root: string;\n config: MidsceneFrameworkConfig;\n}): Promise<FrameworkTestFile[]> {\n const testDir = input.config.testDir || './e2e';\n const include =\n input.config.include && input.config.include.length > 0\n ? input.config.include\n : ['**/*.yaml', '**/*.yml', '**/*.test.ts'];\n const exclude = input.config.exclude || [];\n const cwd = resolve(input.root, testDir);\n\n const files: string[] = [];\n for (const pattern of include) {\n const matched = await glob(pattern, {\n cwd,\n absolute: true,\n ignore: exclude,\n nodir: true,\n dot: true,\n });\n files.push(...matched);\n }\n\n return uniqueSorted(files).map((filePath) => {\n const relativeToRoot = toPosixPath(\n filePath.startsWith(input.root)\n ? filePath.slice(input.root.length + 1)\n : filePath,\n );\n return {\n filePath,\n relativePath: relativeToRoot,\n type: inferFileType(filePath),\n };\n });\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","defineMidsceneConfig","config","DEFAULT_CONFIG_BASENAMES","resolveConfigPath","configPath","resolve","cwd","process","matched","name","candidate","existsSync","loadMidsceneConfig","resolvedPath","Error","jiti","createJiti","validateMidsceneConfig","dirname","source","Array","overridden","BUILTIN_YAML_STEP_NAMES","toPosixPath","value","uniqueSorted","files","Set","a","b","inferFileType","filePath","collectFrameworkTestFiles","input","testDir","include","exclude","pattern","glob","relativeToRoot"],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
12
+ (()=>{
13
+ __webpack_require__.d = (exports1, definition)=>{
14
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
15
+ enumerable: true,
16
+ get: definition[key]
17
+ });
18
+ };
19
+ })();
20
+ (()=>{
21
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
22
+ })();
23
+ (()=>{
24
+ __webpack_require__.r = (exports1)=>{
25
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
26
+ value: 'Module'
27
+ });
28
+ Object.defineProperty(exports1, '__esModule', {
29
+ value: true
30
+ });
31
+ };
32
+ })();
33
+ var __webpack_exports__ = {};
34
+ __webpack_require__.r(__webpack_exports__);
35
+ __webpack_require__.d(__webpack_exports__, {
36
+ resolveDotenvCandidates: ()=>resolveDotenvCandidates,
37
+ loadFrameworkDotenv: ()=>loadFrameworkDotenv
38
+ });
39
+ const external_node_fs_namespaceObject = require("node:fs");
40
+ const external_node_path_namespaceObject = require("node:path");
41
+ const external_dotenv_namespaceObject = require("dotenv");
42
+ var external_dotenv_default = /*#__PURE__*/ __webpack_require__.n(external_dotenv_namespaceObject);
43
+ const toAbsolutePath = (cwd, candidate)=>(0, external_node_path_namespaceObject.resolve)(cwd, candidate);
44
+ const dedupePaths = (paths)=>{
45
+ const seen = new Set();
46
+ const result = [];
47
+ for (const path of paths)if (!seen.has(path)) {
48
+ seen.add(path);
49
+ result.push(path);
50
+ }
51
+ return result;
52
+ };
53
+ function resolveDotenvCandidates(input) {
54
+ const { cwd, configDir, envConfig } = input;
55
+ if (envConfig?.path) {
56
+ const list = Array.isArray(envConfig.path) ? envConfig.path : [
57
+ envConfig.path
58
+ ];
59
+ return dedupePaths(list.map((entry)=>toAbsolutePath(cwd, entry)));
60
+ }
61
+ return dedupePaths([
62
+ toAbsolutePath(cwd, '.env'),
63
+ toAbsolutePath(configDir, '.env')
64
+ ]);
65
+ }
66
+ function loadFrameworkDotenv(input) {
67
+ if (input.envConfig?.enabled === false) return [];
68
+ const candidates = resolveDotenvCandidates(input);
69
+ const override = input.envConfig?.override === true;
70
+ const debug = input.envConfig?.debug === true;
71
+ return candidates.map((path)=>{
72
+ if (!(0, external_node_fs_namespaceObject.existsSync)(path)) return {
73
+ path,
74
+ loaded: false
75
+ };
76
+ external_dotenv_default().config({
77
+ path,
78
+ override,
79
+ debug
80
+ });
81
+ return {
82
+ path,
83
+ loaded: true
84
+ };
85
+ });
86
+ }
87
+ exports.loadFrameworkDotenv = __webpack_exports__.loadFrameworkDotenv;
88
+ exports.resolveDotenvCandidates = __webpack_exports__.resolveDotenvCandidates;
89
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
90
+ "loadFrameworkDotenv",
91
+ "resolveDotenvCandidates"
92
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
93
+ Object.defineProperty(exports, '__esModule', {
94
+ value: true
95
+ });
96
+
97
+ //# sourceMappingURL=dotenv.js.map