@weapp-vite/miniprogram-automator 1.0.2 → 1.0.3
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/index.d.mts +0 -1
- package/dist/index.mjs +59 -17
- package/dist/{launch-Bd3TZy1I.mjs → launch-Didv0lMX.mjs} +229 -225
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -577,11 +577,11 @@ var Connection = class Connection extends EventEmitter {
|
|
|
577
577
|
//#endregion
|
|
578
578
|
//#region src/headless.ts
|
|
579
579
|
async function launchHeadlessAutomator(options) {
|
|
580
|
-
return await (await import("./launch-
|
|
580
|
+
return await (await import("./launch-Didv0lMX.mjs")).launch({ projectPath: options.projectPath });
|
|
581
581
|
}
|
|
582
582
|
//#endregion
|
|
583
583
|
//#region package.json
|
|
584
|
-
var version = "1.0.
|
|
584
|
+
var version = "1.0.3";
|
|
585
585
|
//#endregion
|
|
586
586
|
//#region src/Native.ts
|
|
587
587
|
/** Native 的实现。 */
|
|
@@ -770,6 +770,8 @@ function extractPluginId(p) {
|
|
|
770
770
|
//#endregion
|
|
771
771
|
//#region src/MiniProgram.ts
|
|
772
772
|
const CLOSE_STEP_TIMEOUT = 2e3;
|
|
773
|
+
const CURRENT_PAGE_RETRIES = 3;
|
|
774
|
+
const CURRENT_PAGE_RETRY_DELAY = 400;
|
|
773
775
|
function sleep(ms) {
|
|
774
776
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
775
777
|
}
|
|
@@ -792,6 +794,9 @@ function isFnStr(value) {
|
|
|
792
794
|
const trimmed = trim(value);
|
|
793
795
|
return startWith(trimmed, "function") || startWith(trimmed, "() =>");
|
|
794
796
|
}
|
|
797
|
+
function isCurrentPageProtocolTimeout(error) {
|
|
798
|
+
return error instanceof Error && "code" in error && error.code === "DEVTOOLS_PROTOCOL_TIMEOUT" && "method" in error && error.method === "App.getCurrentPage";
|
|
799
|
+
}
|
|
795
800
|
/** MiniProgram 的实现。 */
|
|
796
801
|
var MiniProgram = class extends EventEmitter {
|
|
797
802
|
appBindings = /* @__PURE__ */ new Map();
|
|
@@ -830,12 +835,20 @@ var MiniProgram = class extends EventEmitter {
|
|
|
830
835
|
return await this.changeRoute("switchTab", url);
|
|
831
836
|
}
|
|
832
837
|
async currentPage() {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
838
|
+
let lastError;
|
|
839
|
+
for (let attempt = 1; attempt <= CURRENT_PAGE_RETRIES; attempt += 1) try {
|
|
840
|
+
const { pageId, path, query } = await this.send("App.getCurrentPage");
|
|
841
|
+
return Page.create(this.connection, {
|
|
842
|
+
id: pageId,
|
|
843
|
+
path,
|
|
844
|
+
query
|
|
845
|
+
}, this.pageMap);
|
|
846
|
+
} catch (error) {
|
|
847
|
+
lastError = error;
|
|
848
|
+
if (!isCurrentPageProtocolTimeout(error) || attempt >= CURRENT_PAGE_RETRIES) throw error;
|
|
849
|
+
await sleep(CURRENT_PAGE_RETRY_DELAY);
|
|
850
|
+
}
|
|
851
|
+
throw lastError;
|
|
839
852
|
}
|
|
840
853
|
async systemInfo() {
|
|
841
854
|
return await this.callWxMethod("getSystemInfoSync");
|
|
@@ -1005,6 +1018,7 @@ const DEFAULT_TIMEOUT = 3e4;
|
|
|
1005
1018
|
const DEFAULT_RUNTIME_PROVIDER_ENV = "WEAPP_VITE_AUTOMATOR_RUNTIME_PROVIDER";
|
|
1006
1019
|
const LEGACY_RUNTIME_PROVIDER_ENV = "WEAPP_VITE_E2E_RUNTIME_PROVIDER";
|
|
1007
1020
|
const EXTENSION_CONTEXT_INVALIDATED_RE = /Extension context invalidated/i;
|
|
1021
|
+
const WINDOWS_BATCH_CLI_RE = /\.(?:bat|cmd)$/i;
|
|
1008
1022
|
let localhostListenPatched = false;
|
|
1009
1023
|
function isExtensionContextInvalidatedError(error) {
|
|
1010
1024
|
return error instanceof Error && EXTENSION_CONTEXT_INVALIDATED_RE.test(error.message);
|
|
@@ -1026,6 +1040,25 @@ function patchNetListenToLoopback() {
|
|
|
1026
1040
|
return rawListen.apply(this, args);
|
|
1027
1041
|
};
|
|
1028
1042
|
}
|
|
1043
|
+
/** IConnectOptions 的类型定义。 */
|
|
1044
|
+
function shouldUseWindowsCommandShell(cliPath) {
|
|
1045
|
+
return isWindows && WINDOWS_BATCH_CLI_RE.test(cliPath);
|
|
1046
|
+
}
|
|
1047
|
+
function escapeWindowsCmdArg(arg) {
|
|
1048
|
+
const escaped = arg.replace(/"/g, "\"\"").replace(/%/g, "%%");
|
|
1049
|
+
return /[\s"&<>^|()]/.test(arg) ? `"${escaped}"` : escaped;
|
|
1050
|
+
}
|
|
1051
|
+
function resolveWindowsBatchSpawn(cliPath, args) {
|
|
1052
|
+
return {
|
|
1053
|
+
file: process.env.ComSpec || "cmd.exe",
|
|
1054
|
+
args: [
|
|
1055
|
+
"/d",
|
|
1056
|
+
"/s",
|
|
1057
|
+
"/c",
|
|
1058
|
+
`"${[cliPath, ...args].map(escapeWindowsCmdArg).join(" ")}"`
|
|
1059
|
+
]
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1029
1062
|
function resolveRuntimeProvider(options) {
|
|
1030
1063
|
return options.runtimeProvider || process.env[DEFAULT_RUNTIME_PROVIDER_ENV] || process.env[LEGACY_RUNTIME_PROVIDER_ENV] || "devtools";
|
|
1031
1064
|
}
|
|
@@ -1045,7 +1078,8 @@ var Launcher = class {
|
|
|
1045
1078
|
if (!await import("node:fs/promises").then((fs) => fs.access(projectPath).then(() => true).catch(() => false))) throw new Error(`Project path ${projectPath} doesn't exist`);
|
|
1046
1079
|
if (!isEmpty(projectConfig)) await this.extendProjectConfig(projectConfig, projectPath);
|
|
1047
1080
|
let processError = null;
|
|
1048
|
-
let
|
|
1081
|
+
let processExitCode = null;
|
|
1082
|
+
let processSignal = null;
|
|
1049
1083
|
args = [
|
|
1050
1084
|
...args,
|
|
1051
1085
|
"auto",
|
|
@@ -1058,17 +1092,25 @@ var Launcher = class {
|
|
|
1058
1092
|
else if (ticket) args.push("--ticket", ticket);
|
|
1059
1093
|
if (trustProject) args.push("--trust-project");
|
|
1060
1094
|
try {
|
|
1061
|
-
const
|
|
1095
|
+
const spawnTarget = shouldUseWindowsCommandShell(cliPath) ? resolveWindowsBatchSpawn(cliPath, args) : {
|
|
1096
|
+
file: cliPath,
|
|
1097
|
+
args
|
|
1098
|
+
};
|
|
1099
|
+
const child = spawn(spawnTarget.file, spawnTarget.args, {
|
|
1062
1100
|
stdio: "ignore",
|
|
1063
|
-
cwd: cwd || void 0
|
|
1101
|
+
cwd: cwd || void 0,
|
|
1102
|
+
...shouldUseWindowsCommandShell(cliPath) ? {
|
|
1103
|
+
windowsHide: true,
|
|
1104
|
+
windowsVerbatimArguments: true
|
|
1105
|
+
} : {}
|
|
1064
1106
|
});
|
|
1065
1107
|
child.on("error", (error) => {
|
|
1066
1108
|
processError = error;
|
|
1067
1109
|
});
|
|
1068
|
-
child.on("exit", () => {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
}
|
|
1110
|
+
child.on("exit", (code, signal) => {
|
|
1111
|
+
processExitCode = code;
|
|
1112
|
+
processSignal = signal;
|
|
1113
|
+
if (code !== 0 || signal) processError = /* @__PURE__ */ new Error(`DevTools cli exited unexpectedly with code ${code ?? "null"}${signal ? ` and signal ${signal}` : ""}`);
|
|
1072
1114
|
});
|
|
1073
1115
|
child.unref();
|
|
1074
1116
|
} catch (error) {
|
|
@@ -1078,7 +1120,7 @@ var Launcher = class {
|
|
|
1078
1120
|
let lastConnectError = null;
|
|
1079
1121
|
await waitUntil(async () => {
|
|
1080
1122
|
try {
|
|
1081
|
-
if (processError
|
|
1123
|
+
if (processError) return true;
|
|
1082
1124
|
const candidate = await this.connectTool({ wsEndpoint: `ws://127.0.0.1:${port}` });
|
|
1083
1125
|
try {
|
|
1084
1126
|
await candidate.checkVersion();
|
|
@@ -1098,7 +1140,7 @@ var Launcher = class {
|
|
|
1098
1140
|
if (!miniProgram) {
|
|
1099
1141
|
if (processError) throw new Error("Failed to launch wechat web devTools, please make sure cliPath is correctly specified");
|
|
1100
1142
|
if (lastConnectError) throw lastConnectError;
|
|
1101
|
-
if (
|
|
1143
|
+
if (processExitCode !== null || processSignal) throw new Error("Failed to launch wechat web devTools, please make sure http port is open");
|
|
1102
1144
|
throw new Error("Failed connecting to devtools websocket endpoint");
|
|
1103
1145
|
}
|
|
1104
1146
|
const resolvedMiniProgram = miniProgram;
|
|
@@ -27,9 +27,12 @@ function createAppInstance(definition) {
|
|
|
27
27
|
return instance;
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
|
-
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance.ts
|
|
30
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/shared.ts
|
|
31
31
|
const ARRAY_INDEX_PATH_RE$1 = /\[(\d+)\]/g;
|
|
32
32
|
const ARRAY_INDEX_SEGMENT_RE$1 = /^\d+$/;
|
|
33
|
+
function cloneObject$1(value) {
|
|
34
|
+
return JSON.parse(JSON.stringify(value));
|
|
35
|
+
}
|
|
33
36
|
function bindFunction$1(target, key, value) {
|
|
34
37
|
if (typeof value !== "function") {
|
|
35
38
|
target[key] = value;
|
|
@@ -37,14 +40,14 @@ function bindFunction$1(target, key, value) {
|
|
|
37
40
|
}
|
|
38
41
|
target[key] = (...args) => value.apply(target, args);
|
|
39
42
|
}
|
|
40
|
-
function cloneObject$1(value) {
|
|
41
|
-
return JSON.parse(JSON.stringify(value));
|
|
42
|
-
}
|
|
43
43
|
function cloneValue$1(value) {
|
|
44
44
|
if (Array.isArray(value)) return JSON.parse(JSON.stringify(value));
|
|
45
45
|
if (value && typeof value === "object") return cloneObject$1(value);
|
|
46
46
|
return value;
|
|
47
47
|
}
|
|
48
|
+
function cloneRecord(value) {
|
|
49
|
+
return cloneObject$1(value);
|
|
50
|
+
}
|
|
48
51
|
function parseDataPath$1(path) {
|
|
49
52
|
return path.replace(ARRAY_INDEX_PATH_RE$1, ".$1").split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
50
53
|
}
|
|
@@ -70,13 +73,70 @@ function assignByPath$1(target, path, value) {
|
|
|
70
73
|
const normalizedLeafSegment = isArrayIndexSegment$1(leafSegment) ? Number(leafSegment) : leafSegment;
|
|
71
74
|
current[normalizedLeafSegment] = value;
|
|
72
75
|
}
|
|
76
|
+
function mergeRecord(...records) {
|
|
77
|
+
return Object.assign({}, ...records.filter(Boolean));
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/observers.ts
|
|
81
|
+
function runPropertyObservers(definition, instance, changedKeys, previousProperties) {
|
|
82
|
+
const propOptions = definition.properties;
|
|
83
|
+
if (!propOptions || typeof propOptions !== "object" || Array.isArray(propOptions)) return;
|
|
84
|
+
for (const changedKey of changedKeys) {
|
|
85
|
+
const option = propOptions[changedKey];
|
|
86
|
+
if (!option || typeof option !== "object" || Array.isArray(option)) continue;
|
|
87
|
+
const observer = option.observer;
|
|
88
|
+
if (typeof observer !== "function") continue;
|
|
89
|
+
observer.call(instance, instance.properties[changedKey], previousProperties[changedKey]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function normalizeObserverPattern(pattern) {
|
|
93
|
+
return pattern.split(",").map((item) => item.trim()).filter(Boolean);
|
|
94
|
+
}
|
|
95
|
+
function resolveObservedValue(instance, pattern) {
|
|
96
|
+
if (pattern === "**") return {
|
|
97
|
+
...instance.properties,
|
|
98
|
+
...instance.data
|
|
99
|
+
};
|
|
100
|
+
const segments = parseDataPath$1(pattern);
|
|
101
|
+
if (segments.length === 0) return;
|
|
102
|
+
const [rootSegment, ...restSegments] = segments;
|
|
103
|
+
let current = (Object.hasOwn(instance.properties, rootSegment) ? instance.properties : instance.data)?.[rootSegment];
|
|
104
|
+
for (const segment of restSegments) {
|
|
105
|
+
const normalizedSegment = isArrayIndexSegment$1(segment) ? Number(segment) : segment;
|
|
106
|
+
current = current?.[normalizedSegment];
|
|
107
|
+
}
|
|
108
|
+
return current;
|
|
109
|
+
}
|
|
110
|
+
function matchObserverPattern(pattern, changedKeys) {
|
|
111
|
+
if (pattern === "**") return changedKeys.length > 0;
|
|
112
|
+
const patterns = normalizeObserverPattern(pattern);
|
|
113
|
+
return changedKeys.some((changedKey) => {
|
|
114
|
+
return patterns.some((item) => {
|
|
115
|
+
if (item === "**") return true;
|
|
116
|
+
if (changedKey === item) return true;
|
|
117
|
+
return changedKey.startsWith(`${item}.`) || changedKey.startsWith(`${item}[`);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function runComponentObservers(definition, instance, changedKeys, previousProperties = {}) {
|
|
122
|
+
if (changedKeys.length === 0) return;
|
|
123
|
+
runPropertyObservers(definition, instance, changedKeys, previousProperties);
|
|
124
|
+
const observers = definition.observers;
|
|
125
|
+
if (observers && typeof observers === "object" && !Array.isArray(observers)) for (const [pattern, handler] of Object.entries(observers)) {
|
|
126
|
+
if (typeof handler !== "function" || !matchObserverPattern(pattern, changedKeys)) continue;
|
|
127
|
+
const args = normalizeObserverPattern(pattern).map((item) => resolveObservedValue(instance, item));
|
|
128
|
+
handler.call(instance, ...args);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/properties.ts
|
|
73
133
|
function resolveInitialData$1(definition) {
|
|
74
134
|
const rawData = definition.data;
|
|
75
135
|
if (typeof rawData === "function") {
|
|
76
136
|
const next = rawData.call(definition);
|
|
77
|
-
return next && typeof next === "object" && !Array.isArray(next) ?
|
|
137
|
+
return next && typeof next === "object" && !Array.isArray(next) ? cloneRecord(next) : {};
|
|
78
138
|
}
|
|
79
|
-
return rawData && typeof rawData === "object" && !Array.isArray(rawData) ?
|
|
139
|
+
return rawData && typeof rawData === "object" && !Array.isArray(rawData) ? cloneRecord(rawData) : {};
|
|
80
140
|
}
|
|
81
141
|
function isBehaviorDefinition(value) {
|
|
82
142
|
return !!value && typeof value === "object" && !Array.isArray(value) && value.__isHeadlessBehavior__ === true;
|
|
@@ -91,9 +151,6 @@ function flattenBehaviors(definition) {
|
|
|
91
151
|
}
|
|
92
152
|
return flattened;
|
|
93
153
|
}
|
|
94
|
-
function mergeRecord(...records) {
|
|
95
|
-
return Object.assign({}, ...records.filter(Boolean));
|
|
96
|
-
}
|
|
97
154
|
function normalizeComponentDefinition(definition) {
|
|
98
155
|
const behaviors = flattenBehaviors(definition);
|
|
99
156
|
if (behaviors.length === 0) return definition;
|
|
@@ -200,61 +257,13 @@ function resolvePropertyDefaultValue(option) {
|
|
|
200
257
|
if (!option || typeof option !== "object" || Array.isArray(option) || !("value" in option)) return;
|
|
201
258
|
return cloneValue$1(option.value);
|
|
202
259
|
}
|
|
203
|
-
function runPropertyObservers(definition, instance, changedKeys, previousProperties) {
|
|
204
|
-
const propOptions = definition.properties;
|
|
205
|
-
if (!propOptions || typeof propOptions !== "object" || Array.isArray(propOptions)) return;
|
|
206
|
-
for (const changedKey of changedKeys) {
|
|
207
|
-
const option = propOptions[changedKey];
|
|
208
|
-
if (!option || typeof option !== "object" || Array.isArray(option)) continue;
|
|
209
|
-
const observer = option.observer;
|
|
210
|
-
if (typeof observer !== "function") continue;
|
|
211
|
-
observer.call(instance, instance.properties[changedKey], previousProperties[changedKey]);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
function normalizeObserverPattern(pattern) {
|
|
215
|
-
return pattern.split(",").map((item) => item.trim()).filter(Boolean);
|
|
216
|
-
}
|
|
217
|
-
function resolveObservedValue(instance, pattern) {
|
|
218
|
-
if (pattern === "**") return {
|
|
219
|
-
...instance.properties,
|
|
220
|
-
...instance.data
|
|
221
|
-
};
|
|
222
|
-
const segments = parseDataPath$1(pattern);
|
|
223
|
-
if (segments.length === 0) return;
|
|
224
|
-
const [rootSegment, ...restSegments] = segments;
|
|
225
|
-
let current = (Object.hasOwn(instance.properties, rootSegment) ? instance.properties : instance.data)?.[rootSegment];
|
|
226
|
-
for (const segment of restSegments) {
|
|
227
|
-
const normalizedSegment = isArrayIndexSegment$1(segment) ? Number(segment) : segment;
|
|
228
|
-
current = current?.[normalizedSegment];
|
|
229
|
-
}
|
|
230
|
-
return current;
|
|
231
|
-
}
|
|
232
|
-
function matchObserverPattern(pattern, changedKeys) {
|
|
233
|
-
if (pattern === "**") return changedKeys.length > 0;
|
|
234
|
-
const patterns = normalizeObserverPattern(pattern);
|
|
235
|
-
return changedKeys.some((changedKey) => {
|
|
236
|
-
return patterns.some((item) => {
|
|
237
|
-
if (item === "**") return true;
|
|
238
|
-
if (changedKey === item) return true;
|
|
239
|
-
return changedKey.startsWith(`${item}.`) || changedKey.startsWith(`${item}[`);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
function runComponentObservers(definition, instance, changedKeys, previousProperties = {}) {
|
|
244
|
-
if (changedKeys.length === 0) return;
|
|
245
|
-
runPropertyObservers(definition, instance, changedKeys, previousProperties);
|
|
246
|
-
const observers = definition.observers;
|
|
247
|
-
if (observers && typeof observers === "object" && !Array.isArray(observers)) for (const [pattern, handler] of Object.entries(observers)) {
|
|
248
|
-
if (typeof handler !== "function" || !matchObserverPattern(pattern, changedKeys)) continue;
|
|
249
|
-
const args = normalizeObserverPattern(pattern).map((item) => resolveObservedValue(instance, item));
|
|
250
|
-
handler.call(instance, ...args);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
260
|
function normalizeComponentPropertyValue(definition, key, rawValue) {
|
|
254
261
|
const option = definition.properties?.[key];
|
|
255
262
|
if (rawValue == null) return resolvePropertyDefaultValue(option);
|
|
256
263
|
return coerceComponentPropertyValue(rawValue, option);
|
|
257
264
|
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/index.ts
|
|
258
267
|
function createComponentInstance(options) {
|
|
259
268
|
const definition = normalizeComponentDefinition(options.definition);
|
|
260
269
|
const instance = {
|
|
@@ -2830,9 +2839,11 @@ function parseDocument(data, options) {
|
|
|
2830
2839
|
return handler.root;
|
|
2831
2840
|
}
|
|
2832
2841
|
//#endregion
|
|
2833
|
-
//#region ../../mpcore/packages/simulator/src/runtime/render.ts
|
|
2834
|
-
const LEADING_SLASH_RE$4 = /^\/+/;
|
|
2842
|
+
//#region ../../mpcore/packages/simulator/src/runtime/render/shared.ts
|
|
2835
2843
|
const TEMPLATE_INTERPOLATION_RE$1 = /\{\{([^{}]+)\}\}/g;
|
|
2844
|
+
const DATASET_NAME_RE$3 = /-([a-z])/g;
|
|
2845
|
+
const BRACKET_INDEX_RE = /\[(\d+)\]/g;
|
|
2846
|
+
const LEADING_SLASH_RE$4 = /^\/+/;
|
|
2836
2847
|
const EVENT_BINDING_ATTRS = [
|
|
2837
2848
|
"bindtap",
|
|
2838
2849
|
"bind:tap",
|
|
@@ -2855,8 +2866,6 @@ const STRUCTURAL_ATTRS = [
|
|
|
2855
2866
|
"wx:key"
|
|
2856
2867
|
];
|
|
2857
2868
|
const WX_ELSE_ATTRS = new Set(["wx:elif", "wx:else"]);
|
|
2858
|
-
const DATASET_NAME_RE$3 = /-([a-z])/g;
|
|
2859
|
-
const BRACKET_INDEX_RE = /\[(\d+)\]/g;
|
|
2860
2869
|
const CLASS_SPLIT_RE = /\s+/;
|
|
2861
2870
|
const JS_FILE_RE = /\.js$/;
|
|
2862
2871
|
function isMustacheOnly(value) {
|
|
@@ -2964,21 +2973,46 @@ function resolveComponentAttributeValue(value, scope) {
|
|
|
2964
2973
|
}
|
|
2965
2974
|
return interpolateTemplate$1(value, scope.data);
|
|
2966
2975
|
}
|
|
2967
|
-
function
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
+
function applyNodeBindings(node, scope) {
|
|
2977
|
+
if (!isTagNode(node)) {
|
|
2978
|
+
if (node.type === "text" && typeof node.data === "string") node.data = interpolateTemplate$1(node.data, scope.data);
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
node.attribs ??= {};
|
|
2982
|
+
node.attribs["data-sim-scope"] = scope.getScopeId();
|
|
2983
|
+
for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
|
|
2984
|
+
for (const [key, value] of Object.entries({ ...node.attribs })) {
|
|
2985
|
+
if (EVENT_BINDING_ATTRS.includes(key)) {
|
|
2986
|
+
node.attribs["data-sim-tap"] = value;
|
|
2987
|
+
continue;
|
|
2976
2988
|
}
|
|
2977
|
-
|
|
2978
|
-
} catch {
|
|
2979
|
-
return /* @__PURE__ */ new Map();
|
|
2989
|
+
node.attribs[key] = typeof value === "string" ? String(resolveAttributeValue(value, scope)) : String(value);
|
|
2980
2990
|
}
|
|
2981
2991
|
}
|
|
2992
|
+
function createMergedScopeData(pageData, componentProperties, componentData) {
|
|
2993
|
+
return {
|
|
2994
|
+
...pageData,
|
|
2995
|
+
...componentProperties,
|
|
2996
|
+
...componentData
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
function evaluateConditionalBranch(node, scope) {
|
|
3000
|
+
const condition = node.attribs?.["wx:if"] ?? node.attribs?.["wx:elif"];
|
|
3001
|
+
if (condition == null) return true;
|
|
3002
|
+
return Boolean(resolveRawValueByPath(scope.data, condition));
|
|
3003
|
+
}
|
|
3004
|
+
function createLoopScope(scope, itemName, indexName, item, index) {
|
|
3005
|
+
return {
|
|
3006
|
+
...scope,
|
|
3007
|
+
data: {
|
|
3008
|
+
...scope.data,
|
|
3009
|
+
[indexName]: index,
|
|
3010
|
+
[itemName]: item
|
|
3011
|
+
}
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
//#endregion
|
|
3015
|
+
//#region ../../mpcore/packages/simulator/src/runtime/render/component.ts
|
|
2982
3016
|
function resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, alias) {
|
|
2983
3017
|
const componentBasePath = resolveUsingComponents(ownerJsonPath, ownerFilePath).get(alias);
|
|
2984
3018
|
if (!componentBasePath) return null;
|
|
@@ -2993,6 +3027,21 @@ function resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, al
|
|
|
2993
3027
|
absoluteTemplatePath
|
|
2994
3028
|
};
|
|
2995
3029
|
}
|
|
3030
|
+
function resolveUsingComponents(ownerJsonPath, ownerFilePath) {
|
|
3031
|
+
try {
|
|
3032
|
+
const usingComponents = JSON.parse(fs.readFileSync(ownerJsonPath, "utf8")).usingComponents;
|
|
3033
|
+
if (!usingComponents || typeof usingComponents !== "object" || Array.isArray(usingComponents)) return /* @__PURE__ */ new Map();
|
|
3034
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
3035
|
+
for (const [alias, rawPath] of Object.entries(usingComponents)) {
|
|
3036
|
+
if (typeof rawPath !== "string") continue;
|
|
3037
|
+
const basePath = rawPath.startsWith("/") ? rawPath.replace(LEADING_SLASH_RE$4, "") : path.posix.normalize(path.posix.join(path.posix.dirname(ownerFilePath), rawPath));
|
|
3038
|
+
resolved.set(alias, basePath.replace(LEADING_SLASH_RE$4, ""));
|
|
3039
|
+
}
|
|
3040
|
+
return resolved;
|
|
3041
|
+
} catch {
|
|
3042
|
+
return /* @__PURE__ */ new Map();
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
2996
3045
|
function collectComponentEventBindings(hostNode) {
|
|
2997
3046
|
const eventBindings = /* @__PURE__ */ new Map();
|
|
2998
3047
|
for (const [key, value] of Object.entries(hostNode.attribs ?? {})) {
|
|
@@ -3043,22 +3092,6 @@ function buildComponentTrigger(componentScopeId, context, hostNode) {
|
|
|
3043
3092
|
}
|
|
3044
3093
|
};
|
|
3045
3094
|
}
|
|
3046
|
-
function applyNodeBindings(node, scope) {
|
|
3047
|
-
if (!isTagNode(node)) {
|
|
3048
|
-
if (node.type === "text" && typeof node.data === "string") node.data = interpolateTemplate$1(node.data, scope.data);
|
|
3049
|
-
return;
|
|
3050
|
-
}
|
|
3051
|
-
node.attribs ??= {};
|
|
3052
|
-
node.attribs["data-sim-scope"] = scope.getScopeId();
|
|
3053
|
-
for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
|
|
3054
|
-
for (const [key, value] of Object.entries({ ...node.attribs })) {
|
|
3055
|
-
if (EVENT_BINDING_ATTRS.includes(key)) {
|
|
3056
|
-
node.attribs["data-sim-tap"] = value;
|
|
3057
|
-
continue;
|
|
3058
|
-
}
|
|
3059
|
-
node.attribs[key] = typeof value === "string" ? String(resolveAttributeValue(value, scope)) : String(value);
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
3095
|
function syncComponentProperties(instance, definition, nextProperties, bindingExpressions, changedPageKeys) {
|
|
3063
3096
|
const changedRootKeys = [];
|
|
3064
3097
|
const previousProperties = {};
|
|
@@ -3081,31 +3114,62 @@ function syncComponentProperties(instance, definition, nextProperties, bindingEx
|
|
|
3081
3114
|
if (changedRootKeys.length === 0) return;
|
|
3082
3115
|
runComponentObservers(definition, instance, changedRootKeys, previousProperties);
|
|
3083
3116
|
}
|
|
3084
|
-
function
|
|
3117
|
+
function createComponentScope(clonedNode, scope, componentScopeId, componentInstance) {
|
|
3118
|
+
const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
|
|
3085
3119
|
return {
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3120
|
+
alias: clonedNode.name,
|
|
3121
|
+
classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
|
|
3122
|
+
data: createMergedScopeData(scope.data, componentInstance.properties, componentInstance.data),
|
|
3123
|
+
dataset: collectDataset$3(clonedNode),
|
|
3124
|
+
eventBindings: collectComponentEventBindings(clonedNode),
|
|
3125
|
+
getMethod: (methodName) => {
|
|
3126
|
+
const method = componentInstance?.[methodName];
|
|
3127
|
+
return typeof method === "function" ? method : void 0;
|
|
3128
|
+
},
|
|
3129
|
+
getScopeId: () => componentScopeId,
|
|
3130
|
+
hostId: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3131
|
+
id: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3132
|
+
listenerScopeId: scope.getScopeId(),
|
|
3133
|
+
ownerScopeId
|
|
3089
3134
|
};
|
|
3090
3135
|
}
|
|
3091
|
-
function
|
|
3092
|
-
|
|
3093
|
-
}
|
|
3094
|
-
|
|
3136
|
+
function resolveComponentProperties(clonedNode, scope) {
|
|
3137
|
+
const nextProperties = {};
|
|
3138
|
+
const bindingExpressions = {};
|
|
3139
|
+
for (const [key, value] of Object.entries(clonedNode.attribs ?? {})) {
|
|
3140
|
+
if (key.startsWith("bind")) continue;
|
|
3141
|
+
if (isMustacheOnly(String(value))) bindingExpressions[key] = String(value).trim().slice(2, -2).trim();
|
|
3142
|
+
nextProperties[key] = resolveComponentAttributeValue(String(value), scope);
|
|
3143
|
+
}
|
|
3095
3144
|
return {
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
...scope.data,
|
|
3099
|
-
[indexName]: index,
|
|
3100
|
-
[itemName]: item
|
|
3101
|
-
}
|
|
3145
|
+
nextProperties,
|
|
3146
|
+
bindingExpressions
|
|
3102
3147
|
};
|
|
3103
3148
|
}
|
|
3104
|
-
function
|
|
3105
|
-
const
|
|
3106
|
-
|
|
3107
|
-
|
|
3149
|
+
function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId) {
|
|
3150
|
+
const componentInstance = createComponentInstance({
|
|
3151
|
+
definition: componentEntry.definition,
|
|
3152
|
+
properties: nextProperties,
|
|
3153
|
+
triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
|
|
3154
|
+
});
|
|
3155
|
+
componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
|
|
3156
|
+
componentInstance.createMediaQueryObserver = () => context.session.createMediaQueryObserver(componentInstance);
|
|
3157
|
+
componentInstance.selectComponent = (selector) => context.session.selectComponentWithin(componentScopeId, selector);
|
|
3158
|
+
componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector);
|
|
3159
|
+
componentInstance.selectOwnerComponent = () => ownerScopeId ? context.session.selectOwnerComponent(componentScopeId) : null;
|
|
3160
|
+
runComponentLifecycle(componentInstance, "created");
|
|
3161
|
+
runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(nextProperties), {});
|
|
3162
|
+
componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
|
|
3163
|
+
runComponentLifecycle(componentInstance, "attached");
|
|
3164
|
+
context.componentCache.set(componentScopeId, componentInstance);
|
|
3165
|
+
return componentInstance;
|
|
3166
|
+
}
|
|
3167
|
+
function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes) {
|
|
3168
|
+
const componentDocument = parseTemplateDocument(readTemplateSource(componentEntry.absoluteTemplatePath));
|
|
3169
|
+
return renderNodeTree((componentDocument.children ?? [])[0] ?? componentDocument, componentScope, context, path.resolve(context.project.miniprogramRootPath, `${componentEntry.filePath.replace(JS_FILE_RE, "")}.json`), componentEntry.filePath, componentScopeId, seenComponentScopes);
|
|
3108
3170
|
}
|
|
3171
|
+
//#endregion
|
|
3172
|
+
//#region ../../mpcore/packages/simulator/src/runtime/render/index.ts
|
|
3109
3173
|
function expandNodeByFor(node, scope) {
|
|
3110
3174
|
const forExpression = node.attribs?.["wx:for"];
|
|
3111
3175
|
if (!forExpression) return [{
|
|
@@ -3175,51 +3239,14 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
|
|
|
3175
3239
|
if (componentEntry) {
|
|
3176
3240
|
const componentScopeId = `${instancePath}/${clonedNode.name}`;
|
|
3177
3241
|
const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
|
|
3178
|
-
const nextProperties =
|
|
3179
|
-
const bindingExpressions = {};
|
|
3180
|
-
for (const [key, value] of Object.entries(clonedNode.attribs ?? {})) {
|
|
3181
|
-
if (key.startsWith("bind")) continue;
|
|
3182
|
-
if (isMustacheOnly(String(value))) bindingExpressions[key] = String(value).trim().slice(2, -2).trim();
|
|
3183
|
-
nextProperties[key] = resolveComponentAttributeValue(String(value), scope);
|
|
3184
|
-
}
|
|
3242
|
+
const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope);
|
|
3185
3243
|
let componentInstance = context.componentCache.get(componentScopeId);
|
|
3186
|
-
if (!componentInstance)
|
|
3187
|
-
|
|
3188
|
-
definition: componentEntry.definition,
|
|
3189
|
-
properties: nextProperties,
|
|
3190
|
-
triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
|
|
3191
|
-
});
|
|
3192
|
-
componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
|
|
3193
|
-
componentInstance.createMediaQueryObserver = () => context.session.createMediaQueryObserver(componentInstance);
|
|
3194
|
-
componentInstance.selectComponent = (selector) => context.session.selectComponentWithin(componentScopeId, selector);
|
|
3195
|
-
componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector);
|
|
3196
|
-
componentInstance.selectOwnerComponent = () => ownerScopeId ? context.session.selectOwnerComponent(componentScopeId) : null;
|
|
3197
|
-
runComponentLifecycle(componentInstance, "created");
|
|
3198
|
-
runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(nextProperties), {});
|
|
3199
|
-
componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
|
|
3200
|
-
runComponentLifecycle(componentInstance, "attached");
|
|
3201
|
-
context.componentCache.set(componentScopeId, componentInstance);
|
|
3202
|
-
} else syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys);
|
|
3244
|
+
if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId);
|
|
3245
|
+
else syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys);
|
|
3203
3246
|
seenComponentScopes.add(componentScopeId);
|
|
3204
|
-
const componentScope =
|
|
3205
|
-
alias: clonedNode.name,
|
|
3206
|
-
classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
|
|
3207
|
-
data: createMergedScopeData(scope.data, componentInstance.properties, componentInstance.data),
|
|
3208
|
-
dataset: collectDataset$3(clonedNode),
|
|
3209
|
-
eventBindings: collectComponentEventBindings(clonedNode),
|
|
3210
|
-
getMethod: (methodName) => {
|
|
3211
|
-
const method = componentInstance?.[methodName];
|
|
3212
|
-
return typeof method === "function" ? method : void 0;
|
|
3213
|
-
},
|
|
3214
|
-
getScopeId: () => componentScopeId,
|
|
3215
|
-
hostId: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3216
|
-
id: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3217
|
-
listenerScopeId: scope.getScopeId(),
|
|
3218
|
-
ownerScopeId
|
|
3219
|
-
};
|
|
3247
|
+
const componentScope = createComponentScope(clonedNode, scope, componentScopeId, componentInstance);
|
|
3220
3248
|
context.componentScopes.set(componentScopeId, componentScope);
|
|
3221
|
-
const
|
|
3222
|
-
const renderedComponentRoot = renderNodeTree((componentDocument.children ?? [])[0] ?? componentDocument, componentScope, context, path.resolve(context.project.miniprogramRootPath, `${componentEntry.filePath.replace(JS_FILE_RE, "")}.json`), componentEntry.filePath, componentScopeId, seenComponentScopes);
|
|
3249
|
+
const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes);
|
|
3223
3250
|
if (renderedComponentRoot.attribs) renderedComponentRoot.attribs["data-sim-component"] = clonedNode.name;
|
|
3224
3251
|
if (!componentInstance.__ready__) {
|
|
3225
3252
|
runComponentLifecycle(componentInstance, "ready");
|
|
@@ -3291,7 +3318,7 @@ function registerComponentDefinition(registries, definition) {
|
|
|
3291
3318
|
return definition;
|
|
3292
3319
|
}
|
|
3293
3320
|
//#endregion
|
|
3294
|
-
//#region ../../mpcore/packages/simulator/src/host/wx.ts
|
|
3321
|
+
//#region ../../mpcore/packages/simulator/src/host/wx/index.ts
|
|
3295
3322
|
function invokeWxApi(operation, option) {
|
|
3296
3323
|
try {
|
|
3297
3324
|
const result = operation();
|
|
@@ -3534,10 +3561,7 @@ function createHeadlessWx(driver) {
|
|
|
3534
3561
|
chooseMedia: (option) => invokeWxApi(() => driver.chooseMedia(option ?? {}), option),
|
|
3535
3562
|
chooseVideo: (option) => invokeWxApi(() => driver.chooseVideo(option ?? {}), option),
|
|
3536
3563
|
compressImage: (option) => invokeWxApi(() => driver.compressImage(option), option),
|
|
3537
|
-
clearStorage: (option) => invokeWxApi(() => {
|
|
3538
|
-
driver.clearStorageSync();
|
|
3539
|
-
return { errMsg: "clearStorage:ok" };
|
|
3540
|
-
}, option),
|
|
3564
|
+
clearStorage: (option) => invokeWxApi(() => ({ errMsg: (driver.clearStorageSync(), "clearStorage:ok") }), option),
|
|
3541
3565
|
clearStorageSync: () => driver.clearStorageSync(),
|
|
3542
3566
|
createAnimation: (option) => driver.createAnimation(option),
|
|
3543
3567
|
createCanvasContext: (canvasId, component) => driver.createCanvasContext(canvasId, component),
|
|
@@ -5515,10 +5539,12 @@ function inferImageType(filePath, fileContent) {
|
|
|
5515
5539
|
}
|
|
5516
5540
|
function inferImageSize(fileContent) {
|
|
5517
5541
|
try {
|
|
5518
|
-
const
|
|
5542
|
+
const config = JSON.parse(fileContent)?.config;
|
|
5543
|
+
const destHeight = config && "destHeight" in config ? config.destHeight : void 0;
|
|
5544
|
+
const destWidth = config && "destWidth" in config ? config.destWidth : void 0;
|
|
5519
5545
|
return {
|
|
5520
|
-
height:
|
|
5521
|
-
width:
|
|
5546
|
+
height: destHeight ?? config?.height ?? 0,
|
|
5547
|
+
width: destWidth ?? config?.width ?? 0
|
|
5522
5548
|
};
|
|
5523
5549
|
} catch {
|
|
5524
5550
|
return {
|
|
@@ -6299,7 +6325,7 @@ function createHeadlessWxState() {
|
|
|
6299
6325
|
width
|
|
6300
6326
|
};
|
|
6301
6327
|
}),
|
|
6302
|
-
type: mediaTypes.length > 1 ? "mix" : mediaTypes[0]
|
|
6328
|
+
type: mediaTypes.length > 1 ? "mix" : mediaTypes[0] ?? "image"
|
|
6303
6329
|
};
|
|
6304
6330
|
},
|
|
6305
6331
|
chooseVideo(option) {
|
|
@@ -7826,7 +7852,7 @@ var HeadlessTestingPageHandle = class {
|
|
|
7826
7852
|
}
|
|
7827
7853
|
};
|
|
7828
7854
|
//#endregion
|
|
7829
|
-
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle.ts
|
|
7855
|
+
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle/shared.ts
|
|
7830
7856
|
const LEADING_SLASH_RE = /^\/+/;
|
|
7831
7857
|
const DEFAULT_WAIT_TIMEOUT = 1e3;
|
|
7832
7858
|
const DEFAULT_WAIT_INTERVAL = 10;
|
|
@@ -7837,6 +7863,28 @@ function resolvePageScopeId(scopeId) {
|
|
|
7837
7863
|
const pagePathIndex = scopeId.indexOf("/page/");
|
|
7838
7864
|
return pagePathIndex >= 0 ? scopeId.slice(0, pagePathIndex) : scopeId;
|
|
7839
7865
|
}
|
|
7866
|
+
function normalizeNonEmptyInput(value, label) {
|
|
7867
|
+
const normalizedValue = value.trim();
|
|
7868
|
+
if (!normalizedValue) throw new Error(`${label} must be a non-empty string in headless testing runtime.`);
|
|
7869
|
+
return normalizedValue;
|
|
7870
|
+
}
|
|
7871
|
+
async function waitForDelay(ms = 0) {
|
|
7872
|
+
if (ms <= 0) return;
|
|
7873
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
7874
|
+
}
|
|
7875
|
+
async function pollUntil(check, errorMessage, options = {}) {
|
|
7876
|
+
const timeout = Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout ?? DEFAULT_WAIT_TIMEOUT)) : DEFAULT_WAIT_TIMEOUT;
|
|
7877
|
+
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? DEFAULT_WAIT_INTERVAL)) : DEFAULT_WAIT_INTERVAL;
|
|
7878
|
+
const deadline = Date.now() + timeout;
|
|
7879
|
+
while (true) {
|
|
7880
|
+
const result = await check();
|
|
7881
|
+
if (result != null) return result;
|
|
7882
|
+
if (Date.now() >= deadline) throw new Error(errorMessage);
|
|
7883
|
+
await waitForDelay(interval);
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
//#endregion
|
|
7887
|
+
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle/scope.ts
|
|
7840
7888
|
var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
7841
7889
|
constructor(scopeId, project, session) {
|
|
7842
7890
|
this.scopeId = scopeId;
|
|
@@ -7848,8 +7896,7 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
|
7848
7896
|
return scopeId ? new HeadlessTestingScopeHandle(scopeId, this.project, this.session) : null;
|
|
7849
7897
|
}
|
|
7850
7898
|
async callMethod(methodName, ...args) {
|
|
7851
|
-
const normalizedMethodName = methodName
|
|
7852
|
-
if (!normalizedMethodName) throw new Error("Method name must be a non-empty string in headless testing runtime.");
|
|
7899
|
+
const normalizedMethodName = normalizeNonEmptyInput(methodName, "Method name");
|
|
7853
7900
|
return this.session.callScopeMethodDirect(this.scopeId, normalizedMethodName, ...args);
|
|
7854
7901
|
}
|
|
7855
7902
|
async snapshot() {
|
|
@@ -7866,75 +7913,37 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
|
7866
7913
|
return this.createScopeHandle(this.session.selectOwnerComponent(this.scopeId));
|
|
7867
7914
|
}
|
|
7868
7915
|
async waitForOwnerComponent(options = {}) {
|
|
7869
|
-
|
|
7870
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? 10)) : 10;
|
|
7871
|
-
const deadline = Date.now() + timeout;
|
|
7872
|
-
while (true) {
|
|
7873
|
-
const owner = await this.ownerComponent();
|
|
7874
|
-
if (owner) return owner;
|
|
7875
|
-
if (Date.now() >= deadline) throw new Error("Timed out waiting for owner component in headless testing runtime.");
|
|
7876
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
7877
|
-
}
|
|
7916
|
+
return await pollUntil(async () => await this.ownerComponent(), "Timed out waiting for owner component in headless testing runtime.", options);
|
|
7878
7917
|
}
|
|
7879
7918
|
async selectComponent(selector) {
|
|
7880
|
-
const normalizedSelector = selector
|
|
7881
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7919
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7882
7920
|
const component = this.session.selectComponentWithin(this.scopeId, normalizedSelector);
|
|
7883
7921
|
return this.createScopeHandle(component);
|
|
7884
7922
|
}
|
|
7885
7923
|
async selectAllComponents(selector) {
|
|
7886
|
-
const normalizedSelector = selector
|
|
7887
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7924
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7888
7925
|
return this.session.selectAllComponentsWithin(this.scopeId, normalizedSelector).map((component) => this.createScopeHandle(component)).filter((handle) => Boolean(handle));
|
|
7889
7926
|
}
|
|
7890
7927
|
async waitForComponent(selector, options = {}) {
|
|
7891
|
-
const normalizedSelector = selector
|
|
7892
|
-
|
|
7893
|
-
const timeout = Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout ?? 1e3)) : 1e3;
|
|
7894
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? 10)) : 10;
|
|
7895
|
-
const deadline = Date.now() + timeout;
|
|
7896
|
-
while (true) {
|
|
7897
|
-
const component = await this.selectComponent(normalizedSelector);
|
|
7898
|
-
if (component) return component;
|
|
7899
|
-
if (Date.now() >= deadline) throw new Error(`Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`);
|
|
7900
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
7901
|
-
}
|
|
7928
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7929
|
+
return await pollUntil(async () => await this.selectComponent(normalizedSelector), `Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`, options);
|
|
7902
7930
|
}
|
|
7903
7931
|
async waitForComponents(selector, count = 1, options = {}) {
|
|
7904
|
-
const normalizedSelector = selector
|
|
7905
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7932
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7906
7933
|
const normalizedCount = Number.isFinite(count) ? Math.max(1, Math.trunc(count)) : 1;
|
|
7907
|
-
|
|
7908
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? 10)) : 10;
|
|
7909
|
-
const deadline = Date.now() + timeout;
|
|
7910
|
-
while (true) {
|
|
7934
|
+
return await pollUntil(async () => {
|
|
7911
7935
|
const components = await this.selectAllComponents(normalizedSelector);
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
7915
|
-
}
|
|
7936
|
+
return components.length >= normalizedCount ? components : null;
|
|
7937
|
+
}, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
|
|
7916
7938
|
}
|
|
7917
7939
|
};
|
|
7940
|
+
//#endregion
|
|
7941
|
+
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle/index.ts
|
|
7918
7942
|
var HeadlessTestingSessionHandle = class {
|
|
7919
7943
|
constructor(project, session) {
|
|
7920
7944
|
this.project = project;
|
|
7921
7945
|
this.session = session;
|
|
7922
7946
|
}
|
|
7923
|
-
async waitFor(ms = 0) {
|
|
7924
|
-
if (ms <= 0) return;
|
|
7925
|
-
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
7926
|
-
}
|
|
7927
|
-
async pollUntil(check, errorMessage, options = {}) {
|
|
7928
|
-
const timeout = Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout ?? DEFAULT_WAIT_TIMEOUT)) : DEFAULT_WAIT_TIMEOUT;
|
|
7929
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? DEFAULT_WAIT_INTERVAL)) : DEFAULT_WAIT_INTERVAL;
|
|
7930
|
-
const deadline = Date.now() + timeout;
|
|
7931
|
-
while (true) {
|
|
7932
|
-
const result = await check();
|
|
7933
|
-
if (result != null) return result;
|
|
7934
|
-
if (Date.now() >= deadline) throw new Error(errorMessage);
|
|
7935
|
-
await this.waitFor(interval);
|
|
7936
|
-
}
|
|
7937
|
-
}
|
|
7938
7947
|
async close() {}
|
|
7939
7948
|
async currentPage() {
|
|
7940
7949
|
const current = this.session.getCurrentPages().at(-1);
|
|
@@ -7944,32 +7953,27 @@ var HeadlessTestingSessionHandle = class {
|
|
|
7944
7953
|
return this.session.getCurrentPages().map((page) => new HeadlessTestingPageHandle(this.project, page, this.session));
|
|
7945
7954
|
}
|
|
7946
7955
|
async scopeSnapshot(scopeId) {
|
|
7947
|
-
const normalizedScopeId = scopeId
|
|
7948
|
-
if (!normalizedScopeId) throw new Error("Scope id must be a non-empty string in headless testing runtime.");
|
|
7956
|
+
const normalizedScopeId = normalizeNonEmptyInput(scopeId, "Scope id");
|
|
7949
7957
|
return this.session.getScopeSnapshot(normalizedScopeId);
|
|
7950
7958
|
}
|
|
7951
7959
|
async selectComponent(selector) {
|
|
7952
|
-
const normalizedSelector = selector
|
|
7953
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7960
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7954
7961
|
const component = this.session.selectComponent(normalizedSelector);
|
|
7955
7962
|
const scopeId = this.session.getScopeIdForComponent(component);
|
|
7956
7963
|
return scopeId ? new HeadlessTestingScopeHandle(scopeId, this.project, this.session) : null;
|
|
7957
7964
|
}
|
|
7958
7965
|
async selectAllComponents(selector) {
|
|
7959
|
-
const normalizedSelector = selector
|
|
7960
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7966
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7961
7967
|
return this.session.selectAllComponents(normalizedSelector).map((component) => this.session.getScopeIdForComponent(component)).filter((scopeId) => Boolean(scopeId)).map((scopeId) => new HeadlessTestingScopeHandle(scopeId, this.project, this.session));
|
|
7962
7968
|
}
|
|
7963
7969
|
async waitForComponent(selector, options = {}) {
|
|
7964
|
-
const normalizedSelector = selector
|
|
7965
|
-
|
|
7966
|
-
return await this.pollUntil(async () => await this.selectComponent(normalizedSelector), `Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`, options);
|
|
7970
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7971
|
+
return await pollUntil(async () => await this.selectComponent(normalizedSelector), `Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`, options);
|
|
7967
7972
|
}
|
|
7968
7973
|
async waitForComponents(selector, count = 1, options = {}) {
|
|
7969
|
-
const normalizedSelector = selector
|
|
7970
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7974
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7971
7975
|
const normalizedCount = Number.isFinite(count) ? Math.max(1, Math.trunc(count)) : 1;
|
|
7972
|
-
return await
|
|
7976
|
+
return await pollUntil(async () => {
|
|
7973
7977
|
const components = await this.selectAllComponents(normalizedSelector);
|
|
7974
7978
|
return components.length >= normalizedCount ? components : null;
|
|
7975
7979
|
}, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
|
|
@@ -7981,7 +7985,7 @@ var HeadlessTestingSessionHandle = class {
|
|
|
7981
7985
|
async waitForCurrentPage(route, options = {}) {
|
|
7982
7986
|
const normalizedRoute = route?.trim();
|
|
7983
7987
|
if (normalizedRoute != null && !normalizedRoute) throw new Error("Route must be a non-empty string in headless testing runtime.");
|
|
7984
|
-
return await
|
|
7988
|
+
return await pollUntil(async () => {
|
|
7985
7989
|
const currentPageInstance = this.session.getCurrentPages().at(-1);
|
|
7986
7990
|
if (!currentPageInstance) return null;
|
|
7987
7991
|
const current = new HeadlessTestingPageHandle(this.project, currentPageInstance, this.session);
|
package/package.json
CHANGED