@testspectra/cli 1.0.7 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/commands/init.js +58 -398
  2. package/dist/commands/run.js +18 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1 -1
  5. package/package.json +8 -1
  6. package/templates/default/actions/applyDiscount/mobile.action.ts +6 -0
  7. package/templates/default/actions/applyDiscount/web.action.ts +6 -0
  8. package/templates/default/actions/verifyOtp/mobile.action.ts +6 -0
  9. package/templates/default/actions/verifyOtp/web.action.ts +6 -0
  10. package/templates/default/fixtures/couponCode.json +1 -0
  11. package/templates/default/fixtures/searchQuery.json +1 -0
  12. package/templates/default/fixtures/userData.json +1 -0
  13. package/templates/default/global-hooks/before.web.hook.ts +4 -0
  14. package/templates/default/package.json +18 -0
  15. package/templates/default/page-objects/LoginPage/mobile.ts +32 -0
  16. package/templates/default/page-objects/LoginPage/web.ts +32 -0
  17. package/templates/default/page-objects/ProductPage/mobile.ts +39 -0
  18. package/templates/default/page-objects/ProductPage/web.ts +38 -0
  19. package/templates/default/page-objects/SettingsPage/common.ts +26 -0
  20. package/templates/default/spectra.config.ts +61 -0
  21. package/templates/default/steps/loginUser/mobile.step.ts +4 -0
  22. package/templates/default/steps/loginUser/web.step.ts +4 -0
  23. package/templates/default/steps/searchProduct/mobile.step.ts +5 -0
  24. package/templates/default/steps/searchProduct/web.step.ts +5 -0
  25. package/templates/default/suites/Auth/TC-AUTH-01/android.test.ts +6 -0
  26. package/templates/default/suites/Auth/TC-AUTH-01/ios.test.ts +6 -0
  27. package/templates/default/suites/Auth/TC-AUTH-01/web.test.ts +13 -0
  28. package/templates/default/suites/Auth/TC-AUTH-02/android.test.ts +5 -0
  29. package/templates/default/suites/Auth/TC-AUTH-02/web.test.ts +5 -0
  30. package/templates/default/suites/Auth/hooks/before.android.hook.ts +3 -0
  31. package/templates/default/suites/Auth/hooks/before.ios.hook.ts +3 -0
  32. package/templates/default/suites/Auth/hooks/before.web.hook.ts +3 -0
  33. package/templates/default/suites/Product/TC-PROD-01/mobile.test.ts +8 -0
  34. package/templates/default/suites/Product/TC-PROD-01/web.test.ts +8 -0
  35. package/templates/default/suites/Product/TC-PROD-02/mobile.test.ts +6 -0
  36. package/templates/default/suites/Product/TC-PROD-02/web.test.ts +6 -0
  37. package/templates/default/suites/Product/hooks/before.web.hook.ts +3 -0
  38. package/templates/default/suites/Settings/TC-SET-01/common.test.ts +8 -0
  39. package/templates/default/tsconfig.android.json +39 -0
  40. package/templates/default/tsconfig.ios.json +39 -0
  41. package/templates/default/tsconfig.json +20 -0
  42. package/templates/default/tsconfig.web.json +45 -0
  43. package/CLI_IMPLEMENTATION_PLAN.md +0 -369
  44. package/src/commands/devices.ts +0 -41
  45. package/src/commands/doctor.ts +0 -57
  46. package/src/commands/init.ts +0 -470
  47. package/src/commands/run.ts +0 -102
  48. package/src/config/loader.ts +0 -82
  49. package/src/config/schema.ts +0 -489
  50. package/src/index.ts +0 -56
  51. package/src/plugin.ts +0 -81
  52. package/src/runner/bridge.ts +0 -146
  53. package/src/runner/reporter.ts +0 -64
  54. package/src/step/index.ts +0 -6
  55. package/src/step/matchers.ts +0 -131
  56. package/src/step/proto.ts +0 -163
  57. package/src/step/runner/collection.ts +0 -73
  58. package/src/step/runner/single.ts +0 -166
  59. package/src/step/spectra.ts +0 -185
  60. package/src/step/types.ts +0 -113
  61. package/src/types/generator.ts +0 -240
  62. package/src/types/webdriverio.d.ts +0 -46
  63. package/testspectra-cli-1.0.5.tgz +0 -0
  64. package/testspectra-cli-1.0.6.tgz +0 -0
  65. package/testspectra-cli-1.0.7.tgz +0 -0
  66. package/tsconfig.json +0 -17
