@midscene/cli 1.8.7 → 1.8.8-beta-20260601092817.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.
package/dist/es/index.mjs CHANGED
@@ -1,26 +1,28 @@
1
1
  /*! For license information please see index.mjs.LICENSE.txt */
2
- import node_path, { basename, dirname, extname, join, posix, relative as external_node_path_relative, resolve as external_node_path_resolve, win32 } from "node:path";
2
+ import { basename as external_node_path_basename, dirname as external_node_path_dirname, extname as external_node_path_extname, join as external_node_path_join, posix, relative as external_node_path_relative, resolve as external_node_path_resolve, sep as external_node_path_sep, win32 } from "node:path";
3
3
  import { createReportCliCommands } from "@midscene/core";
4
4
  import { runToolsCLI } from "@midscene/shared/cli";
5
- import { ScriptPlayer, interpolateEnvVars, parseYamlScript } from "@midscene/core/yaml";
6
- import { getMidsceneRunSubDir } from "@midscene/shared/common";
7
- import { buildChromeArgs, defaultViewportHeight, defaultViewportWidth, puppeteerAgentForTarget } from "@midscene/web/puppeteer-agent-launcher";
8
- import lodash_merge from "lodash.merge";
9
- import puppeteer from "puppeteer";
10
- import { createServer } from "http-server";
11
- import node_assert from "node:assert";
12
- import { createAgent, getReportFileName } from "@midscene/core/agent";
13
- import { processCacheConfig } from "@midscene/core/utils";
14
5
  import { getDebug } from "@midscene/shared/logger";
15
- import { AgentOverChromeBridge } from "@midscene/web/bridge-mode";
16
- import { stripVTControlCharacters } from "node:util";
17
- import node_process, { cwd as external_node_process_cwd } from "node:process";
18
- import { fileURLToPath } from "node:url";
6
+ import { fileURLToPath, pathToFileURL } from "node:url";
19
7
  import { lstat, readdir, readlink, realpath } from "node:fs/promises";
20
8
  import { EventEmitter } from "node:events";
21
9
  import node_stream from "node:stream";
22
10
  import { StringDecoder } from "node:string_decoder";
23
11
  import { fileURLToPath as external_url_fileURLToPath } from "url";
12
+ import { cwd as external_node_process_cwd } from "node:process";
13
+ import { interpolateEnvVars } from "@midscene/core/yaml";
14
+ import lodash_merge from "lodash.merge";
15
+ import { getMidsceneRunSubDir } from "@midscene/shared/common";
16
+ import { createRequire } from "node:module";
17
+ import "@rstest/core";
18
+ import "@midscene/web/puppeteer-agent-launcher";
19
+ import "puppeteer";
20
+ import "http-server";
21
+ import "node:assert";
22
+ import "@midscene/core/agent";
23
+ import "@midscene/core/utils";
24
+ import "@midscene/web/bridge-mode";
25
+ import "node:util";
24
26
  import * as __rspack_external_assert from "assert";
25
27
  import * as __rspack_external_crypto from "crypto";
26
28
  import * as __rspack_external_fs from "fs";
