@testspectra/cli 1.0.28 → 1.0.30

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 (46) hide show
  1. package/dist/commands/add.js +4 -6
  2. package/dist/commands/init.js +84 -43
  3. package/dist/types/generator.js +73 -71
  4. package/package.json +1 -1
  5. package/templates/default/tsconfig.json +1 -15
  6. package/templates/{nx/modules/auth/e2e/tsconfig.json → default/tsconfig.spectra.json} +7 -8
  7. package/templates/nx/modules/auth/e2e/project.json +3 -4
  8. package/templates/nx/modules/catalog/e2e/project.json +3 -4
  9. package/templates/nx/package.json +1 -1
  10. package/templates/nx/shared/testing/page-objects/NavigationBar/common.ts +2 -2
  11. package/templates/nx/tsconfig.json +1 -3
  12. package/templates/nx/tsconfig.spectra.android.json +36 -0
  13. package/templates/nx/tsconfig.spectra.ios.json +36 -0
  14. package/templates/nx/{modules/catalog/e2e/tsconfig.json → tsconfig.spectra.json} +7 -8
  15. package/templates/nx/tsconfig.spectra.web.json +42 -0
  16. package/templates/nx/modules/auth/e2e/.testspectra/types/android.d.ts +0 -82
  17. package/templates/nx/modules/auth/e2e/.testspectra/types/common.d.ts +0 -80
  18. package/templates/nx/modules/auth/e2e/.testspectra/types/fixtures.d.ts +0 -10
  19. package/templates/nx/modules/auth/e2e/.testspectra/types/index.d.ts +0 -1
  20. package/templates/nx/modules/auth/e2e/.testspectra/types/ios.d.ts +0 -82
  21. package/templates/nx/modules/auth/e2e/.testspectra/types/mobile.d.ts +0 -82
  22. package/templates/nx/modules/auth/e2e/.testspectra/types/web.d.ts +0 -82
  23. package/templates/nx/modules/auth/e2e/tsconfig.android.json +0 -39
  24. package/templates/nx/modules/auth/e2e/tsconfig.android.tsbuildinfo +0 -1
  25. package/templates/nx/modules/auth/e2e/tsconfig.ios.json +0 -39
  26. package/templates/nx/modules/auth/e2e/tsconfig.ios.tsbuildinfo +0 -1
  27. package/templates/nx/modules/auth/e2e/tsconfig.web.json +0 -45
  28. package/templates/nx/modules/auth/e2e/tsconfig.web.tsbuildinfo +0 -1
  29. package/templates/nx/modules/catalog/e2e/.testspectra/types/android.d.ts +0 -82
  30. package/templates/nx/modules/catalog/e2e/.testspectra/types/common.d.ts +0 -80
  31. package/templates/nx/modules/catalog/e2e/.testspectra/types/fixtures.d.ts +0 -10
  32. package/templates/nx/modules/catalog/e2e/.testspectra/types/index.d.ts +0 -1
  33. package/templates/nx/modules/catalog/e2e/.testspectra/types/ios.d.ts +0 -82
  34. package/templates/nx/modules/catalog/e2e/.testspectra/types/mobile.d.ts +0 -82
  35. package/templates/nx/modules/catalog/e2e/.testspectra/types/web.d.ts +0 -82
  36. package/templates/nx/modules/catalog/e2e/tsconfig.android.json +0 -39
  37. package/templates/nx/modules/catalog/e2e/tsconfig.android.tsbuildinfo +0 -1
  38. package/templates/nx/modules/catalog/e2e/tsconfig.ios.json +0 -39
  39. package/templates/nx/modules/catalog/e2e/tsconfig.ios.tsbuildinfo +0 -1
  40. package/templates/nx/modules/catalog/e2e/tsconfig.web.json +0 -45
  41. package/templates/nx/modules/catalog/e2e/tsconfig.web.tsbuildinfo +0 -1
  42. package/templates/nx/shared/testing/tsconfig.json +0 -18
  43. package/templates/nx/shared/testing/tsconfig.tsbuildinfo +0 -1
  44. /package/templates/default/{tsconfig.android.json → tsconfig.spectra.android.json} +0 -0
  45. /package/templates/default/{tsconfig.ios.json → tsconfig.spectra.ios.json} +0 -0
  46. /package/templates/default/{tsconfig.web.json → tsconfig.spectra.web.json} +0 -0