@@ -1,470 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { fileURLToPath } from "url";
4
- import { ConfigLoader } from "../config/loader.js";
5
- import { TypeGenerator } from "../types/generator.js";
6
-
7
- export async function initCommand(options: { force?: boolean }) {
8
- const cwd = process.cwd();
9
- const existingConfig = ConfigLoader.findConfigFile(cwd);
10
-
11
- if (existingConfig && !options.force) {
12
- console.log(`\x1b[33m[TestSpectra]\x1b[0m Config already exists at ${path.basename(existingConfig)}. Use --force to overwrite.`);
13
- return;
14
- }
15
-
16
- // 1. Ensure directories exist
17
- const dirs = [
18
- "specs/TC-LOGIN-01",
19
- "page-objects/LoginPage",
20
- "actions/verifyOtp",
21
- "steps/loginUser",
22
- "fixtures",
23
- "hooks/default",
24
- ".testspectra",
25
- ];
26
- for (const d of dirs) {
27
- const dirPath = path.join(cwd, d);
28
- if (!fs.existsSync(dirPath)) {
29
- fs.mkdirSync(dirPath, { recursive: true });
30
- }
31
- }
32
-
33
- // 2. Create spectra.config.ts
34
- const configContent = `import { defineConfig } from "@testspectra/cli";
35
-
36
- export default defineConfig({
37
- webConfig: {
38
- baseUrl: "https://the-internet.herokuapp.com",
39
- maxConcurrentSessions: "1",
40
- headlessMode: true,
41
- implicitWait: "5000",
42
- pageLoadTimeout: "30000",
43
- scriptTimeout: "30000",
44
- parallelizationMode: "testcase",
45
- },
46
- browsers: [
47
- {
48
- id: "chrome-desktop",
49
- type: "chrome",
50
- mobileEmulation: false,
51
- },
52
- ],
53
- androidConfig: {
54
- appiumServer: "http://127.0.0.1:4723",
55
- platformName: "Android",
56
- platformVersion: "13",
57
- deviceName: "emulator-5554",
58
- automationName: "UiAutomator2",
59
- appPackage: "",
60
- appActivity: "",
61
- autoGrantPermissions: true,
62
- noReset: false,
63
- implicitWait: "10000",
64
- parallelizationMode: "suite",
65
- },
66
- iosConfig: {
67
- appiumServer: "http://127.0.0.1:4723",
68
- platformName: "iOS",
69
- platformVersion: "16.0",
70
- deviceName: "iPhone 14",
71
- automationName: "XCUITest",
72
- bundleId: "",
73
- udid: "auto",
74
- xcodeOrgId: "",
75
- xcodeSigningId: "iPhone Developer",
76
- autoAcceptAlerts: true,
77
- noReset: false,
78
- implicitWait: "10000",
79
- parallelizationMode: "suite",
80
- },
81
- loadConfig: {
82
- virtualUsers: "10",
83
- duration: "1m",
84
- },
85
- loadStages: [],
86
- thresholds: [],
87
- executionConfig: {
88
- networkMonitoringEnabled: true,
89
- fastResponseTime: "200",
90
- normalResponseTime: "1000",
91
- monitoredDomains: [],
92
- environmentVariables: [],
93
- },
94
- });
95
- `;
96
- fs.writeFileSync(path.join(cwd, "spectra.config.ts"), configContent, "utf-8");
97
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Initialized spectra.config.ts with defineConfig`);
98
-
99
- // 3. Create or update package.json with npm scripts & devDependencies
100
- const packageJsonPath = path.join(cwd, "package.json");
101
-
102
- // Dynamically resolve CLI version
103
- let cliVersion = "^1.0.6";
104
- try {
105
- const __filename = fileURLToPath(import.meta.url);
106
- const __dirname = path.dirname(__filename);
107
- const cliPackageJsonPath = path.resolve(__dirname, "../../package.json");
108
- if (fs.existsSync(cliPackageJsonPath)) {
109
- const cliPkg = JSON.parse(fs.readFileSync(cliPackageJsonPath, "utf-8"));
110
- if (cliPkg.version) {
111
- cliVersion = `^${cliPkg.version}`;
112
- }
113
- }
114
- } catch {}
115
-
116
- // Check if cwd is inside the testspectra monorepo with a pnpm-workspace.yaml
117
- let isInternalWorkspace = false;
118
- let cur = cwd;
119
- while (cur !== path.dirname(cur)) {
120
- if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml"))) {
121
- try {
122
- const wsContent = fs.readFileSync(path.join(cur, "pnpm-workspace.yaml"), "utf-8");
123
- if (wsContent.includes("cli")) {
124
- isInternalWorkspace = true;
125
- break;
126
- }
127
- } catch {}
128
- }
129
- cur = path.dirname(cur);
130
- }
131
-
132
- const cliDepVersion = isInternalWorkspace ? "workspace:*" : cliVersion;
133
-
134
- let pkg: any = {
135
- name: path.basename(cwd),
136
- version: "1.0.0",
137
- private: true,
138
- type: "module",
139
- scripts: {
140
- test: "spectra run",
141
- "type-check": "tsc -b",
142
- },
143
- devDependencies: {
144
- "@testspectra/cli": cliDepVersion,
145
- "@types/node": "^20.14.0",
146
- "@wdio/globals": "^9.2.8",
147
- "@wdio/mocha-framework": "^9.2.8",
148
- "webdriverio": "^9.2.8",
149
- "typescript": "^5.4.5",
150
- },
151
- };
152
- if (fs.existsSync(packageJsonPath)) {
153
- try {
154
- const existing = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
155
- pkg = {
156
- ...existing,
157
- scripts: {
158
- ...(existing.scripts || {}),
159
- test: existing.scripts?.test || "spectra run",
160
- "type-check": existing.scripts?.["type-check"] || "tsc -b",
161
- },
162
- devDependencies: {
163
- ...(existing.devDependencies || {}),
164
- "@testspectra/cli": existing.devDependencies?.["@testspectra/cli"]?.startsWith("workspace:") && !isInternalWorkspace
165
- ? cliVersion
166
- : existing.devDependencies?.["@testspectra/cli"] || cliDepVersion,
167
- "@types/node": existing.devDependencies?.["@types/node"] || "^20.14.0",
168
- "@wdio/globals": existing.devDependencies?.["@wdio/globals"] || "^9.2.8",
169
- "@wdio/mocha-framework": existing.devDependencies?.["@wdio/mocha-framework"] || "^9.2.8",
170
- "webdriverio": existing.devDependencies?.["webdriverio"] || "^9.2.8",
171
- "typescript": existing.devDependencies?.["typescript"] || "^5.4.5",
172
- },
173
- };
174
- } catch {}
175
- }
176
- fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2), "utf-8");
177
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Updated package.json (scripts & devDependencies)`);
178
-
179
- // 3b. Create .gitignore
180
- const gitignorePath = path.join(cwd, ".gitignore");
181
- if (!fs.existsSync(gitignorePath) || options.force) {
182
- const gitignoreContent = `node_modules/\ndist/\n.testspectra/\n*.tsbuildinfo\n`;
183
- fs.writeFileSync(gitignorePath, gitignoreContent, "utf-8");
184
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Created .gitignore`);
185
- }
186
-
187
- // 4. Create platform tsconfigs with Solution-Style Project References & TS Plugin
188
- const tsconfigRoot = {
189
- compilerOptions: {
190
- target: "ES2022",
191
- module: "NodeNext",
192
- moduleResolution: "NodeNext",
193
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
194
- plugins: [
195
- { name: "@testspectra/cli" }
196
- ],
197
- skipLibCheck: true,
198
- strict: true,
199
- noEmit: true,
200
- },
201
- files: ["spectra.config.ts"],
202
- references: [
203
- { path: "./tsconfig.web.json" },
204
- { path: "./tsconfig.android.json" },
205
- { path: "./tsconfig.ios.json" },
206
- ],
207
- };
208
- fs.writeFileSync(path.join(cwd, "tsconfig.json"), JSON.stringify(tsconfigRoot, null, 2), "utf-8");
209
-
210
- const tsconfigWeb = {
211
- compilerOptions: {
212
- target: "ES2022",
213
- module: "NodeNext",
214
- moduleResolution: "NodeNext",
215
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
216
- skipLibCheck: true,
217
- strict: true,
218
- composite: true,
219
- emitDeclarationOnly: true,
220
- outDir: "./.testspectra/.build/web",
221
- },
222
- include: [
223
- "specs/**/web.test.ts",
224
- "specs/**/common.test.ts",
225
- "page-objects/**/web.ts",
226
- "page-objects/**/common.ts",
227
- "actions/**/web.action.ts",
228
- "actions/**/common.action.ts",
229
- "steps/**/web.step.ts",
230
- "steps/**/common.step.ts",
231
- "hooks/**/before.web.hook.ts",
232
- "hooks/**/before.hook.ts",
233
- ".testspectra/types/web.d.ts",
234
- ".testspectra/types/common.d.ts",
235
- ".testspectra/types/fixtures.d.ts",
236
- ],
237
- };
238
- fs.writeFileSync(path.join(cwd, "tsconfig.web.json"), JSON.stringify(tsconfigWeb, null, 2), "utf-8");
239
-
240
- const tsconfigAndroid = {
241
- compilerOptions: {
242
- target: "ES2022",
243
- module: "NodeNext",
244
- moduleResolution: "NodeNext",
245
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
246
- skipLibCheck: true,
247
- strict: true,
248
- composite: true,
249
- emitDeclarationOnly: true,
250
- outDir: "./.testspectra/.build/android",
251
- },
252
- include: [
253
- "specs/**/android.test.ts",
254
- "specs/**/mobile.test.ts",
255
- "specs/**/common.test.ts",
256
- "page-objects/**/android.ts",
257
- "page-objects/**/mobile.ts",
258
- "page-objects/**/common.ts",
259
- "actions/**/android.action.ts",
260
- "actions/**/mobile.action.ts",
261
- "actions/**/common.action.ts",
262
- "steps/**/android.step.ts",
263
- "steps/**/mobile.step.ts",
264
- "steps/**/common.step.ts",
265
- "hooks/**/before.android.hook.ts",
266
- "hooks/**/before.mobile.hook.ts",
267
- "hooks/**/before.hook.ts",
268
- ".testspectra/types/android.d.ts",
269
- ".testspectra/types/mobile.d.ts",
270
- ".testspectra/types/common.d.ts",
271
- ".testspectra/types/fixtures.d.ts",
272
- ],
273
- };
274
- fs.writeFileSync(path.join(cwd, "tsconfig.android.json"), JSON.stringify(tsconfigAndroid, null, 2), "utf-8");
275
-
276
- const tsconfigIos = {
277
- compilerOptions: {
278
- target: "ES2022",
279
- module: "NodeNext",
280
- moduleResolution: "NodeNext",
281
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
282
- skipLibCheck: true,
283
- strict: true,
284
- composite: true,
285
- emitDeclarationOnly: true,
286
- outDir: "./.testspectra/.build/ios",
287
- },
288
- include: [
289
- "specs/**/ios.test.ts",
290
- "specs/**/mobile.test.ts",
291
- "specs/**/common.test.ts",
292
- "page-objects/**/ios.ts",
293
- "page-objects/**/mobile.ts",
294
- "page-objects/**/common.ts",
295
- "actions/**/ios.action.ts",
296
- "actions/**/mobile.action.ts",
297
- "actions/**/common.action.ts",
298
- "steps/**/ios.step.ts",
299
- "steps/**/mobile.step.ts",
300
- "steps/**/common.step.ts",
301
- "hooks/**/before.ios.hook.ts",
302
- "hooks/**/before.mobile.hook.ts",
303
- "hooks/**/before.hook.ts",
304
- ".testspectra/types/ios.d.ts",
305
- ".testspectra/types/mobile.d.ts",
306
- ".testspectra/types/common.d.ts",
307
- ".testspectra/types/fixtures.d.ts",
308
- ],
309
- };
310
- fs.writeFileSync(path.join(cwd, "tsconfig.ios.json"), JSON.stringify(tsconfigIos, null, 2), "utf-8");
311
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Created platform-isolated tsconfigs with solution references`);
312
-
313
- // 5. Scaffold Fixture File
314
- const fixturePath = path.join(cwd, "fixtures", "userData.json");
315
- if (!fs.existsSync(fixturePath) || options.force) {
316
- fs.writeFileSync(fixturePath, JSON.stringify({ username: "tomsmith", role: "admin" }, null, 2), "utf-8");
317
- }
318
-
319
- // 6. Scaffold Page Objects (Clean zero-import methods with explicit ChainablePromiseElement types)
320
- const poWeb = `export default class LoginPage {
321
- static get usernameInput(): ChainablePromiseElement {
322
- return $('#username');
323
- }
324
-
325
- static get passwordInput(): ChainablePromiseElement {
326
- return $('#password');
327
- }
328
-
329
- static get submitButton(): ChainablePromiseElement {
330
- return $('button[type="submit"]');
331
- }
332
-
333
- static get flashAlert(): ChainablePromiseElement {
334
- return $('#flash');
335
- }
336
-
337
- static async open() {
338
- await Spectra.navigate('/login');
339
- }
340
-
341
- static async login(username: string, pass: string) {
342
- await Spectra.type(this.usernameInput, username);
343
- await Spectra.type(this.passwordInput, pass);
344
- await Spectra.click(this.submitButton);
345
- }
346
- }
347
- `;
348
- fs.writeFileSync(path.join(cwd, "page-objects/LoginPage/web.ts"), poWeb, "utf-8");
349
-
350
- const poMobile = `export default class LoginPage {
351
- static get usernameInput(): ChainablePromiseElement {
352
- return $('~username_input');
353
- }
354
-
355
- static get passwordInput(): ChainablePromiseElement {
356
- return $('~password_input');
357
- }
358
-
359
- static get submitButton(): ChainablePromiseElement {
360
- return $('~login_button');
361
- }
362
-
363
- static get welcomeText(): ChainablePromiseElement {
364
- return $('~welcome_text');
365
- }
366
-
367
- static async open() {
368
- // Mobile app startup
369
- }
370
-
371
- static async login(username: string, pass: string) {
372
- await Spectra.type(this.usernameInput, username);
373
- await Spectra.type(this.passwordInput, pass);
374
- await Spectra.click(this.submitButton);
375
- }
376
- }
377
- `;
378
- fs.writeFileSync(path.join(cwd, "page-objects/LoginPage/mobile.ts"), poMobile, "utf-8");
379
-
380
- // 7. Scaffold Actions (Zero triple-slash lines)
381
- const actionWeb = `export default async function verifyOtp(otp: string): Promise<void> {
382
- const otpInput = await $('#otp');
383
- await otpInput.setValue(otp);
384
- const verifyBtn = await $('#verify-btn');
385
- await verifyBtn.click();
386
- }
387
- `;
388
- fs.writeFileSync(path.join(cwd, "actions/verifyOtp/web.action.ts"), actionWeb, "utf-8");
389
-
390
- const actionMobile = `export default async function verifyOtp(otp: string): Promise<void> {
391
- const otpInput = await $('~otp_input');
392
- await otpInput.setValue(otp);
393
- const verifyBtn = await $('~verify_btn');
394
- await verifyBtn.click();
395
- }
396
- `;
397
- fs.writeFileSync(path.join(cwd, "actions/verifyOtp/mobile.action.ts"), actionMobile, "utf-8");
398
-
399
- // 8. Scaffold Steps (Zero triple-slash lines)
400
- const stepWeb = `export default async function loginUser(u: string, p: string): Promise<void> {
401
- await LoginPage.open();
402
- await LoginPage.login(u, p);
403
- }
404
- `;
405
- fs.writeFileSync(path.join(cwd, "steps/loginUser/web.step.ts"), stepWeb, "utf-8");
406
-
407
- const stepMobile = `export default async function loginUser(u: string, p: string): Promise<void> {
408
- await LoginPage.open();
409
- await LoginPage.login(u, p);
410
- }
411
- `;
412
- fs.writeFileSync(path.join(cwd, "steps/loginUser/mobile.step.ts"), stepMobile, "utf-8");
413
-
414
- // 9. Scaffold Hooks (Zero triple-slash lines)
415
- const hookWeb = `export default async function (): Promise<void> {
416
- await browser.maximizeWindow();
417
- }
418
- `;
419
- fs.writeFileSync(path.join(cwd, "hooks/default/before.web.hook.ts"), hookWeb, "utf-8");
420
-
421
- const hookAndroid = `export default async function (): Promise<void> {
422
- // Setup Android Appium capabilities
423
- }
424
- `;
425
- fs.writeFileSync(path.join(cwd, "hooks/default/before.android.hook.ts"), hookAndroid, "utf-8");
426
-
427
- const hookIos = `export default async function (): Promise<void> {
428
- // Setup iOS Appium capabilities
429
- }
430
- `;
431
- fs.writeFileSync(path.join(cwd, "hooks/default/before.ios.hook.ts"), hookIos, "utf-8");
432
-
433
- // 10. Scaffold Specs (Pure test scripts, ZERO triple-slash lines, ZERO raw selectors)
434
- const specWeb = `it("should authenticate user using Page Objects and Spectra assertions", async () => {
435
- await browser.intercept("/api/status", "GET", Fixture.userData);
436
-
437
- // 1. Navigation via Page Object
438
- await LoginPage.open();
439
-
440
- // 2. High-level business flow via Shared Step & Page Object
441
- await Step.loginUser("tomsmith", "SuperSecretPassword!");
442
-
443
- // 3. Direct callable assertion on Page Object element
444
- await LoginPage.flashAlert.shouldBeVisible();
445
- await LoginPage.flashAlert.shouldContainText("You logged into a secure area!");
446
- });
447
- `;
448
- fs.writeFileSync(path.join(cwd, "specs/TC-LOGIN-01/web.test.ts"), specWeb, "utf-8");
449
-
450
- const specAndroid = `it("should authenticate user on Android Appium device using Page Objects", async () => {
451
- await Step.loginUser("tomsmith", "SuperSecretPassword!");
452
-
453
- await LoginPage.welcomeText.shouldBeVisible();
454
- });
455
- `;
456
- fs.writeFileSync(path.join(cwd, "specs/TC-LOGIN-01/android.test.ts"), specAndroid, "utf-8");
457
-
458
- const specIos = `it("should authenticate user on iOS Appium device using Page Objects", async () => {
459
- await Step.loginUser("tomsmith", "SuperSecretPassword!");
460
-
461
- await LoginPage.welcomeText.shouldBeVisible();
462
- });
463
- `;
464
- fs.writeFileSync(path.join(cwd, "specs/TC-LOGIN-01/ios.test.ts"), specIos, "utf-8");
465
-
466
- // 11. Generate ambient declaration files
467
- TypeGenerator.writeDeclarationFiles(cwd);
468
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Generated ambient multi-platform types in .testspectra/types/`);
469
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Project initialized successfully!`);
470
- }
@@ -1,102 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { ConfigLoader } from "../config/loader.js";
4
- import { RustCoreBridge } from "../runner/bridge.js";
5
- import { Reporter } from "../runner/reporter.js";
6
- import { TypeGenerator } from "../types/generator.js";
7
-
8
- export interface RunCommandOptions {
9
- target?: "web" | "android" | "ios" | "common";
10
- device?: string;
11
- headless?: boolean;
12
- workdir?: string;
13
- output?: string;
14
- }
15
-
16
- export async function runCommand(specPath?: string, options: RunCommandOptions = {}) {
17
- const cwd = process.cwd();
18
-
19
- // Ensure latest ambient types are up-to-date
20
- TypeGenerator.writeDeclarationFiles(cwd);
21
-
22
- const config = await ConfigLoader.loadConfig(cwd);
23
-
24
- if (options.headless !== undefined) {
25
- config.webConfig.headlessMode = options.headless;
26
- }
27
-
28
- const platform = options.target || "web";
29
- const appDataPath = options.workdir ? path.resolve(options.workdir) : path.join(cwd, ".testspectra");
30
-
31
- if (!fs.existsSync(appDataPath)) {
32
- fs.mkdirSync(appDataPath, { recursive: true });
33
- }
34
-
35
- // Determine suite/test case name
36
- let suiteName = "default";
37
- let testCases: Array<{ id: string; title: string; executionOrder?: number }> = [];
38
-
39
- if (specPath) {
40
- const parsed = path.parse(specPath);
41
- const baseName = parsed.name.replace(/\.(web|android|ios|common|test|spec)/g, "");
42
- suiteName = baseName;
43
- testCases.push({
44
- id: baseName,
45
- title: baseName,
46
- executionOrder: 1,
47
- });
48
- } else {
49
- // Scan specs/ directory (both entity folders specs/TC-001/web.test.ts and flat files)
50
- const specsDir = path.join(cwd, "specs");
51
- if (fs.existsSync(specsDir)) {
52
- const entries = fs.readdirSync(specsDir, { withFileTypes: true });
53
- for (const entry of entries) {
54
- if (entry.name.startsWith(".")) continue;
55
- if (entry.isDirectory()) {
56
- testCases.push({ id: entry.name, title: entry.name });
57
- } else if (entry.isFile() && (entry.name.endsWith(".test.ts") || entry.name.endsWith(".spec.ts"))) {
58
- const id = entry.name.replace(/\.(web|android|ios|common|all|mobile|test|spec)\.ts$/g, "").replace(/\.test\.ts$/, "");
59
- testCases.push({ id, title: id });
60
- }
61
- }
62
- }
63
- }
64
-
65
- if (testCases.length === 0) {
66
- testCases.push({
67
- id: suiteName,
68
- title: suiteName,
69
- executionOrder: 1,
70
- });
71
- }
72
-
73
- const outputJsonPath = options.output
74
- ? path.resolve(options.output)
75
- : path.join(appDataPath, "reports/result.json");
76
-
77
- console.log(`\x1b[36m[TestSpectra]\x1b[0m Starting execution on target \x1b[1m${platform}\x1b[0m...`);
78
- console.log(`\x1b[36m[TestSpectra]\x1b[0m Suite: ${suiteName} (${testCases.length} case(s))\n`);
79
-
80
- const reporter = new Reporter();
81
- try {
82
- const result = await RustCoreBridge.run(
83
- {
84
- baseDir: cwd,
85
- appDataPath,
86
- platform,
87
- suite: suiteName,
88
- testCases,
89
- config,
90
- targetDevice: options.device,
91
- outputJsonPath,
92
- },
93
- reporter
94
- );
95
-
96
- console.log(`\n\x1b[36m[TestSpectra]\x1b[0m Run finished: \x1b[1m${result.status.toUpperCase()}\x1b[0m in ${result.duration}`);
97
- console.log(`\x1b[36m[TestSpectra]\x1b[0m Report saved to: ${outputJsonPath}`);
98
- } catch (e) {
99
- console.error(`\x1b[31m[TestSpectra] Execution error:\x1b[0m`, e);
100
- process.exit(1);
101
- }
102
- }
@@ -1,82 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { ConfigData, DEFAULT_CONFIG_DATA } from "./schema.js";
4
-
5
- export class ConfigLoader {
6
- static readonly CONFIG_FILE_NAMES = [
7
- "testspectra.config.ts",
8
- "spectra.config.ts",
9
- "testspectra.config.js",
10
- "spectra.config.js",
11
- "testspectra.config.json",
12
- ".testspectrarc.json",
13
- ];
14
-
15
- static findConfigFile(cwd: string = process.cwd()): string | null {
16
- for (const filename of this.CONFIG_FILE_NAMES) {
17
- const fullPath = path.join(cwd, filename);
18
- if (fs.existsSync(fullPath)) {
19
- return fullPath;
20
- }
21
- }
22
- return null;
23
- }
24
-
25
- static async loadConfig(cwd: string = process.cwd(), overrides?: Partial<ConfigData>): Promise<ConfigData> {
26
- const configPath = this.findConfigFile(cwd);
27
- let loaded: Partial<ConfigData> = {};
28
-
29
- if (configPath) {
30
- if (configPath.endsWith(".json")) {
31
- try {
32
- const raw = fs.readFileSync(configPath, "utf-8");
33
- loaded = JSON.parse(raw);
34
- } catch (e) {
35
- console.warn(`[TestSpectra] Warning: Failed to parse ${configPath}:`, e);
36
- }
37
- } else if (configPath.endsWith(".ts") || configPath.endsWith(".js")) {
38
- try {
39
- const fileUrl = new URL(`file://${path.resolve(configPath)}`).href;
40
- const mod = await import(fileUrl);
41
- loaded = mod.default || mod.config || mod;
42
- } catch (e) {
43
- console.warn(`[TestSpectra] Warning: Failed to import ${configPath}:`, e);
44
- }
45
- }
46
- }
47
-
48
- return {
49
- ...DEFAULT_CONFIG_DATA,
50
- ...loaded,
51
- ...overrides,
52
- webConfig: {
53
- ...DEFAULT_CONFIG_DATA.webConfig,
54
- ...(loaded.webConfig || {}),
55
- ...(overrides?.webConfig || {}),
56
- },
57
- androidConfig: {
58
- ...DEFAULT_CONFIG_DATA.androidConfig,
59
- ...(loaded.androidConfig || {}),
60
- ...(overrides?.androidConfig || {}),
61
- },
62
- iosConfig: {
63
- ...DEFAULT_CONFIG_DATA.iosConfig,
64
- ...(loaded.iosConfig || {}),
65
- ...(overrides?.iosConfig || {}),
66
- },
67
- executionConfig: {
68
- networkMonitoringEnabled: overrides?.executionConfig?.networkMonitoringEnabled ?? loaded.executionConfig?.networkMonitoringEnabled ?? DEFAULT_CONFIG_DATA.executionConfig?.networkMonitoringEnabled ?? true,
69
- fastResponseTime: overrides?.executionConfig?.fastResponseTime ?? loaded.executionConfig?.fastResponseTime ?? DEFAULT_CONFIG_DATA.executionConfig?.fastResponseTime ?? "200",
70
- normalResponseTime: overrides?.executionConfig?.normalResponseTime ?? loaded.executionConfig?.normalResponseTime ?? DEFAULT_CONFIG_DATA.executionConfig?.normalResponseTime ?? "1000",
71
- monitoredDomains: overrides?.executionConfig?.monitoredDomains ?? loaded.executionConfig?.monitoredDomains ?? DEFAULT_CONFIG_DATA.executionConfig?.monitoredDomains ?? [],
72
- environmentVariables: overrides?.executionConfig?.environmentVariables ?? loaded.executionConfig?.environmentVariables ?? DEFAULT_CONFIG_DATA.executionConfig?.environmentVariables ?? [],
73
- },
74
- };
75
- }
76
-
77
- static saveConfig(cwd: string, config: ConfigData, filename = "testspectra.config.json"): string {
78
- const target = path.join(cwd, filename);
79
- fs.writeFileSync(target, JSON.stringify(config, null, 2), "utf-8");
80
- return target;
81
- }
82
- }