@@ -3024,1126 +3026,7 @@ var __webpack_modules__ = {
3024
3026
  "./src/index.ts" (__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
3025
3027
  var main = __webpack_require__("../../node_modules/.pnpm/dotenv@16.4.5/node_modules/dotenv/lib/main.js");
3026
3028
  var main_default = /*#__PURE__*/ __webpack_require__.n(main);
3027
- var package_namespaceObject = {
3028
- rE: "1.8.7"
3029
- };
3030
- class Node {
3031
- value;
3032
- next;
3033
- constructor(value){
3034
- this.value = value;
3035
- }
3036
- }
3037
- class Queue {
3038
- #head;
3039
- #tail;
3040
- #size;
3041
- constructor(){
3042
- this.clear();
3043
- }
3044
- enqueue(value) {
3045
- const node = new Node(value);
3046
- if (this.#head) {
3047
- this.#tail.next = node;
3048
- this.#tail = node;
3049
- } else {
3050
- this.#head = node;
3051
- this.#tail = node;
3052
- }
3053
- this.#size++;
3054
- }
3055
- dequeue() {
3056
- const current = this.#head;
3057
- if (!current) return;
3058
- this.#head = this.#head.next;
3059
- this.#size--;
3060
- return current.value;
3061
- }
3062
- peek() {
3063
- if (!this.#head) return;
3064
- return this.#head.value;
3065
- }
3066
- clear() {
3067
- this.#head = void 0;
3068
- this.#tail = void 0;
3069
- this.#size = 0;
3070
- }
3071
- get size() {
3072
- return this.#size;
3073
- }
3074
- *[Symbol.iterator]() {
3075
- let current = this.#head;
3076
- while(current){
3077
- yield current.value;
3078
- current = current.next;
3079
- }
3080
- }
3081
- *drain() {
3082
- while(this.#head)yield this.dequeue();
3083
- }
3084
- }
3085
- function pLimit(concurrency) {
3086
- validateConcurrency(concurrency);
3087
- const queue = new Queue();
3088
- let activeCount = 0;
3089
- const resumeNext = ()=>{
3090
- if (activeCount < concurrency && queue.size > 0) {
3091
- queue.dequeue()();
3092
- activeCount++;
3093
- }
3094
- };
3095
- const next = ()=>{
3096
- activeCount--;
3097
- resumeNext();
3098
- };
3099
- const run = async (function_, resolve, arguments_)=>{
3100
- const result = (async ()=>function_(...arguments_))();
3101
- resolve(result);
3102
- try {
3103
- await result;
3104
- } catch {}
3105
- next();
3106
- };
3107
- const enqueue = (function_, resolve, arguments_)=>{
3108
- new Promise((internalResolve)=>{
3109
- queue.enqueue(internalResolve);
3110
- }).then(run.bind(void 0, function_, resolve, arguments_));
3111
- (async ()=>{
3112
- await Promise.resolve();
3113
- if (activeCount < concurrency) resumeNext();
3114
- })();
3115
- };
3116
- const generator = (function_, ...arguments_)=>new Promise((resolve)=>{
3117
- enqueue(function_, resolve, arguments_);
3118
- });
3119
- Object.defineProperties(generator, {
3120
- activeCount: {
3121
- get: ()=>activeCount
3122
- },
3123
- pendingCount: {
3124
- get: ()=>queue.size
3125
- },
3126
- clearQueue: {
3127
- value () {
3128
- queue.clear();
3129
- }
3130
- },
3131
- concurrency: {
3132
- get: ()=>concurrency,
3133
- set (newConcurrency) {
3134
- validateConcurrency(newConcurrency);
3135
- concurrency = newConcurrency;
3136
- queueMicrotask(()=>{
3137
- while(activeCount < concurrency && queue.size > 0)resumeNext();
3138
- });
3139
- }
3140
- }
3141
- });
3142
- return generator;
3143
- }
3144
- function validateConcurrency(concurrency) {
3145
- if (!((Number.isInteger(concurrency) || concurrency === 1 / 0) && concurrency > 0)) throw new TypeError('Expected `concurrency` to be a number from 1 and up');
3146
- }
3147
- const debug = getDebug('create-yaml-player');
3148
- const launchServer = async (dir)=>new Promise((resolve)=>{
3149
- const server = createServer({
3150
- root: dir
3151
- });
3152
- server.listen(0, '127.0.0.1', ()=>{
3153
- resolve(server);
3154
- });
3155
- });
3156
- function resolveReportFileName(yamlReportFileName, cliTestId, yamlTestId, fileName) {
3157
- const baseName = yamlReportFileName ?? cliTestId ?? yamlTestId ?? fileName;
3158
- return getReportFileName(baseName);
3159
- }
3160
- function buildAgentOptions(yamlAgent, reportFileName, fileName) {
3161
- return {
3162
- ...yamlAgent || {},
3163
- cache: processCacheConfig(yamlAgent?.cache, fileName),
3164
- reportFileName
3165
- };
3166
- }
3167
- async function createYamlPlayer(file, script, options) {
3168
- const yamlScript = script || parseYamlScript((0, __rspack_external_node_fs_5ea92f0c.readFileSync)(file, 'utf-8'), file);
3169
- const clonedYamlScript = structuredClone(yamlScript);
3170
- const fileName = basename(file, extname(file));
3171
- const preference = {
3172
- headed: options?.headed,
3173
- keepWindow: options?.keepWindow,
3174
- reportFileName: resolveReportFileName(clonedYamlScript.agent?.reportFileName, options?.testId, clonedYamlScript.agent?.testId, fileName)
3175
- };
3176
- const player = new ScriptPlayer(clonedYamlScript, async ()=>{
3177
- const freeFn = [];
3178
- const webTarget = clonedYamlScript.web || clonedYamlScript.target;
3179
- const targetCount = [
3180
- void 0 !== webTarget,
3181
- void 0 !== clonedYamlScript.android,
3182
- void 0 !== clonedYamlScript.ios,
3183
- void 0 !== clonedYamlScript.computer,
3184
- void 0 !== clonedYamlScript.interface
3185
- ].filter(Boolean).length;
3186
- if (targetCount > 1) {
3187
- const specifiedTargets = [
3188
- void 0 !== webTarget ? 'web' : null,
3189
- void 0 !== clonedYamlScript.android ? 'android' : null,
3190
- void 0 !== clonedYamlScript.ios ? 'ios' : null,
3191
- void 0 !== clonedYamlScript.computer ? 'computer' : null,
3192
- void 0 !== clonedYamlScript.interface ? 'interface' : null
3193
- ].filter(Boolean);
3194
- throw new Error(`Only one target type can be specified, but found multiple: ${specifiedTargets.join(', ')}. Please specify only one of: web, android, ios, computer, or interface.`);
3195
- }
3196
- if (void 0 !== webTarget) {
3197
- if (void 0 !== clonedYamlScript.target) console.warn("target is deprecated, please use web instead. See https://midscenejs.com/automate-with-scripts-in-yaml for more information. Sorry for the inconvenience.");
3198
- let localServer;
3199
- let urlToVisit;
3200
- if (webTarget.serve) {
3201
- node_assert('string' == typeof webTarget.url, 'url is required in serve mode');
3202
- localServer = await launchServer(webTarget.serve);
3203
- const serverAddress = localServer.server.address();
3204
- freeFn.push({
3205
- name: 'local_server',
3206
- fn: ()=>localServer?.server.close()
3207
- });
3208
- urlToVisit = webTarget.url.startsWith('/') ? `http://${serverAddress?.address}:${serverAddress?.port}${webTarget.url}` : `http://${serverAddress?.address}:${serverAddress?.port}/${webTarget.url}`;
3209
- webTarget.url = urlToVisit;
3210
- }
3211
- if (webTarget.cdpEndpoint && webTarget.bridgeMode) throw new Error('cdpEndpoint and bridgeMode are mutually exclusive. Please specify only one.');
3212
- if (webTarget.cdpEndpoint) {
3213
- const cdpBrowser = options?.browser ?? await puppeteer.connect({
3214
- browserWSEndpoint: webTarget.cdpEndpoint,
3215
- defaultViewport: null
3216
- });
3217
- if (webTarget.chromeArgs) console.warn('chromeArgs are not supported in CDP mode (browser is already running). They will be ignored.');
3218
- const { agent, freeFn: newFreeFn } = await puppeteerAgentForTarget(webTarget, {
3219
- ...preference,
3220
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3221
- }, cdpBrowser, options?.page);
3222
- const cleanFreeFn = newFreeFn.filter((f)=>'puppeteer_browser' !== f.name);
3223
- if (!options?.browser) cleanFreeFn.push({
3224
- name: 'cdp_browser_disconnect',
3225
- fn: ()=>cdpBrowser.disconnect()
3226
- });
3227
- freeFn.push(...cleanFreeFn);
3228
- return {
3229
- agent,
3230
- freeFn
3231
- };
3232
- }
3233
- if (!webTarget.bridgeMode) {
3234
- const { agent, freeFn: newFreeFn } = await puppeteerAgentForTarget(webTarget, {
3235
- ...preference,
3236
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3237
- }, options?.browser, options?.page);
3238
- freeFn.push(...newFreeFn);
3239
- return {
3240
- agent,
3241
- freeFn
3242
- };
3243
- }
3244
- node_assert('newTabWithUrl' === webTarget.bridgeMode || 'currentTab' === webTarget.bridgeMode, `bridgeMode config value must be either "newTabWithUrl" or "currentTab", but got ${webTarget.bridgeMode}`);
3245
- if (webTarget.userAgent || null != webTarget.viewportWidth || null != webTarget.viewportHeight || null != webTarget.deviceScaleFactor || webTarget.waitForNetworkIdle || webTarget.cookie || webTarget.chromeArgs) console.warn('puppeteer options (userAgent, viewportWidth, viewportHeight, deviceScaleFactor, waitForNetworkIdle, cookie, chromeArgs) are not supported in bridge mode. They will be ignored.');
3246
- const agent = new AgentOverChromeBridge({
3247
- closeNewTabsAfterDisconnect: webTarget.closeNewTabsAfterDisconnect,
3248
- closeConflictServer: true,
3249
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3250
- });
3251
- if ('newTabWithUrl' === webTarget.bridgeMode) await agent.connectNewTabWithUrl(webTarget.url);
3252
- else {
3253
- if (webTarget.url) console.warn('url will be ignored in bridge mode with "currentTab"');
3254
- await agent.connectCurrentTab();
3255
- }
3256
- freeFn.push({
3257
- name: 'destroy_agent_over_chrome_bridge',
3258
- fn: ()=>agent.destroy()
3259
- });
3260
- return {
3261
- agent,
3262
- freeFn
3263
- };
3264
- }
3265
- if (void 0 !== clonedYamlScript.android) {
3266
- const androidTarget = clonedYamlScript.android;
3267
- const { agentFromAdbDevice } = await import("@midscene/android");
3268
- const agent = await agentFromAdbDevice(androidTarget?.deviceId, {
3269
- ...androidTarget,
3270
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3271
- });
3272
- if (androidTarget?.launch) await agent.launch(androidTarget.launch);
3273
- freeFn.push({
3274
- name: 'destroy_android_agent',
3275
- fn: ()=>agent.destroy()
3276
- });
3277
- return {
3278
- agent,
3279
- freeFn
3280
- };
3281
- }
3282
- if (void 0 !== clonedYamlScript.ios) {
3283
- const iosTarget = clonedYamlScript.ios;
3284
- const { agentFromWebDriverAgent } = await import("@midscene/ios");
3285
- const agent = await agentFromWebDriverAgent({
3286
- ...iosTarget,
3287
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3288
- });
3289
- if (iosTarget?.launch) await agent.launch(iosTarget.launch);
3290
- freeFn.push({
3291
- name: 'destroy_ios_agent',
3292
- fn: ()=>agent.destroy()
3293
- });
3294
- return {
3295
- agent,
3296
- freeFn
3297
- };
3298
- }
3299
- if (void 0 !== clonedYamlScript.computer) {
3300
- const computerTarget = clonedYamlScript.computer;
3301
- const { agentForComputer } = await import("@midscene/computer");
3302
- const agent = await agentForComputer({
3303
- ...computerTarget,
3304
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3305
- });
3306
- freeFn.push({
3307
- name: 'destroy_computer_agent',
3308
- fn: ()=>agent.destroy()
3309
- });
3310
- return {
3311
- agent,
3312
- freeFn
3313
- };
3314
- }
3315
- if (void 0 !== clonedYamlScript.interface) {
3316
- const interfaceTarget = clonedYamlScript.interface;
3317
- const moduleSpecifier = interfaceTarget.module;
3318
- let finalModuleSpecifier;
3319
- if (moduleSpecifier.startsWith('./') || moduleSpecifier.startsWith('../') || node_path.isAbsolute(moduleSpecifier)) {
3320
- const resolvedPath = join(process.cwd(), moduleSpecifier);
3321
- finalModuleSpecifier = resolvedPath;
3322
- } else finalModuleSpecifier = moduleSpecifier;
3323
- debug('importing module config', interfaceTarget.module, 'with export config', interfaceTarget.export, 'final module specifier', finalModuleSpecifier);
3324
- const importedModule = await import(finalModuleSpecifier);
3325
- const DeviceClass = interfaceTarget.export ? importedModule[interfaceTarget.export] : importedModule.default || importedModule;
3326
- debug('DeviceClass', DeviceClass, 'with param', interfaceTarget.param);
3327
- const device = new DeviceClass(interfaceTarget.param || {});
3328
- debug('creating agent from device', device);
3329
- const agent = createAgent(device, buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName));
3330
- freeFn.push({
3331
- name: 'destroy_general_interface_agent',
3332
- fn: ()=>{
3333
- agent.destroy();
3334
- }
3335
- });
3336
- return {
3337
- agent,
3338
- freeFn
3339
- };
3340
- }
3341
- throw new Error('No valid interface configuration found in the yaml script, should be either "web", "android", "ios", "computer", or "interface"');
3342
- }, void 0, file);
3343
- return player;
3344
- }
3345
- var chalk_source = __webpack_require__("../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/index.js");
3346
- var source_default = /*#__PURE__*/ __webpack_require__.n(chalk_source);
3347
- const isTTY = process.env.MIDSCENE_CLI_LOG_ON_NON_TTY ? false : process.stdout.isTTY;
3348
- const printer_indent = ' ';
3349
- const spinnerInterval = 80;
3350
- const spinnerFrames = [
3351
- '◰',
3352
- '◳',
3353
- '◲',
3354
- '◱'
3355
- ];
3356
- const currentSpinningFrame = ()=>spinnerFrames[Math.floor(Date.now() / spinnerInterval) % spinnerFrames.length];
3357
- function indicatorForStatus(status) {
3358
- if ('init' === status) return source_default().gray('◌');
3359
- if ('running' === status) return source_default().yellowBright(currentSpinningFrame());
3360
- if ('done' === status) return source_default().green('✔︎');
3361
- if ('error' === status) return source_default().red('✘');
3362
- }
3363
- const contextInfo = (context)=>{
3364
- const filePath = context.file;
3365
- const filePathToShow = external_node_path_relative(process.cwd(), filePath);
3366
- const fileNameToPrint = `${source_default().gray(`${filePathToShow}`)}`;
3367
- const fileStatusText = indicatorForStatus(context.player.status);
3368
- const contextActionText = void 0 === context.player.currentTaskIndex && 'running' === context.player.status ? source_default().gray('(navigating)') : '';
3369
- const errorText = context.player.errorInSetup ? `\n${printer_indent}${source_default().red('error:')} ${context.player.errorInSetup?.message}\n${printer_indent}${printer_indent}${context.player.errorInSetup?.stack}` : '';
3370
- const outputFile = context.player.output;
3371
- const outputText = outputFile && Object.keys(context.player.result || {}).length > 0 ? `\n${printer_indent}${source_default().gray(`output: ${outputFile}`)}` : '';
3372
- const reportFile = context.player.reportFile;
3373
- const reportText = reportFile ? `\n${printer_indent}${source_default().gray(`report: ${reportFile}`)}` : '';
3374
- const agentStatusTip = context.player.agentStatusTip;
3375
- const agentStatusText = agentStatusTip ? `\n${printer_indent}${source_default().gray(`agent status: ${agentStatusTip}`)}` : '';
3376
- const mergedText = `${fileStatusText} ${fileNameToPrint} ${contextActionText}${outputText}${reportText}${errorText}${agentStatusText}`.trim();
3377
- return {
3378
- fileNameToPrint,
3379
- fileStatusText,
3380
- contextActionText,
3381
- outputText,
3382
- reportText,
3383
- mergedText
3384
- };
3385
- };
3386
- const singleTaskInfo = (task)=>{
3387
- let stepText = '';
3388
- if ('init' === task.status) stepText = '';
3389
- else if ('running' === task.status || 'error' === task.status) if (void 0 === task.currentStep) stepText = source_default().gray('(navigating)');
3390
- else if ('number' == typeof task.currentStep) {
3391
- const actionText = '';
3392
- stepText = source_default().gray(`(task ${task.currentStep + 1}/${task.totalSteps}${actionText})`.trim());
3393
- } else stepText = source_default().gray('(unknown task)');
3394
- const errorText = 'error' === task.status ? `\n${printer_indent}${source_default().gray('error:')}\n${printer_indent}${printer_indent}${task.error?.message}` : '';
3395
- const statusText = indicatorForStatus(task.status);
3396
- const mergedLine = `${statusText} ${task.name} ${stepText}${errorText}`;
3397
- return {
3398
- nameText: task.name,
3399
- stepText,
3400
- errorText,
3401
- itemStatusText: statusText,
3402
- mergedLine
3403
- };
3404
- };
3405
- function paddingLines(lines) {
3406
- return lines.map((line)=>`${printer_indent}${line}`);
3407
- }
3408
- const contextTaskListSummary = (taskStatusArray, context)=>{
3409
- const prefixLines = [];
3410
- const currentLine = [];
3411
- const suffixText = [];
3412
- const { mergedText: fileInfo } = contextInfo(context);
3413
- if (!context.player.errorInSetup) for (const task of taskStatusArray){
3414
- const { mergedLine } = singleTaskInfo(task);
3415
- if ('init' === context.player.status) suffixText.push(mergedLine);
3416
- else if ('running' === context.player.status) currentLine.push(mergedLine);
3417
- else if ('done' === context.player.status) prefixLines.push(mergedLine);
3418
- else if ('error' === context.player.status) prefixLines.push(mergedLine);
3419
- }
3420
- const lines = [
3421
- fileInfo
3422
- ];
3423
- if (prefixLines.length > 0) lines.push(...paddingLines(prefixLines));
3424
- if (currentLine.length > 0) lines.push(...paddingLines(currentLine));
3425
- if (suffixText.length > 0) lines.push(...paddingLines(suffixText));
3426
- return lines.join('\n');
3427
- };
3428
- const copyProperty = (to, from, property, ignoreNonConfigurable)=>{
3429
- if ('length' === property || 'prototype' === property) return;
3430
- if ('arguments' === property || 'caller' === property) return;
3431
- const toDescriptor = Object.getOwnPropertyDescriptor(to, property);
3432
- const fromDescriptor = Object.getOwnPropertyDescriptor(from, property);
3433
- if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) return;
3434
- Object.defineProperty(to, property, fromDescriptor);
3435
- };
3436
- const canCopyProperty = function(toDescriptor, fromDescriptor) {
3437
- return void 0 === toDescriptor || toDescriptor.configurable || toDescriptor.writable === fromDescriptor.writable && toDescriptor.enumerable === fromDescriptor.enumerable && toDescriptor.configurable === fromDescriptor.configurable && (toDescriptor.writable || toDescriptor.value === fromDescriptor.value);
3438
- };
3439
- const changePrototype = (to, from)=>{
3440
- const fromPrototype = Object.getPrototypeOf(from);
3441
- if (fromPrototype === Object.getPrototypeOf(to)) return;
3442
- Object.setPrototypeOf(to, fromPrototype);
3443
- };
3444
- const wrappedToString = (withName, fromBody)=>`/* Wrapped ${withName}*/\n${fromBody}`;
3445
- const toStringDescriptor = Object.getOwnPropertyDescriptor(Function.prototype, 'toString');
3446
- const toStringName = Object.getOwnPropertyDescriptor(Function.prototype.toString, 'name');
3447
- const changeToString = (to, from, name)=>{
3448
- const withName = '' === name ? '' : `with ${name.trim()}() `;
3449
- const newToString = wrappedToString.bind(null, withName, from.toString());
3450
- Object.defineProperty(newToString, 'name', toStringName);
3451
- const { writable, enumerable, configurable } = toStringDescriptor;
3452
- Object.defineProperty(to, 'toString', {
3453
- value: newToString,
3454
- writable,
3455
- enumerable,
3456
- configurable
3457
- });
3458
- };
3459
- function mimicFunction(to, from, { ignoreNonConfigurable = false } = {}) {
3460
- const { name } = to;
3461
- for (const property of Reflect.ownKeys(from))copyProperty(to, from, property, ignoreNonConfigurable);
3462
- changePrototype(to, from);
3463
- changeToString(to, from, name);
3464
- return to;
3465
- }
3466
- const calledFunctions = new WeakMap();
3467
- const onetime_onetime = (function_, options = {})=>{
3468
- if ('function' != typeof function_) throw new TypeError('Expected a function');
3469
- let returnValue;
3470
- let callCount = 0;
3471
- const functionName = function_.displayName || function_.name || '<anonymous>';
3472
- const onetime = function(...arguments_) {
3473
- calledFunctions.set(onetime, ++callCount);
3474
- if (1 === callCount) {
3475
- returnValue = function_.apply(this, arguments_);
3476
- function_ = void 0;
3477
- } else if (true === options.throw) throw new Error(`Function \`${functionName}\` can only be called once`);
3478
- return returnValue;
3479
- };
3480
- mimicFunction(onetime, function_);
3481
- calledFunctions.set(onetime, callCount);
3482
- return onetime;
3483
- };
3484
- onetime_onetime.callCount = (function_)=>{
3485
- if (!calledFunctions.has(function_)) throw new Error(`The given function \`${function_.name}\` is not wrapped by the \`onetime\` package`);
3486
- return calledFunctions.get(function_);
3487
- };
3488
- const node_modules_onetime = onetime_onetime;
3489
- const signals = [];
3490
- signals.push('SIGHUP', 'SIGINT', 'SIGTERM');
3491
- if ('win32' !== process.platform) signals.push('SIGALRM', 'SIGABRT', 'SIGVTALRM', 'SIGXCPU', 'SIGXFSZ', 'SIGUSR2', 'SIGTRAP', 'SIGSYS', 'SIGQUIT', 'SIGIOT');
3492
- if ('linux' === process.platform) signals.push('SIGIO', 'SIGPOLL', 'SIGPWR', 'SIGSTKFLT');
3493
- const processOk = (process1)=>!!process1 && 'object' == typeof process1 && 'function' == typeof process1.removeListener && 'function' == typeof process1.emit && 'function' == typeof process1.reallyExit && 'function' == typeof process1.listeners && 'function' == typeof process1.kill && 'number' == typeof process1.pid && 'function' == typeof process1.on;
3494
- const kExitEmitter = Symbol.for('signal-exit emitter');
3495
- const global = globalThis;
3496
- const ObjectDefineProperty = Object.defineProperty.bind(Object);
3497
- class Emitter {
3498
- emitted = {
3499
- afterExit: false,
3500
- exit: false
3501
- };
3502
- listeners = {
3503
- afterExit: [],
3504
- exit: []
3505
- };
3506
- count = 0;
3507
- id = Math.random();
3508
- constructor(){
3509
- if (global[kExitEmitter]) return global[kExitEmitter];
3510
- ObjectDefineProperty(global, kExitEmitter, {
3511
- value: this,
3512
- writable: false,
3513
- enumerable: false,
3514
- configurable: false
3515
- });
3516
- }
3517
- on(ev, fn) {
3518
- this.listeners[ev].push(fn);
3519
- }
3520
- removeListener(ev, fn) {
3521
- const list = this.listeners[ev];
3522
- const i = list.indexOf(fn);
3523
- if (-1 === i) return;
3524
- if (0 === i && 1 === list.length) list.length = 0;
3525
- else list.splice(i, 1);
3526
- }
3527
- emit(ev, code, signal) {
3528
- if (this.emitted[ev]) return false;
3529
- this.emitted[ev] = true;
3530
- let ret = false;
3531
- for (const fn of this.listeners[ev])ret = true === fn(code, signal) || ret;
3532
- if ('exit' === ev) ret = this.emit('afterExit', code, signal) || ret;
3533
- return ret;
3534
- }
3535
- }
3536
- class SignalExitBase {
3537
- }
3538
- const signalExitWrap = (handler)=>({
3539
- onExit (cb, opts) {
3540
- return handler.onExit(cb, opts);
3541
- },
3542
- load () {
3543
- return handler.load();
3544
- },
3545
- unload () {
3546
- return handler.unload();
3547
- }
3548
- });
3549
- class SignalExitFallback extends SignalExitBase {
3550
- onExit() {
3551
- return ()=>{};
3552
- }
3553
- load() {}
3554
- unload() {}
3555
- }
3556
- class SignalExit extends SignalExitBase {
3557
- #hupSig = 'win32' === mjs_process.platform ? 'SIGINT' : 'SIGHUP';
3558
- #emitter = new Emitter();
3559
- #process;
3560
- #originalProcessEmit;
3561
- #originalProcessReallyExit;
3562
- #sigListeners = {};
3563
- #loaded = false;
3564
- constructor(process1){
3565
- super();
3566
- this.#process = process1;
3567
- this.#sigListeners = {};
3568
- for (const sig of signals)this.#sigListeners[sig] = ()=>{
3569
- const listeners = this.#process.listeners(sig);
3570
- let { count } = this.#emitter;
3571
- const p = process1;
3572
- if ('object' == typeof p.__signal_exit_emitter__ && 'number' == typeof p.__signal_exit_emitter__.count) count += p.__signal_exit_emitter__.count;
3573
- if (listeners.length === count) {
3574
- this.unload();
3575
- const ret = this.#emitter.emit('exit', null, sig);
3576
- const s = 'SIGHUP' === sig ? this.#hupSig : sig;
3577
- if (!ret) process1.kill(process1.pid, s);
3578
- }
3579
- };
3580
- this.#originalProcessReallyExit = process1.reallyExit;
3581
- this.#originalProcessEmit = process1.emit;
3582
- }
3583
- onExit(cb, opts) {
3584
- if (!processOk(this.#process)) return ()=>{};
3585
- if (false === this.#loaded) this.load();
3586
- const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
3587
- this.#emitter.on(ev, cb);
3588
- return ()=>{
3589
- this.#emitter.removeListener(ev, cb);
3590
- if (0 === this.#emitter.listeners['exit'].length && 0 === this.#emitter.listeners['afterExit'].length) this.unload();
3591
- };
3592
- }
3593
- load() {
3594
- if (this.#loaded) return;
3595
- this.#loaded = true;
3596
- this.#emitter.count += 1;
3597
- for (const sig of signals)try {
3598
- const fn = this.#sigListeners[sig];
3599
- if (fn) this.#process.on(sig, fn);
3600
- } catch (_) {}
3601
- this.#process.emit = (ev, ...a)=>this.#processEmit(ev, ...a);
3602
- this.#process.reallyExit = (code)=>this.#processReallyExit(code);
3603
- }
3604
- unload() {
3605
- if (!this.#loaded) return;
3606
- this.#loaded = false;
3607
- signals.forEach((sig)=>{
3608
- const listener = this.#sigListeners[sig];
3609
- if (!listener) throw new Error('Listener not defined for signal: ' + sig);
3610
- try {
3611
- this.#process.removeListener(sig, listener);
3612
- } catch (_) {}
3613
- });
3614
- this.#process.emit = this.#originalProcessEmit;
3615
- this.#process.reallyExit = this.#originalProcessReallyExit;
3616
- this.#emitter.count -= 1;
3617
- }
3618
- #processReallyExit(code) {
3619
- if (!processOk(this.#process)) return 0;
3620
- this.#process.exitCode = code || 0;
3621
- this.#emitter.emit('exit', this.#process.exitCode, null);
3622
- return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
3623
- }
3624
- #processEmit(ev, ...args) {
3625
- const og = this.#originalProcessEmit;
3626
- if (!('exit' === ev && processOk(this.#process))) return og.call(this.#process, ev, ...args);
3627
- {
3628
- if ('number' == typeof args[0]) this.#process.exitCode = args[0];
3629
- const ret = og.call(this.#process, ev, ...args);
3630
- this.#emitter.emit('exit', this.#process.exitCode, null);
3631
- return ret;
3632
- }
3633
- }
3634
- }
3635
- const mjs_process = globalThis.process;
3636
- const { onExit, load, unload } = signalExitWrap(processOk(mjs_process) ? new SignalExit(mjs_process) : new SignalExitFallback());
3637
- const terminal = node_process.stderr.isTTY ? node_process.stderr : node_process.stdout.isTTY ? node_process.stdout : void 0;
3638
- const restoreCursor = terminal ? node_modules_onetime(()=>{
3639
- onExit(()=>{
3640
- terminal.write('\u001B[?25h');
3641
- }, {
3642
- alwaysLast: true
3643
- });
3644
- }) : ()=>{};
3645
- const restore_cursor = restoreCursor;
3646
- function _define_property(obj, key, value) {
3647
- if (key in obj) Object.defineProperty(obj, key, {
3648
- value: value,
3649
- enumerable: true,
3650
- configurable: true,
3651
- writable: true
3652
- });
3653
- else obj[key] = value;
3654
- return obj;
3655
- }
3656
- const DEFAULT_RENDER_INTERVAL = 160;
3657
- const ESC = '\x1B[';
3658
- const CLEAR_LINE = `${ESC}K`;
3659
- const MOVE_CURSOR_ONE_ROW_UP = `${ESC}1A`;
3660
- const HIDE_CURSOR = `${ESC}?25l`;
3661
- const SHOW_CURSOR = `${ESC}?25h`;
3662
- const SYNC_START = `${ESC}?2026h`;
3663
- const SYNC_END = `${ESC}?2026l`;
3664
- class TTYWindowRenderer {
3665
- start() {
3666
- this.finished = false;
3667
- this.renderInterval = setInterval(()=>this.flushBuffer(), this.options.interval);
3668
- }
3669
- stop() {
3670
- this.flushBuffer();
3671
- this.write(SHOW_CURSOR, 'output');
3672
- this.cleanups.splice(0).map((fn)=>fn());
3673
- clearInterval(this.renderInterval);
3674
- }
3675
- finish() {
3676
- this.finished = true;
3677
- this.flushBuffer();
3678
- clearInterval(this.renderInterval);
3679
- }
3680
- flushBuffer() {
3681
- if (0 === this.buffer.length) return this.render();
3682
- let current;
3683
- for (const next of this.buffer.splice(0)){
3684
- if (!current) {
3685
- current = next;
3686
- continue;
3687
- }
3688
- if (current.type !== next.type) {
3689
- this.render(current.message, current.type);
3690
- current = next;
3691
- continue;
3692
- }
3693
- current.message += next.message;
3694
- }
3695
- if (current) this.render(current?.message, current?.type);
3696
- }
3697
- render(message, type = 'output') {
3698
- if (this.finished) {
3699
- this.clearWindow();
3700
- return this.write(message || '', type);
3701
- }
3702
- const windowContent = this.options.getWindow();
3703
- const rowCount = getRenderedRowCount(windowContent, this.options.outputStream);
3704
- let padding = this.windowHeight - rowCount;
3705
- if (padding > 0 && message) padding -= getRenderedRowCount([
3706
- message
3707
- ], this.options.outputStream);
3708
- this.write(SYNC_START);
3709
- this.clearWindow();
3710
- if (message) this.write(message, type);
3711
- if (padding > 0) this.write('\n'.repeat(padding));
3712
- this.write(windowContent.join('\n'));
3713
- this.write(SYNC_END);
3714
- this.windowHeight = rowCount + Math.max(0, padding);
3715
- }
3716
- clearWindow() {
3717
- if (0 === this.windowHeight) return;
3718
- this.write(CLEAR_LINE);
3719
- for(let i = 1; i < this.windowHeight; i++)this.write(`${MOVE_CURSOR_ONE_ROW_UP}${CLEAR_LINE}`);
3720
- this.windowHeight = 0;
3721
- }
3722
- interceptStream(stream, type) {
3723
- const original = stream.write;
3724
- stream.write = (chunk, _, callback)=>{
3725
- if (chunk) if (this.finished) this.write(chunk.toString(), type);
3726
- else this.buffer.push({
3727
- type,
3728
- message: chunk.toString()
3729
- });
3730
- callback?.();
3731
- };
3732
- return function() {
3733
- stream.write = original;
3734
- };
3735
- }
3736
- write(message, type = 'output') {
3737
- this.streams[type](message);
3738
- }
3739
- constructor(options){
3740
- _define_property(this, "options", void 0);
3741
- _define_property(this, "streams", void 0);
3742
- _define_property(this, "buffer", []);
3743
- _define_property(this, "renderInterval", void 0);
3744
- _define_property(this, "windowHeight", 0);
3745
- _define_property(this, "finished", false);
3746
- _define_property(this, "cleanups", []);
3747
- this.options = {
3748
- interval: DEFAULT_RENDER_INTERVAL,
3749
- ...options
3750
- };
3751
- this.streams = {
3752
- output: options.outputStream.write.bind(options.outputStream),
3753
- error: options.errorStream.write.bind(options.errorStream)
3754
- };
3755
- this.cleanups.push(this.interceptStream(process.stdout, 'output'), this.interceptStream(process.stderr, 'error'));
3756
- restore_cursor();
3757
- this.write(HIDE_CURSOR, 'output');
3758
- this.start();
3759
- }
3760
- }
3761
- function getRenderedRowCount(contents, stream) {
3762
- let count = 0;
3763
- const columns = 'columns' in stream ? stream.columns : 80;
3764
- for (const content of contents){
3765
- const rows = content.split('\n');
3766
- for (const row of rows){
3767
- const text = stripVTControlCharacters(row);
3768
- count += Math.max(1, Math.ceil(text.length / columns));
3769
- }
3770
- }
3771
- return count;
3772
- }
3773
- function batch_runner_define_property(obj, key, value) {
3774
- if (key in obj) Object.defineProperty(obj, key, {
3775
- value: value,
3776
- enumerable: true,
3777
- configurable: true,
3778
- writable: true
3779
- });
3780
- else obj[key] = value;
3781
- return obj;
3782
- }
3783
- class BatchRunner {
3784
- async run() {
3785
- const { keepWindow, headed } = this.config;
3786
- this.printExecutionPlan();
3787
- const fileContextList = [];
3788
- let browser = null;
3789
- let sharedPage = null;
3790
- try {
3791
- for (const file of this.config.files){
3792
- const fileConfig = await this.loadFileConfig(file);
3793
- const context = await this.createFileContext(file, fileConfig, {
3794
- headed,
3795
- keepWindow
3796
- });
3797
- fileContextList.push(context);
3798
- }
3799
- const needsBrowser = fileContextList.some((ctx)=>Object.keys(ctx.executionConfig.web || ctx.executionConfig.target || {}).length > 0);
3800
- if (needsBrowser && this.config.shareBrowserContext) {
3801
- const globalWebConfig = this.config.globalConfig?.web;
3802
- if (globalWebConfig?.cdpEndpoint) browser = await puppeteer.connect({
3803
- browserWSEndpoint: globalWebConfig.cdpEndpoint,
3804
- defaultViewport: null
3805
- });
3806
- else {
3807
- const width = globalWebConfig?.viewportWidth ?? defaultViewportWidth;
3808
- const height = globalWebConfig?.viewportHeight ?? defaultViewportHeight;
3809
- const args = buildChromeArgs({
3810
- userAgent: globalWebConfig?.userAgent,
3811
- windowSize: headed ? {
3812
- width,
3813
- height
3814
- } : void 0,
3815
- chromeArgs: globalWebConfig?.chromeArgs
3816
- });
3817
- browser = await puppeteer.launch({
3818
- headless: !headed,
3819
- defaultViewport: headed ? null : {
3820
- width,
3821
- height
3822
- },
3823
- args,
3824
- acceptInsecureCerts: globalWebConfig?.acceptInsecureCerts
3825
- });
3826
- }
3827
- sharedPage = await browser.newPage();
3828
- for (const context of fileContextList){
3829
- context.options.browser = browser;
3830
- context.options.page = sharedPage;
3831
- }
3832
- }
3833
- const { executedResults, notExecutedContexts } = await this.executeFiles(fileContextList);
3834
- this.results = await this.processResults(executedResults, notExecutedContexts);
3835
- } finally{
3836
- if (browser && !this.config.keepWindow) {
3837
- const isCdp = !!this.config.globalConfig?.web?.cdpEndpoint;
3838
- if (isCdp) browser.disconnect();
3839
- else await browser.close();
3840
- }
3841
- await this.generateOutputIndex();
3842
- }
3843
- return this.results;
3844
- }
3845
- async createFileContext(file, fileConfig, options) {
3846
- const { globalConfig } = this.config;
3847
- const clonedFileConfig = JSON.parse(JSON.stringify(fileConfig));
3848
- if (clonedFileConfig.target) {
3849
- clonedFileConfig.web = {
3850
- ...clonedFileConfig.target,
3851
- ...clonedFileConfig.web
3852
- };
3853
- delete clonedFileConfig.target;
3854
- }
3855
- if (globalConfig?.target) {
3856
- globalConfig.web = {
3857
- ...globalConfig.target,
3858
- ...globalConfig.web
3859
- };
3860
- delete globalConfig.target;
3861
- }
3862
- const executionConfig = lodash_merge(clonedFileConfig, globalConfig);
3863
- return {
3864
- file,
3865
- executionConfig,
3866
- options
3867
- };
3868
- }
3869
- async executeFiles(fileContextList) {
3870
- const executedResults = [];
3871
- const notExecutedContexts = [];
3872
- const allFileContexts = [];
3873
- for (const context of fileContextList){
3874
- const player = await createYamlPlayer(context.file, context.executionConfig, context.options);
3875
- allFileContexts.push({
3876
- file: context.file,
3877
- player
3878
- });
3879
- }
3880
- let ttyRenderer;
3881
- if (isTTY) {
3882
- const summaryContents = ()=>{
3883
- const summary = [
3884
- ''
3885
- ];
3886
- for (const context of allFileContexts)summary.push(contextTaskListSummary(context.player.taskStatusList, context));
3887
- summary.push('');
3888
- return summary;
3889
- };
3890
- ttyRenderer = new TTYWindowRenderer({
3891
- outputStream: process.stdout,
3892
- errorStream: process.stderr,
3893
- getWindow: summaryContents,
3894
- interval: spinnerInterval
3895
- });
3896
- ttyRenderer.start();
3897
- }
3898
- try {
3899
- const executeFile = async (context)=>{
3900
- const allFileContext = allFileContexts.find((c)=>c.file === context.file);
3901
- if (!allFileContext) throw new Error(`Player not found for file: ${context.file}`);
3902
- if (!isTTY) {
3903
- const { mergedText } = contextInfo(allFileContext);
3904
- console.log(mergedText);
3905
- }
3906
- if (context.outputPath) allFileContext.player.output = context.outputPath;
3907
- const startTime = Date.now();
3908
- await allFileContext.player.run();
3909
- const endTime = Date.now();
3910
- const duration = endTime - startTime;
3911
- const executedContext = {
3912
- file: context.file,
3913
- player: allFileContext.player,
3914
- duration
3915
- };
3916
- if (!isTTY) console.log(contextTaskListSummary(allFileContext.player.taskStatusList, executedContext));
3917
- return executedContext;
3918
- };
3919
- await this.executeConcurrently(fileContextList, executeFile, executedResults, notExecutedContexts);
3920
- if (!isTTY) {
3921
- console.log('\n📋 Execution Results:');
3922
- for (const context of executedResults)console.log(contextTaskListSummary(context.player.taskStatusList, context));
3923
- }
3924
- } finally{
3925
- if (ttyRenderer) ttyRenderer.stop();
3926
- }
3927
- return {
3928
- executedResults,
3929
- notExecutedContexts
3930
- };
3931
- }
3932
- async executeConcurrently(fileContextList, executeFile, executedResults, notExecutedContexts) {
3933
- const limit = pLimit(this.config.concurrent);
3934
- if (this.config.continueOnError) {
3935
- const tasks = fileContextList.map((context)=>limit(async ()=>{
3936
- const executedContext = await executeFile(context);
3937
- executedResults.push(executedContext);
3938
- }));
3939
- await Promise.allSettled(tasks);
3940
- } else {
3941
- let shouldStop = false;
3942
- const stopLock = {
3943
- value: false
3944
- };
3945
- const tasks = fileContextList.map((context)=>limit(async ()=>{
3946
- if (stopLock.value) return void notExecutedContexts.push({
3947
- file: context.file,
3948
- player: null
3949
- });
3950
- const executedContext = await executeFile(context);
3951
- executedResults.push(executedContext);
3952
- if ('error' === executedContext.player.status && !stopLock.value) {
3953
- stopLock.value = true;
3954
- shouldStop = true;
3955
- }
3956
- }));
3957
- await Promise.allSettled(tasks);
3958
- if (shouldStop) {
3959
- for (const context of fileContextList)if (!executedResults.some((r)=>r.file === context.file) && !notExecutedContexts.some((ctx)=>ctx.file === context.file)) notExecutedContexts.push({
3960
- file: context.file,
3961
- player: null
3962
- });
3963
- }
3964
- }
3965
- }
3966
- async processResults(executedContexts, notExecutedContexts) {
3967
- const results = [];
3968
- for (const context of executedContexts){
3969
- const { file, player, duration } = context;
3970
- const hasFailedTasks = player.taskStatusList?.some((task)=>'error' === task.status) ?? false;
3971
- const hasPlayerError = 'error' === player.status;
3972
- let success;
3973
- let resultType;
3974
- if (hasPlayerError) {
3975
- success = false;
3976
- resultType = 'failed';
3977
- } else if (hasFailedTasks) {
3978
- success = false;
3979
- resultType = 'partialFailed';
3980
- } else {
3981
- success = true;
3982
- resultType = 'success';
3983
- }
3984
- let reportFile;
3985
- if (player.reportFile) reportFile = player.reportFile;
3986
- let outputPath = player.output || void 0;
3987
- if (outputPath && !(0, __rspack_external_node_fs_5ea92f0c.existsSync)(outputPath)) outputPath = void 0;
3988
- let errorMessage;
3989
- if (player.errorInSetup?.message) errorMessage = player.errorInSetup.message;
3990
- else if (hasPlayerError || hasFailedTasks) {
3991
- const taskErrors = player.taskStatusList?.filter((task)=>'error' === task.status && task.error?.message).map((task)=>task.error.message);
3992
- errorMessage = taskErrors && taskErrors.length > 0 ? taskErrors.join('; ') : hasPlayerError ? 'Execution failed' : 'Some tasks failed';
3993
- }
3994
- results.push({
3995
- file,
3996
- success,
3997
- executed: true,
3998
- output: outputPath,
3999
- report: reportFile,
4000
- duration,
4001
- resultType,
4002
- error: errorMessage
4003
- });
4004
- }
4005
- for (const context of notExecutedContexts)results.push({
4006
- file: context.file,
4007
- success: false,
4008
- executed: false,
4009
- output: void 0,
4010
- report: void 0,
4011
- duration: 0,
4012
- resultType: 'notExecuted',
4013
- error: 'Not executed (previous task failed)'
4014
- });
4015
- return results;
4016
- }
4017
- async loadFileConfig(file) {
4018
- const content = (0, __rspack_external_node_fs_5ea92f0c.readFileSync)(file, 'utf8');
4019
- return parseYamlScript(content, file);
4020
- }
4021
- getSummaryAbsolutePath() {
4022
- return external_node_path_resolve(getMidsceneRunSubDir('output'), this.config.summary);
4023
- }
4024
- printExecutionPlan() {
4025
- console.log(' Scripts:');
4026
- for (const file of this.config.files)console.log(` - ${file}`);
4027
- console.log('📋 Execution plan');
4028
- console.log(` Concurrency: ${this.config.concurrent}`);
4029
- console.log(` Keep window: ${this.config.keepWindow}`);
4030
- console.log(` Headed: ${this.config.headed}`);
4031
- console.log(` Continue on error: ${this.config.continueOnError}`);
4032
- console.log(` Share browser context: ${this.config.shareBrowserContext ?? false}`);
4033
- console.log(` Summary output: ${this.config.summary}`);
4034
- }
4035
- async generateOutputIndex() {
4036
- const indexPath = external_node_path_resolve(getMidsceneRunSubDir('output'), this.config.summary);
4037
- const outputDir = dirname(indexPath);
4038
- try {
4039
- (0, __rspack_external_node_fs_5ea92f0c.mkdirSync)(outputDir, {
4040
- recursive: true
4041
- });
4042
- const indexData = {
4043
- summary: {
4044
- total: this.results.length,
4045
- successful: this.results.filter((r)=>'success' === r.resultType).length,
4046
- failed: this.results.filter((r)=>'failed' === r.resultType).length,
4047
- partialFailed: this.results.filter((r)=>'partialFailed' === r.resultType).length,
4048
- notExecuted: this.results.filter((r)=>'notExecuted' === r.resultType).length,
4049
- totalDuration: this.results.reduce((sum, r)=>sum + (r.duration || 0), 0),
4050
- generatedAt: new Date().toLocaleString()
4051
- },
4052
- results: this.results.map((result)=>({
4053
- script: external_node_path_relative(outputDir, result.file),
4054
- success: result.success,
4055
- resultType: result.resultType,
4056
- output: result.output ? (()=>{
4057
- const relativePath = external_node_path_relative(outputDir, result.output);
4058
- return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
4059
- })() : void 0,
4060
- report: result.report ? external_node_path_relative(outputDir, result.report) : void 0,
4061
- error: result.error,
4062
- duration: result.duration
4063
- }))
4064
- };
4065
- (0, __rspack_external_node_fs_5ea92f0c.writeFileSync)(indexPath, JSON.stringify(indexData, null, 2));
4066
- console.log('Execution finished:');
4067
- } catch (error) {
4068
- console.error('Failed to generate output index:', error);
4069
- }
4070
- }
4071
- getExecutionSummary() {
4072
- const successful = this.results.filter((r)=>'success' === r.resultType).length;
4073
- const failed = this.results.filter((r)=>'failed' === r.resultType).length;
4074
- const partialFailed = this.results.filter((r)=>'partialFailed' === r.resultType).length;
4075
- const notExecuted = this.results.filter((r)=>'notExecuted' === r.resultType).length;
4076
- return {
4077
- total: this.results.length,
4078
- successful,
4079
- failed,
4080
- partialFailed,
4081
- notExecuted,
4082
- totalDuration: this.results.reduce((sum, r)=>sum + (r.duration || 0), 0)
4083
- };
4084
- }
4085
- getFailedFiles() {
4086
- return this.results.filter((r)=>'failed' === r.resultType).map((r)=>r.file);
4087
- }
4088
- getPartialFailedFiles() {
4089
- return this.results.filter((r)=>'partialFailed' === r.resultType).map((r)=>r.file);
4090
- }
4091
- getNotExecutedFiles() {
4092
- return this.results.filter((r)=>'notExecuted' === r.resultType).map((r)=>r.file);
4093
- }
4094
- getSuccessfulFiles() {
4095
- return this.results.filter((r)=>'success' === r.resultType).map((r)=>r.file);
4096
- }
4097
- getResults() {
4098
- return [
4099
- ...this.results
4100
- ];
4101
- }
4102
- printExecutionSummary() {
4103
- const summary = this.getExecutionSummary();
4104
- const success = 0 === summary.failed && 0 === summary.partialFailed && 0 === summary.notExecuted;
4105
- console.log('\n📊 Execution Summary:');
4106
- console.log(` Total files: ${summary.total}`);
4107
- console.log(` Successful: ${summary.successful}`);
4108
- console.log(` Failed: ${summary.failed}`);
4109
- console.log(` Partial failed: ${summary.partialFailed}`);
4110
- console.log(` Not executed: ${summary.notExecuted}`);
4111
- console.log(` Duration: ${(summary.totalDuration / 1000).toFixed(2)}s`);
4112
- console.log(` Summary: ${this.getSummaryAbsolutePath()}`);
4113
- if (summary.successful > 0) {
4114
- console.log('\n✅ Successful files:');
4115
- this.getSuccessfulFiles().forEach((file)=>{
4116
- console.log(` ${file}`);
4117
- });
4118
- }
4119
- if (summary.failed > 0) {
4120
- console.log('\n❌ Failed files');
4121
- this.getFailedFiles().forEach((file)=>{
4122
- console.log(` ${file}`);
4123
- });
4124
- }
4125
- if (summary.partialFailed > 0) {
4126
- console.log('\n⚠️ Partial failed files (some tasks failed with continueOnError)');
4127
- this.getPartialFailedFiles().forEach((file)=>{
4128
- console.log(` ${file}`);
4129
- });
4130
- }
4131
- if (summary.notExecuted > 0) {
4132
- console.log('\n⏸️ Not executed files');
4133
- this.getNotExecutedFiles().forEach((file)=>{
4134
- console.log(` ${file}`);
4135
- });
4136
- }
4137
- if (success) console.log('\n🎉 All files executed successfully!');
4138
- else console.log('\n⚠️ Some files failed or were not executed.');
4139
- return success;
4140
- }
4141
- constructor(config){
4142
- batch_runner_define_property(this, "config", void 0);
4143
- batch_runner_define_property(this, "results", []);
4144
- this.config = config;
4145
- }
4146
- }
3029
+ var package_namespaceObject = JSON.parse('{"rE":"1.8.8-beta-20260601092817.0"}');
4147
3030
  var brace_expansion = __webpack_require__("../../node_modules/.pnpm/brace-expansion@2.0.1/node_modules/brace-expansion/index.js");
4148
3031
  const MAX_PATTERN_LENGTH = 65536;
4149
3032
  const assertValidPattern = (pattern)=>{
@@ -9549,7 +8432,7 @@ var __webpack_modules__ = {
9549
8432
  yargsParser.looksLikeNumber = looksLikeNumber;
9550
8433
  var external_assert_ = __webpack_require__("assert");
9551
8434
  new RegExp("\x1b(?:\\[(?:\\d+[ABCDEFGJKSTm]|\\d+;\\d+[Hfm]|\\d+;\\d+;\\d+m|6n|s|u|\\?25[lh])|\\w)", 'g');
9552
- const platform_shims_node = {
8435
+ const node = {
9553
8436
  fs: {
9554
8437
  readFileSync: external_fs_.readFileSync,
9555
8438
  writeFile: external_fs_.writeFile
@@ -9699,7 +8582,7 @@ var __webpack_modules__ = {
9699
8582
  locale: y18n.locale
9700
8583
  };
9701
8584
  }
9702
- const y18n_y18n = (opts)=>lib_y18n(opts, platform_shims_node);
8585
+ const y18n_y18n = (opts)=>lib_y18n(opts, node);
9703
8586
  const node_modules_y18n = y18n_y18n;
9704
8587
  let esm_dirname;
9705
8588
  try {
@@ -10283,7 +9166,7 @@ var __webpack_modules__ = {
10283
9166
  function resolveYamlMerge(data) {
10284
9167
  return '<<' === data || null === data;
10285
9168
  }
10286
- var merge = new js_yaml_type('tag:yaml.org,2002:merge', {
9169
+ var js_yaml_merge = new js_yaml_type('tag:yaml.org,2002:merge', {
10287
9170
  kind: 'scalar',
10288
9171
  resolve: resolveYamlMerge
10289
9172
  });
@@ -10442,7 +9325,7 @@ var __webpack_modules__ = {
10442
9325
  var _default = core.extend({
10443
9326
  implicit: [
10444
9327
  js_yaml_timestamp,
10445
- merge
9328
+ js_yaml_merge
10446
9329
  ],
10447
9330
  explicit: [
10448
9331
  binary,
@@ -11790,7 +10673,7 @@ var __webpack_modules__ = {
11790
10673
  throw new Error('Function yaml.' + from + " is removed in js-yaml 4. Use yaml." + to + ' instead, which is now safe by default.');
11791
10674
  };
11792
10675
  }
11793
- var js_yaml_load = loader.load;
10676
+ var load = loader.load;
11794
10677
  loader.loadAll;
11795
10678
  dumper.dump;
11796
10679
  renamed('safeLoad', 'load');
@@ -11818,19 +10701,19 @@ var __webpack_modules__ = {
11818
10701
  return allFiles;
11819
10702
  }
11820
10703
  async function parseConfigYaml(configYamlPath) {
11821
- const basePath = dirname(external_node_path_resolve(configYamlPath));
10704
+ const basePath = external_node_path_dirname(external_node_path_resolve(configYamlPath));
11822
10705
  const configContent = (0, __rspack_external_node_fs_5ea92f0c.readFileSync)(configYamlPath, 'utf8');
11823
10706
  const interpolatedContent = interpolateEnvVars(configContent);
11824
10707
  let configYaml;
11825
10708
  try {
11826
- configYaml = js_yaml_load(interpolatedContent);
10709
+ configYaml = load(interpolatedContent);
11827
10710
  } catch (error) {
11828
10711
  throw new Error(`Failed to parse config YAML: ${error}`);
11829
10712
  }
11830
10713
  if (!configYaml?.files || !Array.isArray(configYaml?.files)) throw new Error('Config YAML must contain a "files" array');
11831
10714
  const files = await expandFilePatterns(configYaml?.files, basePath);
11832
10715
  if (0 === files.length) throw new Error('No YAML files found matching the patterns in "files"');
11833
- const configFileName = basename(configYamlPath, extname(configYamlPath));
10716
+ const configFileName = external_node_path_basename(configYamlPath, external_node_path_extname(configYamlPath));
11834
10717
  const timestamp = Date.now();
11835
10718
  const defaultSummary = `${configFileName}-${timestamp}.json`;
11836
10719
  const config = {
@@ -11867,7 +10750,7 @@ var __webpack_modules__ = {
11867
10750
  const finalHeaded = keepWindow || headed;
11868
10751
  let files = parsedConfig.files;
11869
10752
  if (options?.files && options.files.length > 0) {
11870
- const basePath = dirname(external_node_path_resolve(configYamlPath));
10753
+ const basePath = external_node_path_dirname(external_node_path_resolve(configYamlPath));
11871
10754
  files = await expandFilePatterns(options.files, basePath);
11872
10755
  }
11873
10756
  return {
@@ -11907,7 +10790,7 @@ var __webpack_modules__ = {
11907
10790
  }
11908
10791
  };
11909
10792
  }
11910
- const cli_utils_debug = getDebug('midscene:cli');
10793
+ const debug = getDebug('midscene:cli');
11911
10794
  function kebabToCamel(str) {
11912
10795
  return str.replace(/-([a-z])/g, (_, letter)=>letter.toUpperCase());
11913
10796
  }
@@ -11966,7 +10849,7 @@ Usage:
11966
10849
  type: 'boolean',
11967
10850
  description: `Turn on logging to help debug why certain keys or values are not being set as you expect, default is ${config_factory_defaultConfig.dotenvDebug}`
11968
10851
  }
11969
- }).version('version', 'Show version number', "1.8.7").help().epilogue(`For complete list of configuration options, please visit:
10852
+ }).version('version', 'Show version number', "1.8.8-beta-20260601092817.0").help().epilogue(`For complete list of configuration options, please visit:
11970
10853
  • Web options: https://midscenejs.com/automate-with-scripts-in-yaml#the-web-part
11971
10854
  • Android options: https://midscenejs.com/automate-with-scripts-in-yaml#the-android-part
11972
10855
  • iOS options: https://midscenejs.com/automate-with-scripts-in-yaml#the-ios-part
@@ -11976,7 +10859,7 @@ Examples:
11976
10859
  $0 script.yaml --android.device-id emulator-5554 --android.ime-strategy yadb-for-non-ascii
11977
10860
  $0 script.yaml --ios.wda-port 8100 --ios.auto-dismiss-keyboard`).wrap(yargs().terminalWidth());
11978
10861
  const argv = await args.argv;
11979
- cli_utils_debug('argv', argv);
10862
+ debug('argv', argv);
11980
10863
  const transformedArgv = {
11981
10864
  ...argv
11982
10865
  };
@@ -12000,7 +10883,7 @@ Examples:
12000
10883
  };
12001
10884
  };
12002
10885
  async function matchYamlFiles(fileGlob, options) {
12003
- if ((0, __rspack_external_node_fs_5ea92f0c.existsSync)(fileGlob) && (0, __rspack_external_node_fs_5ea92f0c.statSync)(fileGlob).isDirectory()) fileGlob = join(fileGlob, '**/*.{yml,yaml}');
10886
+ if ((0, __rspack_external_node_fs_5ea92f0c.existsSync)(fileGlob) && (0, __rspack_external_node_fs_5ea92f0c.statSync)(fileGlob).isDirectory()) fileGlob = external_node_path_join(fileGlob, '**/*.{yml,yaml}');
12004
10887
  const { cwd } = options || {};
12005
10888
  const ignore = [
12006
10889
  '**/node_modules/**'
@@ -12014,6 +10897,341 @@ Examples:
12014
10897
  });
12015
10898
  return files.filter((file)=>file.endsWith('.yml') || file.endsWith('.yaml')).sort();
12016
10899
  }
10900
+ const notExecutedError = 'Not executed (previous task failed)';
10901
+ function execution_summary_createNotExecutedYamlResult(file) {
10902
+ return {
10903
+ file,
10904
+ success: false,
10905
+ executed: false,
10906
+ output: void 0,
10907
+ report: void 0,
10908
+ duration: 0,
10909
+ resultType: 'notExecuted',
10910
+ error: notExecutedError
10911
+ };
10912
+ }
10913
+ function getExecutionSummary(results) {
10914
+ return {
10915
+ total: results.length,
10916
+ successful: getResultsByType(results, 'success').length,
10917
+ failed: getResultsByType(results, 'failed').length,
10918
+ partialFailed: getResultsByType(results, 'partialFailed').length,
10919
+ notExecuted: getResultsByType(results, 'notExecuted').length,
10920
+ totalDuration: results.reduce((sum, r)=>sum + (r.duration || 0), 0)
10921
+ };
10922
+ }
10923
+ function getResultsByType(results, resultType) {
10924
+ return results.filter((result)=>result.resultType === resultType);
10925
+ }
10926
+ function getSummaryAbsolutePath(summary) {
10927
+ return external_node_path_resolve(getMidsceneRunSubDir('output'), summary);
10928
+ }
10929
+ function execution_summary_writeExecutionSummaryFile(summary, results) {
10930
+ const indexPath = getSummaryAbsolutePath(summary);
10931
+ const outputDir = external_node_path_dirname(indexPath);
10932
+ (0, __rspack_external_node_fs_5ea92f0c.mkdirSync)(outputDir, {
10933
+ recursive: true
10934
+ });
10935
+ const executionSummary = getExecutionSummary(results);
10936
+ const indexData = {
10937
+ summary: {
10938
+ ...executionSummary,
10939
+ generatedAt: new Date().toLocaleString()
10940
+ },
10941
+ results: results.map((result)=>({
10942
+ script: external_node_path_relative(outputDir, result.file),
10943
+ success: result.success,
10944
+ resultType: result.resultType,
10945
+ output: result.output ? (()=>{
10946
+ const relativePath = external_node_path_relative(outputDir, result.output);
10947
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
10948
+ })() : void 0,
10949
+ report: result.report ? external_node_path_relative(outputDir, result.report) : void 0,
10950
+ error: result.error,
10951
+ duration: result.duration
10952
+ }))
10953
+ };
10954
+ (0, __rspack_external_node_fs_5ea92f0c.writeFileSync)(indexPath, JSON.stringify(indexData, null, 2));
10955
+ return indexPath;
10956
+ }
10957
+ function execution_summary_printExecutionPlan(config) {
10958
+ console.log(' Scripts:');
10959
+ for (const file of config.files)console.log(` - ${file}`);
10960
+ console.log('📋 Execution plan');
10961
+ console.log(` Concurrency: ${config.concurrent}`);
10962
+ console.log(` Keep window: ${config.keepWindow}`);
10963
+ console.log(` Headed: ${config.headed}`);
10964
+ console.log(` Continue on error: ${config.continueOnError}`);
10965
+ console.log(` Share browser context: ${config.shareBrowserContext ?? false}`);
10966
+ console.log(` Summary output: ${config.summary}`);
10967
+ }
10968
+ function execution_summary_printExecutionFinished() {
10969
+ console.log('Execution finished:');
10970
+ }
10971
+ function printExecutionSummary(results, summaryPath) {
10972
+ const summary = getExecutionSummary(results);
10973
+ const successfulFiles = getResultsByType(results, 'success');
10974
+ const failedFiles = getResultsByType(results, 'failed');
10975
+ const partialFailedFiles = getResultsByType(results, 'partialFailed');
10976
+ const notExecutedFiles = getResultsByType(results, 'notExecuted');
10977
+ const success = 0 === summary.failed && 0 === summary.partialFailed && 0 === summary.notExecuted;
10978
+ console.log('\n📊 Execution Summary:');
10979
+ console.log(` Total files: ${summary.total}`);
10980
+ console.log(` Successful: ${summary.successful}`);
10981
+ console.log(` Failed: ${summary.failed}`);
10982
+ console.log(` Partial failed: ${summary.partialFailed}`);
10983
+ console.log(` Not executed: ${summary.notExecuted}`);
10984
+ console.log(` Duration: ${(summary.totalDuration / 1000).toFixed(2)}s`);
10985
+ console.log(` Summary: ${summaryPath}`);
10986
+ if (successfulFiles.length > 0) {
10987
+ console.log('\n✅ Successful files:');
10988
+ successfulFiles.forEach((result)=>{
10989
+ console.log(` ${result.file}`);
10990
+ });
10991
+ }
10992
+ if (failedFiles.length > 0) {
10993
+ console.log('\n❌ Failed files');
10994
+ failedFiles.forEach((result)=>{
10995
+ console.log(` ${result.file}`);
10996
+ if (result.error) console.log(` Error: ${result.error}`);
10997
+ });
10998
+ }
10999
+ if (partialFailedFiles.length > 0) {
11000
+ console.log('\n⚠️ Partial failed files (some tasks failed with continueOnError)');
11001
+ partialFailedFiles.forEach((result)=>{
11002
+ console.log(` ${result.file}`);
11003
+ if (result.error) console.log(` Error: ${result.error}`);
11004
+ });
11005
+ }
11006
+ if (notExecutedFiles.length > 0) {
11007
+ console.log('\n⏸️ Not executed files');
11008
+ notExecutedFiles.forEach((result)=>{
11009
+ console.log(` ${result.file}`);
11010
+ });
11011
+ }
11012
+ if (success) console.log('\n🎉 All files executed successfully!');
11013
+ else console.log('\n⚠️ Some files failed or were not executed.');
11014
+ return success;
11015
+ }
11016
+ const DEFAULT_YAML_TEST_TIMEOUT = 0;
11017
+ const toPosixPath = (value)=>value.split(external_node_path_sep).join('/');
11018
+ const toImportLiteral = (value)=>JSON.stringify(toPosixPath(value));
11019
+ const toVirtualModuleId = (fileStem)=>`virtual:midscene-yaml/${fileStem}.test.ts`;
11020
+ const safeFileStem = (file, index)=>{
11021
+ const base = external_node_path_basename(file, external_node_path_extname(file)).replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
11022
+ return `${String(index + 1).padStart(3, '0')}-${base || 'case'}`;
11023
+ };
11024
+ const resolveTestName = (projectDir, yamlFile)=>{
11025
+ const relativePath = external_node_path_relative(projectDir, yamlFile);
11026
+ return toPosixPath(relativePath.startsWith('..') ? yamlFile : relativePath);
11027
+ };
11028
+ const createGeneratedTestContent = (options)=>{
11029
+ const testOptions = {
11030
+ testName: options.testName,
11031
+ yamlFile: options.yamlFile,
11032
+ resultFile: options.resultFile,
11033
+ ...options.caseOptions ? {
11034
+ caseOptions: options.caseOptions
11035
+ } : {},
11036
+ ...options.webRuntimeOptions ? {
11037
+ webRuntimeOptions: options.webRuntimeOptions
11038
+ } : {}
11039
+ };
11040
+ return `import { defineYamlCaseTest } from ${toImportLiteral(options.frameworkImport)};
11041
+
11042
+ defineYamlCaseTest(${JSON.stringify(testOptions, null, 2)});
11043
+ `;
11044
+ };
11045
+ const createGeneratedBatchTestContent = (options)=>{
11046
+ const testOptions = {
11047
+ testName: options.testName,
11048
+ config: options.config,
11049
+ resultFiles: options.resultFiles
11050
+ };
11051
+ return `import { defineYamlBatchTest } from ${toImportLiteral(options.frameworkImport)};
11052
+
11053
+ defineYamlBatchTest(${JSON.stringify(testOptions, null, 2)});
11054
+ `;
11055
+ };
11056
+ const resolveDefaultFrameworkImport = ()=>{
11057
+ const entry = process.argv[1] ? external_node_path_resolve(process.argv[1]) : '';
11058
+ const candidates = [
11059
+ entry ? external_node_path_join(external_node_path_dirname(entry), 'framework', 'index.js') : '',
11060
+ entry ? external_node_path_join(external_node_path_dirname(entry), '..', 'dist', 'lib', 'framework', 'index.js') : ''
11061
+ ].filter(Boolean);
11062
+ const matched = candidates.find((candidate)=>(0, __rspack_external_node_fs_5ea92f0c.existsSync)(candidate));
11063
+ return matched || '@midscene/cli/dist/lib/framework/index.js';
11064
+ };
11065
+ function createRstestYamlProject(options) {
11066
+ const projectDir = external_node_path_resolve(options.projectDir || process.cwd());
11067
+ const outputDir = options.outputDir || external_node_path_join(getMidsceneRunSubDir('tmp'), `rstest-yaml-${Date.now()}`);
11068
+ const resultDir = options.resultDir || external_node_path_join(outputDir, 'results');
11069
+ const frameworkImport = options.frameworkImport || resolveDefaultFrameworkImport();
11070
+ const testTimeout = options.testTimeout ?? DEFAULT_YAML_TEST_TIMEOUT;
11071
+ (0, __rspack_external_node_fs_5ea92f0c.rmSync)(outputDir, {
11072
+ recursive: true,
11073
+ force: true
11074
+ });
11075
+ (0, __rspack_external_node_fs_5ea92f0c.mkdirSync)(resultDir, {
11076
+ recursive: true
11077
+ });
11078
+ const virtualModules = {};
11079
+ const cases = options.files.map((file, index)=>{
11080
+ const yamlFile = external_node_path_resolve(file);
11081
+ const testName = resolveTestName(projectDir, yamlFile);
11082
+ const fileStem = safeFileStem(yamlFile, index);
11083
+ const resultFile = external_node_path_join(resultDir, `${fileStem}.json`);
11084
+ const testModule = toVirtualModuleId(fileStem);
11085
+ virtualModules[testModule] = createGeneratedTestContent({
11086
+ frameworkImport,
11087
+ yamlFile,
11088
+ resultFile,
11089
+ testName,
11090
+ caseOptions: options.caseOptions?.[yamlFile],
11091
+ webRuntimeOptions: options.webRuntimeOptions?.[yamlFile]
11092
+ });
11093
+ return {
11094
+ yamlFile,
11095
+ testModule,
11096
+ resultFile,
11097
+ testName
11098
+ };
11099
+ });
11100
+ if (options.batchConfig) {
11101
+ const batchModule = 'virtual:midscene-yaml/batch.test.ts';
11102
+ const resultFiles = Object.fromEntries(cases.map((item)=>[
11103
+ item.yamlFile,
11104
+ item.resultFile
11105
+ ]));
11106
+ return {
11107
+ projectDir,
11108
+ outputDir,
11109
+ resultDir,
11110
+ include: [
11111
+ batchModule
11112
+ ],
11113
+ virtualModules: {
11114
+ [batchModule]: createGeneratedBatchTestContent({
11115
+ frameworkImport,
11116
+ testName: 'midscene yaml batch',
11117
+ config: options.batchConfig,
11118
+ resultFiles
11119
+ })
11120
+ },
11121
+ cases,
11122
+ maxConcurrency: 1,
11123
+ testTimeout,
11124
+ bail: options.bail
11125
+ };
11126
+ }
11127
+ return {
11128
+ projectDir,
11129
+ outputDir,
11130
+ resultDir,
11131
+ include: cases.map((item)=>item.testModule),
11132
+ virtualModules,
11133
+ cases,
11134
+ maxConcurrency: options.maxConcurrency,
11135
+ testTimeout,
11136
+ bail: options.bail
11137
+ };
11138
+ }
11139
+ const requireFromCliEntry = ()=>{
11140
+ const entry = process.argv[1] ? external_node_path_resolve(process.argv[1]) : external_node_path_join(process.cwd(), 'midscene-cli.js');
11141
+ return createRequire(entry);
11142
+ };
11143
+ const resolvePackageFromRstestCore = (packageName)=>{
11144
+ const require1 = requireFromCliEntry();
11145
+ const rstestPackageJsonPath = require1.resolve('@rstest/core/package.json');
11146
+ return createRequire(rstestPackageJsonPath).resolve(packageName);
11147
+ };
11148
+ const formatRunError = (error)=>error.stack || `${error.name}: ${error.message}`;
11149
+ async function runRstestYamlProject(options) {
11150
+ const [{ runRstest }, { rspack }] = await Promise.all([
11151
+ import("@rstest/core/api"),
11152
+ import(pathToFileURL(resolvePackageFromRstestCore('@rsbuild/core')).href)
11153
+ ]);
11154
+ const { project } = options;
11155
+ const maxConcurrency = void 0 !== project.maxConcurrency ? Math.max(1, project.maxConcurrency) : void 0;
11156
+ const inlineConfig = {
11157
+ root: project.projectDir,
11158
+ include: project.include,
11159
+ testEnvironment: 'node',
11160
+ testTimeout: project.testTimeout,
11161
+ ...void 0 !== maxConcurrency ? {
11162
+ maxConcurrency
11163
+ } : {},
11164
+ ...void 0 !== maxConcurrency ? {
11165
+ pool: {
11166
+ maxWorkers: maxConcurrency,
11167
+ minWorkers: maxConcurrency
11168
+ }
11169
+ } : {},
11170
+ ...void 0 !== project.bail ? {
11171
+ bail: project.bail
11172
+ } : {},
11173
+ reporters: [],
11174
+ tools: {
11175
+ rspack: (_config, { appendPlugins })=>{
11176
+ appendPlugins(new rspack.experiments.VirtualModulesPlugin(project.virtualModules));
11177
+ }
11178
+ }
11179
+ };
11180
+ const result = await runRstest({
11181
+ cwd: options.cwd || project.projectDir,
11182
+ inlineConfig
11183
+ });
11184
+ if (!result.ok && 'pipe' !== options.stdio && result.unhandledErrors.length) console.error(result.unhandledErrors.map((error)=>formatRunError(error)).join('\n'));
11185
+ return result.ok ? 0 : 1;
11186
+ }
11187
+ const createCaseOptions = (config)=>{
11188
+ const caseOptions = {};
11189
+ for (const file of config.files)caseOptions[external_node_path_resolve(file)] = {
11190
+ globalConfig: config.globalConfig
11191
+ };
11192
+ return caseOptions;
11193
+ };
11194
+ const createWebRuntimeOptions = (config, runtimeOptions)=>{
11195
+ const caseOptions = {};
11196
+ for (const file of config.files)caseOptions[external_node_path_resolve(file)] = {
11197
+ headed: runtimeOptions.headed ?? config.headed,
11198
+ keepWindow: runtimeOptions.keepWindow ?? config.keepWindow
11199
+ };
11200
+ return caseOptions;
11201
+ };
11202
+ const readProjectResults = (project)=>project.cases.map((item)=>{
11203
+ if ((0, __rspack_external_node_fs_5ea92f0c.existsSync)(item.resultFile)) return JSON.parse((0, __rspack_external_node_fs_5ea92f0c.readFileSync)(item.resultFile, 'utf8'));
11204
+ return execution_summary_createNotExecutedYamlResult(item.yamlFile);
11205
+ });
11206
+ async function runFrameworkTestConfig(config, commandOptions = {}) {
11207
+ execution_summary_printExecutionPlan(config);
11208
+ const projectDir = external_node_path_resolve(commandOptions.projectDir || process.cwd());
11209
+ const project = createRstestYamlProject({
11210
+ files: config.files,
11211
+ projectDir,
11212
+ outputDir: commandOptions.outputDir,
11213
+ frameworkImport: commandOptions.frameworkImport,
11214
+ caseOptions: createCaseOptions(config),
11215
+ webRuntimeOptions: createWebRuntimeOptions(config, commandOptions),
11216
+ maxConcurrency: commandOptions.concurrent ?? config.concurrent,
11217
+ bail: config.continueOnError ? 0 : 1,
11218
+ batchConfig: config.shareBrowserContext ? config : void 0
11219
+ });
11220
+ const runner = commandOptions.rstestRunner || runRstestYamlProject;
11221
+ const exitCode = await runner({
11222
+ project,
11223
+ cwd: projectDir,
11224
+ stdio: commandOptions.stdio
11225
+ });
11226
+ const results = readProjectResults(project);
11227
+ const summaryPath = execution_summary_writeExecutionSummaryFile(config.summary, results);
11228
+ execution_summary_printExecutionFinished();
11229
+ const success = printExecutionSummary(results, summaryPath);
11230
+ return success ? exitCode : 1;
11231
+ }
11232
+ getDebug('create-yaml-player');
11233
+ __webpack_require__("../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/index.js");
11234
+ process.env.MIDSCENE_CLI_LOG_ON_NON_TTY || process.stdout.isTTY;
12017
11235
  Promise.resolve((async ()=>{
12018
11236
  const rawArgs = process.argv.slice(2);
12019
11237
  const [firstArg] = rawArgs;
@@ -12072,7 +11290,7 @@ Examples:
12072
11290
  console.error('Could not create a valid configuration.');
12073
11291
  process.exit(1);
12074
11292
  }
12075
- const dotEnvConfigFile = join(process.cwd(), '.env');
11293
+ const dotEnvConfigFile = external_node_path_join(process.cwd(), '.env');
12076
11294
  if ((0, __rspack_external_node_fs_5ea92f0c.existsSync)(dotEnvConfigFile)) {
12077
11295
  console.log(` Env file: ${dotEnvConfigFile}`);
12078
11296
  main_default().config({
@@ -12081,16 +11299,11 @@ Examples:
12081
11299
  override: config.dotenvOverride
12082
11300
  });
12083
11301
  }
12084
- const executor = new BatchRunner(config);
12085
- await executor.run();
12086
- const success = executor.printExecutionSummary();
11302
+ const exitCode = await runFrameworkTestConfig(config);
12087
11303
  if (config.keepWindow) setInterval(()=>{
12088
11304
  console.log('browser is still running, use ctrl+c to stop it');
12089
11305
  }, 5000);
12090
- else {
12091
- if (!success) process.exit(1);
12092
- process.exit(0);
12093
- }
11306
+ else process.exit(exitCode);
12094
11307
  })().catch((e1)=>{
12095
11308
  console.error(e1);
12096
11309
  process.exit(1);