@@ -311,24 +311,22 @@ export async function addCommand(moduleNameArg, options = {}) {
311
311
  const localConfig = path.join(targetE2eDir, "spectra.config.ts");
312
312
  if (fs.existsSync(localConfig))
313
313
  fs.unlinkSync(localConfig);
314
- // Update Root Solution tsconfig.json references
314
+ // Ensure Root Solution tsconfig.json references tsconfig.spectra.json
315
315
  const rootTsConfigPath = path.join(rootDir, "tsconfig.json");
316
316
  if (fs.existsSync(rootTsConfigPath)) {
317
317
  try {
318
318
  const rootTsConfig = JSON.parse(fs.readFileSync(rootTsConfigPath, "utf-8"));
319
319
  if (!rootTsConfig.references)
320
320
  rootTsConfig.references = [];
321
- const relRef = `./${path.relative(rootDir, targetE2eDir).replace(/\\/g, "/")}`;
322
- if (!rootTsConfig.references.some((r) => r.path === relRef)) {
323
- rootTsConfig.references.push({ path: relRef });
321
+ if (!rootTsConfig.references.some((r) => r.path === "./tsconfig.spectra.json")) {
322
+ rootTsConfig.references.push({ path: "./tsconfig.spectra.json" });
324
323
  fs.writeFileSync(rootTsConfigPath, JSON.stringify(rootTsConfig, null, 2), "utf-8");
325
324
  }
326
325
  }
327
326
  catch { }
328
327
  }
329
- // Generate Ambient Types for root and new module
328
+ // Generate Ambient Types for root workspace
330
329
  TypeGenerator.writeDeclarationFiles(rootDir);
331
- TypeGenerator.writeDeclarationFiles(targetE2eDir);
332
330
  s.stop(`Module ${e2eProjectName} added successfully!`);
333
331
  p.log.success(chalk.green(`Directory: ./${path.relative(rootDir, targetE2eDir)}`));
334
332
  p.log.success(chalk.green(`Nx Target: ${e2eProjectName}:e2e`));
@@ -293,7 +293,10 @@ export async function initCommand(options = {}) {
293
293
  // --- MONOREPO INTERACTIVE FLOW ---
294
294
  if (templateName === "nx" && (detectedProjects.length > 0 || isMonorepo)) {
295
295
  let chosenProjects = [];
296
- if (detectedProjects.length > 0) {
296
+ if (options.force) {
297
+ chosenProjects = detectedProjects.map((p) => p.relPath);
298
+ }
299
+ else if (detectedProjects.length > 0) {
297
300
  p.log.step(chalk.cyan(`Monorepo Auto-Discovery: Found ${detectedProjects.length} workspace feature package(s)`));
298
301
  const projectChoices = await p.multiselect({
299
302
  message: "Select workspace features to initialize with TestSpectra:",
@@ -311,34 +314,77 @@ export async function initCommand(options = {}) {
311
314
  }
312
315
  chosenProjects = projectChoices;
313
316
  }
314
- const e2eFolderNameInput = await p.text({
315
- message: "Enter E2E test folder name for each selected feature:",
316
- placeholder: "e2e",
317
- defaultValue: "e2e",
318
- });
319
- if (p.isCancel(e2eFolderNameInput)) {
320
- p.cancel("Setup cancelled.");
321
- return;
322
- }
323
- const sharedTestingPathInput = await p.text({
324
- message: "Enter path for Shared Testing library (POMs, Steps, Actions, Fixtures):",
325
- placeholder: "shared/testing",
326
- defaultValue: "shared/testing",
327
- });
328
- if (p.isCancel(sharedTestingPathInput)) {
329
- p.cancel("Setup cancelled.");
330
- return;
317
+ let e2eFolderName = "e2e";
318
+ let sharedTestingRelPath = "shared/testing";
319
+ if (!options.force) {
320
+ const e2eFolderNameInput = await p.text({
321
+ message: "Enter E2E test folder name for each selected feature:",
322
+ placeholder: "e2e",
323
+ defaultValue: "e2e",
324
+ });
325
+ if (p.isCancel(e2eFolderNameInput)) {
326
+ p.cancel("Setup cancelled.");
327
+ return;
328
+ }
329
+ const sharedTestingPathInput = await p.text({
330
+ message: "Enter path for Shared Testing library (POMs, Steps, Actions, Fixtures):",
331
+ placeholder: "shared/testing",
332
+ defaultValue: "shared/testing",
333
+ });
334
+ if (p.isCancel(sharedTestingPathInput)) {
335
+ p.cancel("Setup cancelled.");
336
+ return;
337
+ }
338
+ e2eFolderName = e2eFolderNameInput.trim() || "e2e";
339
+ sharedTestingRelPath = sharedTestingPathInput.trim() || "shared/testing";
331
340
  }
332
- const e2eFolderName = e2eFolderNameInput.trim() || "e2e";
333
- const sharedTestingRelPath = sharedTestingPathInput.trim() || "shared/testing";
334
341
  const sharedTestingAbsPath = path.join(cwd, sharedTestingRelPath);
335
342
  const s = p.spinner();
336
343
  s.start("Scaffolding distributed monorepo structure and ambient types...");
337
- // 1. Centralized Root Config
338
- const rootConfigSrc = path.join(templateDir, "spectra.config.ts");
339
- const rootConfigDest = path.join(cwd, "spectra.config.ts");
340
- if (fs.existsSync(rootConfigSrc) && (!fs.existsSync(rootConfigDest) || options.force)) {
341
- fs.copyFileSync(rootConfigSrc, rootConfigDest);
344
+ // 1. Centralized Root Configs & Orchestration
345
+ const rootConfigFiles = [
346
+ "spectra.config.ts",
347
+ "tsconfig.spectra.json",
348
+ "tsconfig.spectra.web.json",
349
+ "tsconfig.spectra.android.json",
350
+ "tsconfig.spectra.ios.json",
351
+ "nx.json",
352
+ ];
353
+ for (const cf of rootConfigFiles) {
354
+ const src = path.join(templateDir, cf);
355
+ const dest = path.join(cwd, cf);
356
+ if (fs.existsSync(src) && (!fs.existsSync(dest) || options.force)) {
357
+ fs.copyFileSync(src, dest);
358
+ }
359
+ }
360
+ // Ensure root package.json has TestSpectra devDependencies
361
+ const rootPkgPath = path.join(cwd, "package.json");
362
+ if (!fs.existsSync(rootPkgPath)) {
363
+ const templatePkgSrc = path.join(templateDir, "package.json");
364
+ if (fs.existsSync(templatePkgSrc)) {
365
+ copyRecursive(templatePkgSrc, rootPkgPath);
366
+ }
367
+ }
368
+ else {
369
+ try {
370
+ const rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, "utf-8"));
371
+ if (!rootPkg.devDependencies)
372
+ rootPkg.devDependencies = {};
373
+ if (!rootPkg.devDependencies["@testspectra/cli"])
374
+ rootPkg.devDependencies["@testspectra/cli"] = cliDepVersion;
375
+ if (!rootPkg.devDependencies["typescript"])
376
+ rootPkg.devDependencies["typescript"] = "^5.4.5";
377
+ if (!rootPkg.devDependencies["@wdio/globals"])
378
+ rootPkg.devDependencies["@wdio/globals"] = "^9.2.8";
379
+ if (!rootPkg.devDependencies["@wdio/mocha-framework"])
380
+ rootPkg.devDependencies["@wdio/mocha-framework"] = "^9.2.8";
381
+ if (!rootPkg.devDependencies["@types/node"])
382
+ rootPkg.devDependencies["@types/node"] = "^20.14.0";
383
+ if (!rootPkg.devDependencies["webdriverio"])
384
+ rootPkg.devDependencies["webdriverio"] = "^9.2.8";
385
+ fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2), "utf-8");
386
+ }
387
+ catch { }
342
388
  }
343
389
  // 2. Centralized Root .testspectra folder for cache/logs
344
390
  const rootTestDataDir = path.join(cwd, ".testspectra");
@@ -385,36 +431,28 @@ export async function initCommand(options = {}) {
385
431
  // 5. Ensure pnpm-workspace.yaml includes shared library (feature modules are already part of existing workspace globs)
386
432
  const pnpmWorkspaceFilePath = path.join(cwd, "pnpm-workspace.yaml");
387
433
  ensurePnpmWorkspaceIncludes(pnpmWorkspaceFilePath, sharedTestingRelPath);
388
- // 6. Update Root Solution tsconfig.json references
434
+ // 6. Update Root Solution tsconfig.json to reference tsconfig.spectra.json
389
435
  const rootTsConfigPath = path.join(cwd, "tsconfig.json");
390
- const references = [];
391
- for (const e2eDir of featureE2eDirs) {
392
- references.push({ path: `./${path.relative(cwd, e2eDir).replace(/\\/g, "/")}` });
393
- }
394
- references.push({ path: `./${sharedTestingRelPath.replace(/\\/g, "/")}` });
395
436
  let rootTsConfig = { files: [], references: [] };
396
437
  if (fs.existsSync(rootTsConfigPath)) {
397
438
  try {
398
439
  rootTsConfig = JSON.parse(fs.readFileSync(rootTsConfigPath, "utf-8"));
399
440
  if (!rootTsConfig.references)
400
441
  rootTsConfig.references = [];
401
- for (const ref of references) {
402
- if (!rootTsConfig.references.some((r) => r.path === ref.path)) {
403
- rootTsConfig.references.push(ref);
404
- }
442
+ if (!rootTsConfig.references.some((r) => r.path === "./tsconfig.spectra.json")) {
443
+ rootTsConfig.references.push({ path: "./tsconfig.spectra.json" });
405
444
  }
406
445
  }
407
- catch { }
446
+ catch {
447
+ rootTsConfig = { files: [], references: [{ path: "./tsconfig.spectra.json" }] };
448
+ }
408
449
  }
409
450
  else {
410
- rootTsConfig = { files: [], references };
451
+ rootTsConfig = { files: [], references: [{ path: "./tsconfig.spectra.json" }] };
411
452
  }
412
453
  fs.writeFileSync(rootTsConfigPath, JSON.stringify(rootTsConfig, null, 2), "utf-8");
413
- // 7. Generate Ambient Types for Shared + All E2E Feature Directories
454
+ // 7. Generate Ambient Types in ROOT only
414
455
  TypeGenerator.writeDeclarationFiles(cwd);
415
- for (const e2eDir of featureE2eDirs) {
416
- TypeGenerator.writeDeclarationFiles(e2eDir);
417
- }
418
456
  const sharedPoLine = `${sharedTestingRelPath}/page-objects`.padEnd(28, " ");
419
457
  const sharedStepsLine = `${sharedTestingRelPath}/steps`.padEnd(28, " ");
420
458
  const sharedActLine = `${sharedTestingRelPath}/actions`.padEnd(28, " ");
@@ -430,6 +468,10 @@ This workspace uses TestSpectra's **"Centralized Configuration, Distributed Impl
430
468
  ### Centralized Root Elements
431
469
  - \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
432
470
  - \`./.testspectra/\`: Centralized app data folder containing driver caches (ChromeDriver, GeckoDriver, Appium), execution logs, and universal ambient types (\`.testspectra/types/\`).
471
+ - \`./tsconfig.spectra.json\`: Solution TypeScript entry point equipped with TestSpectra language service plugin.
472
+ - \`./tsconfig.spectra.web.json\`: Strongly-typed composite configuration for Web platform tests.
473
+ - \`./tsconfig.spectra.android.json\`: Strongly-typed composite configuration for Android platform tests.
474
+ - \`./tsconfig.spectra.ios.json\`: Strongly-typed composite configuration for iOS platform tests.
433
475
  - \`./nx.json\`: Nx target defaults with execution caching and inputs.
434
476
  - \`./pnpm-workspace.yaml\`: Monorepo package glob definitions.
435
477
  - \`./tsconfig.json\`: Solution-style TypeScript orchestrator linking all distributed E2E projects and shared libraries.
@@ -449,8 +491,7 @@ ${featureE2eDirs.map((d) => {
449
491
  return `- \`./${rel}\` (**${name}**, Nx Project: \`${e2eName}\`):
450
492
  - \`specs/\`: Feature test cases (\`specs/<SuiteName>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
451
493
  - \`page-objects/\`: Feature-specific Page Objects (e.g. \`LoginPage\`, \`ProductPage\`).
452
- - \`project.json\`: Nx targets (\`e2e\`, \`type-check\`) supporting configurations (\`--configuration=android\`, \`--configuration=ios\`, \`--configuration=headless\`).
453
- - \`tsconfig.json\`: Solution TypeScript config reference.`;
494
+ - \`project.json\`: Nx targets (\`e2e\`, \`type-check\`) supporting configurations (\`--configuration=android\`, \`--configuration=ios\`, \`--configuration=headless\`).`;
454
495
  }).join("\n")}
455
496
 
456
497
  ---
@@ -10,91 +10,72 @@ const PLATFORM_HIERARCHY = {
10
10
  export class TypeGenerator {
11
11
  static getPageObjectsDirs(cwd) {
12
12
  const dirs = [];
13
- const p1 = path.join(cwd, "page-objects");
14
- const p2 = path.join(cwd, "pageobjects");
15
- if (fs.existsSync(p1))
16
- dirs.push(p1);
17
- else if (fs.existsSync(p2))
18
- dirs.push(p2);
19
- // Look for shared directories in monorepo root
20
- let cur = cwd;
21
- while (cur !== path.dirname(cur)) {
22
- const candidates = [
23
- path.join(cur, "shared/testing/page-objects"),
24
- path.join(cur, "shared/page-objects"),
25
- path.join(cur, "packages/core/testing/page-objects"),
26
- path.join(cur, "packages/shared/testing/page-objects"),
27
- path.join(cur, "packages/testing/page-objects"),
28
- path.join(cur, "core/testing/page-objects"),
29
- ];
30
- for (const cand of candidates) {
31
- if (fs.existsSync(cand) && !dirs.includes(cand))
32
- dirs.push(cand);
13
+ function scanDir(dir, maxDepth) {
14
+ if (maxDepth <= 0 || !fs.existsSync(dir))
15
+ return;
16
+ const base = path.basename(dir);
17
+ if (base === "node_modules" || base === "dist" || base === ".git" || base === ".testspectra")
18
+ return;
19
+ if (base === "page-objects" || base === "pageobjects") {
20
+ if (!dirs.includes(dir))
21
+ dirs.push(dir);
22
+ return;
33
23
  }
34
- // Also dynamically check tsconfig.json references if in root
35
- const rootTsConfig = path.join(cur, "tsconfig.json");
36
- if (fs.existsSync(rootTsConfig)) {
37
- try {
38
- const parsed = JSON.parse(fs.readFileSync(rootTsConfig, "utf-8"));
39
- if (Array.isArray(parsed.references)) {
40
- for (const ref of parsed.references) {
41
- if (ref.path) {
42
- const poPath1 = path.resolve(cur, ref.path, "page-objects");
43
- const poPath2 = path.resolve(cur, ref.path, "pageobjects");
44
- if (fs.existsSync(poPath1) && !dirs.includes(poPath1))
45
- dirs.push(poPath1);
46
- if (fs.existsSync(poPath2) && !dirs.includes(poPath2))
47
- dirs.push(poPath2);
48
- }
49
- }
24
+ try {
25
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
26
+ for (const entry of entries) {
27
+ if (entry.isDirectory()) {
28
+ scanDir(path.join(dir, entry.name), maxDepth - 1);
50
29
  }
51
30
  }
52
- catch { }
53
31
  }
54
- if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml")) || fs.existsSync(path.join(cur, "nx.json")))
32
+ catch { }
33
+ }
34
+ // 1. Scan from cwd downwards (finds all feature page-objects and shared page-objects)
35
+ scanDir(cwd, 6);
36
+ // 2. Also look upwards to monorepo root if called from a subfolder
37
+ let cur = cwd;
38
+ while (cur !== path.dirname(cur)) {
39
+ if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml")) || fs.existsSync(path.join(cur, "nx.json")) || fs.existsSync(path.join(cur, "spectra.config.ts"))) {
40
+ scanDir(cur, 6);
55
41
  break;
42
+ }
56
43
  cur = path.dirname(cur);
57
44
  }
58
45
  return dirs;
59
46
  }
60
47
  static getEntityDirs(cwd, entityType) {
61
48
  const dirs = [];
62
- const local = path.join(cwd, entityType);
63
- if (fs.existsSync(local))
64
- dirs.push(local);
65
- let cur = cwd;
66
- while (cur !== path.dirname(cur)) {
67
- const candidates = [
68
- path.join(cur, "shared/testing", entityType),
69
- path.join(cur, "shared", entityType),
70
- path.join(cur, "packages/core/testing", entityType),
71
- path.join(cur, "packages/shared/testing", entityType),
72
- path.join(cur, "packages/testing", entityType),
73
- path.join(cur, "core/testing", entityType),
74
- ];
75
- for (const cand of candidates) {
76
- if (fs.existsSync(cand) && !dirs.includes(cand))
77
- dirs.push(cand);
49
+ function scanDir(dir, maxDepth) {
50
+ if (maxDepth <= 0 || !fs.existsSync(dir))
51
+ return;
52
+ const base = path.basename(dir);
53
+ if (base === "node_modules" || base === "dist" || base === ".git" || base === ".testspectra")
54
+ return;
55
+ if (base === entityType) {
56
+ if (!dirs.includes(dir))
57
+ dirs.push(dir);
58
+ return;
78
59
  }
79
- // Also dynamically check tsconfig.json references
80
- const rootTsConfig = path.join(cur, "tsconfig.json");
81
- if (fs.existsSync(rootTsConfig)) {
82
- try {
83
- const parsed = JSON.parse(fs.readFileSync(rootTsConfig, "utf-8"));
84
- if (Array.isArray(parsed.references)) {
85
- for (const ref of parsed.references) {
86
- if (ref.path) {
87
- const entPath = path.resolve(cur, ref.path, entityType);
88
- if (fs.existsSync(entPath) && !dirs.includes(entPath))
89
- dirs.push(entPath);
90
- }
91
- }
60
+ try {
61
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
62
+ for (const entry of entries) {
63
+ if (entry.isDirectory()) {
64
+ scanDir(path.join(dir, entry.name), maxDepth - 1);
92
65
  }
93
66
  }
94
- catch { }
95
67
  }
96
- if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml")) || fs.existsSync(path.join(cur, "nx.json")))
68
+ catch { }
69
+ }
70
+ // 1. Scan from cwd downwards
71
+ scanDir(cwd, 6);
72
+ // 2. Look upwards to monorepo root if called from a subfolder
73
+ let cur = cwd;
74
+ while (cur !== path.dirname(cur)) {
75
+ if (fs.existsSync(path.join(cur, "pnpm-workspace.yaml")) || fs.existsSync(path.join(cur, "nx.json")) || fs.existsSync(path.join(cur, "spectra.config.ts"))) {
76
+ scanDir(cur, 6);
97
77
  break;
78
+ }
98
79
  cur = path.dirname(cur);
99
80
  }
100
81
  return dirs;
@@ -207,8 +188,28 @@ export class TypeGenerator {
207
188
  }
208
189
  }
209
190
  }
210
- // 2. Actions Global Interface (Local + Shared)
211
- content += `\ninterface TestSpectraActions extends Omit<typeof import('@testspectra/cli').Spectra, ''> {\n`;
191
+ // 2. Built-in Spectra Static Methods & Custom Actions
192
+ content += `\ninterface SpectraStaticInstance {\n`;
193
+ content += ` get(target: any): import('@testspectra/matchers').SingleElementRunner;\n`;
194
+ content += ` getAll(selector: string): import('@testspectra/matchers').MultiElementRunner;\n`;
195
+ content += ` navigate(url: string): import('@testspectra/matchers').SingleElementRunner;\n`;
196
+ content += ` back(): import('@testspectra/matchers').SingleElementRunner;\n`;
197
+ content += ` refresh(): import('@testspectra/matchers').SingleElementRunner;\n`;
198
+ content += ` click(target: any, textOrOptions?: string | import('@testspectra/matchers').ClickOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
199
+ content += ` doubleClick(target: any, textOrOptions?: string | import('@testspectra/matchers').ClickOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
200
+ content += ` longPress(target: any, options?: import('@testspectra/matchers').LongPressOptions | number): import('@testspectra/matchers').SingleElementRunner;\n`;
201
+ content += ` type(target: any, value: string, options?: import('@testspectra/matchers').TypeOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
202
+ content += ` clear(target: any): import('@testspectra/matchers').SingleElementRunner;\n`;
203
+ content += ` select(target: any, value: string): import('@testspectra/matchers').SingleElementRunner;\n`;
204
+ content += ` hover(target: any): import('@testspectra/matchers').SingleElementRunner;\n`;
205
+ content += ` pressKey(key: import('@testspectra/matchers').KeyOption | string): import('@testspectra/matchers').SingleElementRunner;\n`;
206
+ content += ` dragDrop(sourceTarget: any, destTarget: any): import('@testspectra/matchers').SingleElementRunner;\n`;
207
+ content += ` scroll(options?: import('@testspectra/matchers').ScrollOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
208
+ content += ` swipe(options: import('@testspectra/matchers').SwipeOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
209
+ content += ` wait(durationMs: number): import('@testspectra/matchers').SingleElementRunner;\n`;
210
+ content += ` waitForElement(target: any, timeoutMs?: number): import('@testspectra/matchers').SingleElementRunner;\n`;
211
+ content += `}\n\n`;
212
+ content += `interface TestSpectraCustomActions {\n`;
212
213
  const declaredActions = new Set();
213
214
  for (const actionsDir of actionsDirs) {
214
215
  if (!fs.existsSync(actionsDir))
@@ -259,6 +260,7 @@ export class TypeGenerator {
259
260
  }
260
261
  }
261
262
  content += `}\n`;
263
+ content += `type TestSpectraActions = SpectraStaticInstance & TestSpectraCustomActions;\n`;
262
264
  content += `declare const Spectra: TestSpectraActions;\n\n`;
263
265
  // 3. Shared Steps Global Interface (Local + Shared)
264
266
  content += `interface TestSpectraSteps {\n`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/cli",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,20 +1,6 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
7
- "plugins": [
8
- { "name": "@testspectra/cli" }
9
- ],
10
- "skipLibCheck": true,
11
- "strict": true,
12
- "noEmit": true
13
- },
14
2
  "files": [],
15
3
  "references": [
16
- { "path": "./tsconfig.web.json" },
17
- { "path": "./tsconfig.android.json" },
18
- { "path": "./tsconfig.ios.json" }
4
+ { "path": "./tsconfig.spectra.json" }
19
5
  ]
20
6
  }
@@ -3,18 +3,17 @@
3
3
  "target": "ES2022",
4
4
  "module": "NodeNext",
5
5
  "moduleResolution": "NodeNext",
6
- "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
7
- "plugins": [
8
- { "name": "@testspectra/cli" }
9
- ],
10
6
  "skipLibCheck": true,
11
7
  "strict": true,
12
- "noEmit": true
8
+ "noEmit": true,
9
+ "plugins": [
10
+ { "name": "@testspectra/cli" }
11
+ ]
13
12
  },
14
13
  "files": [],
15
14
  "references": [
16
- { "path": "./tsconfig.web.json" },
17
- { "path": "./tsconfig.android.json" },
18
- { "path": "./tsconfig.ios.json" }
15
+ { "path": "./tsconfig.spectra.web.json" },
16
+ { "path": "./tsconfig.spectra.android.json" },
17
+ { "path": "./tsconfig.spectra.ios.json" }
19
18
  ]
20
19
  }
@@ -25,11 +25,10 @@
25
25
  "type-check": {
26
26
  "executor": "nx:run-commands",
27
27
  "options": {
28
- "command": "tsc -b",
29
- "cwd": "modules/auth/e2e"
28
+ "command": "tsc -b tsconfig.spectra.json",
29
+ "cwd": "."
30
30
  }
31
31
  }
32
32
  },
33
- "tags": ["scope:auth", "type:e2e"],
34
- "implicitDependencies": ["@testspectra/feature-auth"]
33
+ "tags": ["scope:auth", "type:e2e"]
35
34
  }
@@ -25,11 +25,10 @@
25
25
  "type-check": {
26
26
  "executor": "nx:run-commands",
27
27
  "options": {
28
- "command": "tsc -b",
29
- "cwd": "modules/catalog/e2e"
28
+ "command": "tsc -b tsconfig.spectra.json",
29
+ "cwd": "."
30
30
  }
31
31
  }
32
32
  },
33
- "tags": ["scope:catalog", "type:e2e"],
34
- "implicitDependencies": ["@testspectra/feature-catalog"]
33
+ "tags": ["scope:catalog", "type:e2e"]
35
34
  }
@@ -6,7 +6,7 @@
6
6
  "scripts": {
7
7
  "e2e": "nx run-many -t e2e",
8
8
  "e2e:affected": "nx affected -t e2e",
9
- "type-check": "tsc -b",
9
+ "type-check": "tsc -b tsconfig.spectra.json",
10
10
  "build": "nx run-many -t build"
11
11
  },
12
12
  "devDependencies": {
@@ -1,9 +1,9 @@
1
1
  export default class NavigationBar {
2
- static get logo() {
2
+ static get logo(): ChainablePromiseElement {
3
3
  return $('header .logo');
4
4
  }
5
5
 
6
- static get profileMenu() {
6
+ static get profileMenu(): ChainablePromiseElement {
7
7
  return $('header .user-profile');
8
8
  }
9
9
 
@@ -1,8 +1,6 @@
1
1
  {
2
2
  "files": [],
3
3
  "references": [
4
- { "path": "./modules/auth/e2e" },
5
- { "path": "./modules/catalog/e2e" },
6
- { "path": "./shared/testing" }
4
+ { "path": "./tsconfig.spectra.json" }
7
5
  ]
8
6
  }
@@ -0,0 +1,36 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "composite": true,
7
+ "noEmit": true,
8
+ "skipLibCheck": true,
9
+ "strict": true,
10
+ "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
11
+ },
12
+ "include": [
13
+ ".testspectra/types/android.d.ts",
14
+ ".testspectra/types/fixtures.d.ts",
15
+ "spectra.config.ts",
16
+ "modules/**/e2e/**/*.ts",
17
+ "packages/**/e2e/**/*.ts",
18
+ "shared/testing/**/*.ts"
19
+ ],
20
+ "exclude": [
21
+ "node_modules",
22
+ "dist",
23
+ "**/page-objects/**/web.ts",
24
+ "**/page-objects/**/ios.ts",
25
+ "**/steps/**/web.step.ts",
26
+ "**/steps/**/ios.step.ts",
27
+ "**/actions/**/web.action.ts",
28
+ "**/actions/**/ios.action.ts",
29
+ "**/global-hooks/**/web.hook.ts",
30
+ "**/global-hooks/**/ios.hook.ts",
31
+ "**/specs/**/hooks/**/web.hook.ts",
32
+ "**/specs/**/hooks/**/ios.hook.ts",
33
+ "**/specs/**/web.test.ts",
34
+ "**/specs/**/ios.test.ts"
35
+ ]
36
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "composite": true,
7
+ "noEmit": true,
8
+ "skipLibCheck": true,
9
+ "strict": true,
10
+ "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
11
+ },
12
+ "include": [
13
+ ".testspectra/types/ios.d.ts",
14
+ ".testspectra/types/fixtures.d.ts",
15
+ "spectra.config.ts",
16
+ "modules/**/e2e/**/*.ts",
17
+ "packages/**/e2e/**/*.ts",
18
+ "shared/testing/**/*.ts"
19
+ ],
20
+ "exclude": [
21
+ "node_modules",
22
+ "dist",
23
+ "**/page-objects/**/web.ts",
24
+ "**/page-objects/**/android.ts",
25
+ "**/steps/**/web.step.ts",
26
+ "**/steps/**/android.step.ts",
27
+ "**/actions/**/web.action.ts",
28
+ "**/actions/**/android.action.ts",
29
+ "**/global-hooks/**/web.hook.ts",
30
+ "**/global-hooks/**/android.hook.ts",
31
+ "**/specs/**/hooks/**/web.hook.ts",
32
+ "**/specs/**/hooks/**/android.hook.ts",
33
+ "**/specs/**/web.test.ts",
34
+ "**/specs/**/android.test.ts"
35
+ ]
36
+ }
@@ -3,18 +3,17 @@
3
3
  "target": "ES2022",
4
4
  "module": "NodeNext",
5
5
  "moduleResolution": "NodeNext",
6
- "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
7
- "plugins": [
8
- { "name": "@testspectra/cli" }
9
- ],
10
6
  "skipLibCheck": true,
11
7
  "strict": true,
12
- "noEmit": true
8
+ "noEmit": true,
9
+ "plugins": [
10
+ { "name": "@testspectra/cli" }
11
+ ]
13
12
  },
14
13
  "files": [],
15
14
  "references": [
16
- { "path": "./tsconfig.web.json" },
17
- { "path": "./tsconfig.android.json" },
18
- { "path": "./tsconfig.ios.json" }
15
+ { "path": "./tsconfig.spectra.web.json" },
16
+ { "path": "./tsconfig.spectra.android.json" },
17
+ { "path": "./tsconfig.spectra.ios.json" }
19
18
  ]
20
19
  }