@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
@@ -10,94 +10,23 @@ export async function initCommand(options) {
10
10
  console.log(`\x1b[33m[TestSpectra]\x1b[0m Config already exists at ${path.basename(existingConfig)}. Use --force to overwrite.`);
11
11
  return;
12
12
  }
13
- // 1. Ensure directories exist
14
- const dirs = [
15
- "specs/TC-LOGIN-01",
16
- "page-objects/LoginPage",
17
- "actions/verifyOtp",
18
- "steps/loginUser",
19
- "fixtures",
20
- "hooks/default",
21
- ".testspectra",
22
- ];
23
- for (const d of dirs) {
24
- const dirPath = path.join(cwd, d);
25
- if (!fs.existsSync(dirPath)) {
26
- fs.mkdirSync(dirPath, { recursive: true });
27
- }
13
+ // 1. Resolve template directory
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+ // Look for templates in ../templates/default (from src) or ../../templates/default (from dist)
17
+ let templateDir = path.resolve(__dirname, "../templates/default");
18
+ if (!fs.existsSync(templateDir)) {
19
+ templateDir = path.resolve(__dirname, "../../templates/default");
20
+ }
21
+ if (!fs.existsSync(templateDir)) {
22
+ templateDir = path.resolve(__dirname, "../../../templates/default");
23
+ }
24
+ if (!fs.existsSync(templateDir)) {
25
+ throw new Error(`[TestSpectra] Scaffold template directory not found at: ${templateDir}`);
28
26
  }
29
- // 2. Create spectra.config.ts
30
- const configContent = `import { defineConfig } from "@testspectra/cli";
31
-
32
- export default defineConfig({
33
- webConfig: {
34
- baseUrl: "https://the-internet.herokuapp.com",
35
- maxConcurrentSessions: "1",
36
- headlessMode: true,
37
- implicitWait: "5000",
38
- pageLoadTimeout: "30000",
39
- scriptTimeout: "30000",
40
- parallelizationMode: "testcase",
41
- },
42
- browsers: [
43
- {
44
- id: "chrome-desktop",
45
- type: "chrome",
46
- mobileEmulation: false,
47
- },
48
- ],
49
- androidConfig: {
50
- appiumServer: "http://127.0.0.1:4723",
51
- platformName: "Android",
52
- platformVersion: "13",
53
- deviceName: "emulator-5554",
54
- automationName: "UiAutomator2",
55
- appPackage: "",
56
- appActivity: "",
57
- autoGrantPermissions: true,
58
- noReset: false,
59
- implicitWait: "10000",
60
- parallelizationMode: "suite",
61
- },
62
- iosConfig: {
63
- appiumServer: "http://127.0.0.1:4723",
64
- platformName: "iOS",
65
- platformVersion: "16.0",
66
- deviceName: "iPhone 14",
67
- automationName: "XCUITest",
68
- bundleId: "",
69
- udid: "auto",
70
- xcodeOrgId: "",
71
- xcodeSigningId: "iPhone Developer",
72
- autoAcceptAlerts: true,
73
- noReset: false,
74
- implicitWait: "10000",
75
- parallelizationMode: "suite",
76
- },
77
- loadConfig: {
78
- virtualUsers: "10",
79
- duration: "1m",
80
- },
81
- loadStages: [],
82
- thresholds: [],
83
- executionConfig: {
84
- networkMonitoringEnabled: true,
85
- fastResponseTime: "200",
86
- normalResponseTime: "1000",
87
- monitoredDomains: [],
88
- environmentVariables: [],
89
- },
90
- });
91
- `;
92
- fs.writeFileSync(path.join(cwd, "spectra.config.ts"), configContent, "utf-8");
93
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Initialized spectra.config.ts with defineConfig`);
94
- // 3. Create or update package.json with npm scripts & devDependencies
95
- const packageJsonPath = path.join(cwd, "package.json");
96
- // Dynamically resolve CLI version
27
+ // 2. Dynamically resolve CLI version
97
28
  let cliVersion = "^1.0.6";
98
29
  try {
99
- const __filename = fileURLToPath(import.meta.url);
100
- const __dirname = path.dirname(__filename);
101
30
  const cliPackageJsonPath = path.resolve(__dirname, "../../package.json");
102
31
  if (fs.existsSync(cliPackageJsonPath)) {
103
32
  const cliPkg = JSON.parse(fs.readFileSync(cliPackageJsonPath, "utf-8"));
@@ -124,322 +53,53 @@ export default defineConfig({
124
53
  cur = path.dirname(cur);
125
54
  }
126
55
  const cliDepVersion = isInternalWorkspace ? "workspace:*" : cliVersion;
127
- let pkg = {
128
- name: path.basename(cwd),
129
- version: "1.0.0",
130
- private: true,
131
- type: "module",
132
- scripts: {
133
- test: "spectra run",
134
- "type-check": "tsc -b",
135
- },
136
- devDependencies: {
137
- "@testspectra/cli": cliDepVersion,
138
- "@types/node": "^20.14.0",
139
- "@wdio/globals": "^9.2.8",
140
- "@wdio/mocha-framework": "^9.2.8",
141
- "webdriverio": "^9.2.8",
142
- "typescript": "^5.4.5",
143
- },
144
- };
145
- if (fs.existsSync(packageJsonPath)) {
146
- try {
147
- const existing = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
148
- pkg = {
149
- ...existing,
150
- scripts: {
151
- ...(existing.scripts || {}),
152
- test: existing.scripts?.test || "spectra run",
153
- "type-check": existing.scripts?.["type-check"] || "tsc -b",
154
- },
155
- devDependencies: {
156
- ...(existing.devDependencies || {}),
157
- "@testspectra/cli": existing.devDependencies?.["@testspectra/cli"]?.startsWith("workspace:") && !isInternalWorkspace
158
- ? cliVersion
159
- : existing.devDependencies?.["@testspectra/cli"] || cliDepVersion,
160
- "@types/node": existing.devDependencies?.["@types/node"] || "^20.14.0",
161
- "@wdio/globals": existing.devDependencies?.["@wdio/globals"] || "^9.2.8",
162
- "@wdio/mocha-framework": existing.devDependencies?.["@wdio/mocha-framework"] || "^9.2.8",
163
- "webdriverio": existing.devDependencies?.["webdriverio"] || "^9.2.8",
164
- "typescript": existing.devDependencies?.["typescript"] || "^5.4.5",
165
- },
166
- };
56
+ const projectName = path.basename(cwd);
57
+ // 3. Recursive copy function with variable replacement
58
+ function copyRecursive(src, dest) {
59
+ const base = path.basename(src);
60
+ if (base === "node_modules" || base === "dist" || base === ".testspectra" || base === ".git") {
61
+ return;
62
+ }
63
+ const stat = fs.statSync(src);
64
+ if (stat.isDirectory()) {
65
+ if (!fs.existsSync(dest)) {
66
+ fs.mkdirSync(dest, { recursive: true });
67
+ }
68
+ const entries = fs.readdirSync(src);
69
+ for (const entry of entries) {
70
+ copyRecursive(path.join(src, entry), path.join(dest, entry));
71
+ }
72
+ }
73
+ else {
74
+ // If destination exists and force is not set, skip
75
+ if (fs.existsSync(dest) && !options.force) {
76
+ return;
77
+ }
78
+ let content = fs.readFileSync(src, "utf-8");
79
+ if (src.endsWith("package.json")) {
80
+ try {
81
+ const pkgObj = JSON.parse(content);
82
+ pkgObj.name = projectName;
83
+ pkgObj.devDependencies = pkgObj.devDependencies || {};
84
+ pkgObj.devDependencies["@testspectra/cli"] = cliDepVersion;
85
+ content = JSON.stringify(pkgObj, null, 2);
86
+ }
87
+ catch { }
88
+ }
89
+ fs.writeFileSync(dest, content, "utf-8");
167
90
  }
168
- catch { }
169
- }
170
- fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2), "utf-8");
171
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Updated package.json (scripts & devDependencies)`);
172
- // 3b. Create .gitignore
173
- const gitignorePath = path.join(cwd, ".gitignore");
174
- if (!fs.existsSync(gitignorePath) || options.force) {
175
- const gitignoreContent = `node_modules/\ndist/\n.testspectra/\n*.tsbuildinfo\n`;
176
- fs.writeFileSync(gitignorePath, gitignoreContent, "utf-8");
177
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Created .gitignore`);
178
91
  }
179
- // 4. Create platform tsconfigs with Solution-Style Project References & TS Plugin
180
- const tsconfigRoot = {
181
- compilerOptions: {
182
- target: "ES2022",
183
- module: "NodeNext",
184
- moduleResolution: "NodeNext",
185
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
186
- plugins: [
187
- { name: "@testspectra/cli" }
188
- ],
189
- skipLibCheck: true,
190
- strict: true,
191
- noEmit: true,
192
- },
193
- files: ["spectra.config.ts"],
194
- references: [
195
- { path: "./tsconfig.web.json" },
196
- { path: "./tsconfig.android.json" },
197
- { path: "./tsconfig.ios.json" },
198
- ],
199
- };
200
- fs.writeFileSync(path.join(cwd, "tsconfig.json"), JSON.stringify(tsconfigRoot, null, 2), "utf-8");
201
- const tsconfigWeb = {
202
- compilerOptions: {
203
- target: "ES2022",
204
- module: "NodeNext",
205
- moduleResolution: "NodeNext",
206
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
207
- skipLibCheck: true,
208
- strict: true,
209
- composite: true,
210
- emitDeclarationOnly: true,
211
- outDir: "./.testspectra/.build/web",
212
- },
213
- include: [
214
- "specs/**/web.test.ts",
215
- "specs/**/common.test.ts",
216
- "page-objects/**/web.ts",
217
- "page-objects/**/common.ts",
218
- "actions/**/web.action.ts",
219
- "actions/**/common.action.ts",
220
- "steps/**/web.step.ts",
221
- "steps/**/common.step.ts",
222
- "hooks/**/before.web.hook.ts",
223
- "hooks/**/before.hook.ts",
224
- ".testspectra/types/web.d.ts",
225
- ".testspectra/types/common.d.ts",
226
- ".testspectra/types/fixtures.d.ts",
227
- ],
228
- };
229
- fs.writeFileSync(path.join(cwd, "tsconfig.web.json"), JSON.stringify(tsconfigWeb, null, 2), "utf-8");
230
- const tsconfigAndroid = {
231
- compilerOptions: {
232
- target: "ES2022",
233
- module: "NodeNext",
234
- moduleResolution: "NodeNext",
235
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
236
- skipLibCheck: true,
237
- strict: true,
238
- composite: true,
239
- emitDeclarationOnly: true,
240
- outDir: "./.testspectra/.build/android",
241
- },
242
- include: [
243
- "specs/**/android.test.ts",
244
- "specs/**/mobile.test.ts",
245
- "specs/**/common.test.ts",
246
- "page-objects/**/android.ts",
247
- "page-objects/**/mobile.ts",
248
- "page-objects/**/common.ts",
249
- "actions/**/android.action.ts",
250
- "actions/**/mobile.action.ts",
251
- "actions/**/common.action.ts",
252
- "steps/**/android.step.ts",
253
- "steps/**/mobile.step.ts",
254
- "steps/**/common.step.ts",
255
- "hooks/**/before.android.hook.ts",
256
- "hooks/**/before.mobile.hook.ts",
257
- "hooks/**/before.hook.ts",
258
- ".testspectra/types/android.d.ts",
259
- ".testspectra/types/mobile.d.ts",
260
- ".testspectra/types/common.d.ts",
261
- ".testspectra/types/fixtures.d.ts",
262
- ],
263
- };
264
- fs.writeFileSync(path.join(cwd, "tsconfig.android.json"), JSON.stringify(tsconfigAndroid, null, 2), "utf-8");
265
- const tsconfigIos = {
266
- compilerOptions: {
267
- target: "ES2022",
268
- module: "NodeNext",
269
- moduleResolution: "NodeNext",
270
- types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
271
- skipLibCheck: true,
272
- strict: true,
273
- composite: true,
274
- emitDeclarationOnly: true,
275
- outDir: "./.testspectra/.build/ios",
276
- },
277
- include: [
278
- "specs/**/ios.test.ts",
279
- "specs/**/mobile.test.ts",
280
- "specs/**/common.test.ts",
281
- "page-objects/**/ios.ts",
282
- "page-objects/**/mobile.ts",
283
- "page-objects/**/common.ts",
284
- "actions/**/ios.action.ts",
285
- "actions/**/mobile.action.ts",
286
- "actions/**/common.action.ts",
287
- "steps/**/ios.step.ts",
288
- "steps/**/mobile.step.ts",
289
- "steps/**/common.step.ts",
290
- "hooks/**/before.ios.hook.ts",
291
- "hooks/**/before.mobile.hook.ts",
292
- "hooks/**/before.hook.ts",
293
- ".testspectra/types/ios.d.ts",
294
- ".testspectra/types/mobile.d.ts",
295
- ".testspectra/types/common.d.ts",
296
- ".testspectra/types/fixtures.d.ts",
297
- ],
298
- };
299
- fs.writeFileSync(path.join(cwd, "tsconfig.ios.json"), JSON.stringify(tsconfigIos, null, 2), "utf-8");
300
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Created platform-isolated tsconfigs with solution references`);
301
- // 5. Scaffold Fixture File
302
- const fixturePath = path.join(cwd, "fixtures", "userData.json");
303
- if (!fs.existsSync(fixturePath) || options.force) {
304
- fs.writeFileSync(fixturePath, JSON.stringify({ username: "tomsmith", role: "admin" }, null, 2), "utf-8");
92
+ copyRecursive(templateDir, cwd);
93
+ // 4. Pre-configure pnpm allowBuilds for standalone projects
94
+ if (!isInternalWorkspace) {
95
+ const pnpmWorkspacePath = path.join(cwd, "pnpm-workspace.yaml");
96
+ if (!fs.existsSync(pnpmWorkspacePath) || options.force) {
97
+ const pnpmWsContent = `allowBuilds:\n chromedriver: true\n edgedriver: true\n geckodriver: true\n`;
98
+ fs.writeFileSync(pnpmWorkspacePath, pnpmWsContent, "utf-8");
99
+ }
305
100
  }
306
- // 6. Scaffold Page Objects (Clean zero-import methods with explicit ChainablePromiseElement types)
307
- const poWeb = `export default class LoginPage {
308
- static get usernameInput(): ChainablePromiseElement {
309
- return $('#username');
310
- }
311
-
312
- static get passwordInput(): ChainablePromiseElement {
313
- return $('#password');
314
- }
315
-
316
- static get submitButton(): ChainablePromiseElement {
317
- return $('button[type="submit"]');
318
- }
319
-
320
- static get flashAlert(): ChainablePromiseElement {
321
- return $('#flash');
322
- }
323
-
324
- static async open() {
325
- await Spectra.navigate('/login');
326
- }
327
-
328
- static async login(username: string, pass: string) {
329
- await Spectra.type(this.usernameInput, username);
330
- await Spectra.type(this.passwordInput, pass);
331
- await Spectra.click(this.submitButton);
332
- }
333
- }
334
- `;
335
- fs.writeFileSync(path.join(cwd, "page-objects/LoginPage/web.ts"), poWeb, "utf-8");
336
- const poMobile = `export default class LoginPage {
337
- static get usernameInput(): ChainablePromiseElement {
338
- return $('~username_input');
339
- }
340
-
341
- static get passwordInput(): ChainablePromiseElement {
342
- return $('~password_input');
343
- }
344
-
345
- static get submitButton(): ChainablePromiseElement {
346
- return $('~login_button');
347
- }
348
-
349
- static get welcomeText(): ChainablePromiseElement {
350
- return $('~welcome_text');
351
- }
352
-
353
- static async open() {
354
- // Mobile app startup
355
- }
356
-
357
- static async login(username: string, pass: string) {
358
- await Spectra.type(this.usernameInput, username);
359
- await Spectra.type(this.passwordInput, pass);
360
- await Spectra.click(this.submitButton);
361
- }
362
- }
363
- `;
364
- fs.writeFileSync(path.join(cwd, "page-objects/LoginPage/mobile.ts"), poMobile, "utf-8");
365
- // 7. Scaffold Actions (Zero triple-slash lines)
366
- const actionWeb = `export default async function verifyOtp(otp: string): Promise<void> {
367
- const otpInput = await $('#otp');
368
- await otpInput.setValue(otp);
369
- const verifyBtn = await $('#verify-btn');
370
- await verifyBtn.click();
371
- }
372
- `;
373
- fs.writeFileSync(path.join(cwd, "actions/verifyOtp/web.action.ts"), actionWeb, "utf-8");
374
- const actionMobile = `export default async function verifyOtp(otp: string): Promise<void> {
375
- const otpInput = await $('~otp_input');
376
- await otpInput.setValue(otp);
377
- const verifyBtn = await $('~verify_btn');
378
- await verifyBtn.click();
379
- }
380
- `;
381
- fs.writeFileSync(path.join(cwd, "actions/verifyOtp/mobile.action.ts"), actionMobile, "utf-8");
382
- // 8. Scaffold Steps (Zero triple-slash lines)
383
- const stepWeb = `export default async function loginUser(u: string, p: string): Promise<void> {
384
- await LoginPage.open();
385
- await LoginPage.login(u, p);
386
- }
387
- `;
388
- fs.writeFileSync(path.join(cwd, "steps/loginUser/web.step.ts"), stepWeb, "utf-8");
389
- const stepMobile = `export default async function loginUser(u: string, p: string): Promise<void> {
390
- await LoginPage.open();
391
- await LoginPage.login(u, p);
392
- }
393
- `;
394
- fs.writeFileSync(path.join(cwd, "steps/loginUser/mobile.step.ts"), stepMobile, "utf-8");
395
- // 9. Scaffold Hooks (Zero triple-slash lines)
396
- const hookWeb = `export default async function (): Promise<void> {
397
- await browser.maximizeWindow();
398
- }
399
- `;
400
- fs.writeFileSync(path.join(cwd, "hooks/default/before.web.hook.ts"), hookWeb, "utf-8");
401
- const hookAndroid = `export default async function (): Promise<void> {
402
- // Setup Android Appium capabilities
403
- }
404
- `;
405
- fs.writeFileSync(path.join(cwd, "hooks/default/before.android.hook.ts"), hookAndroid, "utf-8");
406
- const hookIos = `export default async function (): Promise<void> {
407
- // Setup iOS Appium capabilities
408
- }
409
- `;
410
- fs.writeFileSync(path.join(cwd, "hooks/default/before.ios.hook.ts"), hookIos, "utf-8");
411
- // 10. Scaffold Specs (Pure test scripts, ZERO triple-slash lines, ZERO raw selectors)
412
- const specWeb = `it("should authenticate user using Page Objects and Spectra assertions", async () => {
413
- await browser.intercept("/api/status", "GET", Fixture.userData);
414
-
415
- // 1. Navigation via Page Object
416
- await LoginPage.open();
417
-
418
- // 2. High-level business flow via Shared Step & Page Object
419
- await Step.loginUser("tomsmith", "SuperSecretPassword!");
420
-
421
- // 3. Direct callable assertion on Page Object element
422
- await LoginPage.flashAlert.shouldBeVisible();
423
- await LoginPage.flashAlert.shouldContainText("You logged into a secure area!");
424
- });
425
- `;
426
- fs.writeFileSync(path.join(cwd, "specs/TC-LOGIN-01/web.test.ts"), specWeb, "utf-8");
427
- const specAndroid = `it("should authenticate user on Android Appium device using Page Objects", async () => {
428
- await Step.loginUser("tomsmith", "SuperSecretPassword!");
429
-
430
- await LoginPage.welcomeText.shouldBeVisible();
431
- });
432
- `;
433
- fs.writeFileSync(path.join(cwd, "specs/TC-LOGIN-01/android.test.ts"), specAndroid, "utf-8");
434
- const specIos = `it("should authenticate user on iOS Appium device using Page Objects", async () => {
435
- await Step.loginUser("tomsmith", "SuperSecretPassword!");
436
-
437
- await LoginPage.welcomeText.shouldBeVisible();
438
- });
439
- `;
440
- fs.writeFileSync(path.join(cwd, "specs/TC-LOGIN-01/ios.test.ts"), specIos, "utf-8");
441
- // 11. Generate ambient declaration files
101
+ // 5. Generate ambient declaration files
442
102
  TypeGenerator.writeDeclarationFiles(cwd);
103
+ console.log(`\x1b[32m[TestSpectra]\x1b[0m Initialized project from template successfully!`);
443
104
  console.log(`\x1b[32m[TestSpectra]\x1b[0m Generated ambient multi-platform types in .testspectra/types/`);
444
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Project initialized successfully!`);
445
105
  }
@@ -31,7 +31,24 @@ export async function runCommand(specPath, options = {}) {
31
31
  });
32
32
  }
33
33
  else {
34
- // Scan specs/ directory (both entity folders specs/TC-001/web.test.ts and flat files)
34
+ // Scan suites/ directory (suites/Auth/TC-001/web.test.ts)
35
+ const suitesDir = path.join(cwd, "suites");
36
+ if (fs.existsSync(suitesDir)) {
37
+ const suiteEntries = fs.readdirSync(suitesDir, { withFileTypes: true });
38
+ for (const sEntry of suiteEntries) {
39
+ if (sEntry.name.startsWith(".") || !sEntry.isDirectory())
40
+ continue;
41
+ const currentSuite = sEntry.name;
42
+ const suitePath = path.join(suitesDir, currentSuite);
43
+ const caseEntries = fs.readdirSync(suitePath, { withFileTypes: true });
44
+ for (const cEntry of caseEntries) {
45
+ if (cEntry.name.startsWith(".") || cEntry.name === "hooks" || !cEntry.isDirectory())
46
+ continue;
47
+ testCases.push({ id: cEntry.name, title: `${currentSuite} / ${cEntry.name}` });
48
+ }
49
+ }
50
+ }
51
+ // Fallback: Scan legacy specs/ directory
35
52
  const specsDir = path.join(cwd, "specs");
36
53
  if (fs.existsSync(specsDir)) {
37
54
  const entries = fs.readdirSync(specsDir, { withFileTypes: true });
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { Command } from "commander";
2
2
  export * from "./config/schema.js";
3
3
  export * from "./config/loader.js";
4
4
  export * from "./types/generator.js";
5
- export * from "./step/index.js";
5
+ export * from "@testspectra/matchers";
6
6
  export { init as initTsPlugin, default as tsPlugin } from "./plugin.js";
7
7
  export declare function createCliProgram(): Command;
8
8
  export declare function runCli(args?: string[]): Promise<void>;
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { runCommand } from "./commands/run.js";
6
6
  export * from "./config/schema.js";
7
7
  export * from "./config/loader.js";
8
8
  export * from "./types/generator.js";
9
- export * from "./step/index.js";
9
+ export * from "@testspectra/matchers";
10
10
  export { init as initTsPlugin, default as tsPlugin } from "./plugin.js";
11
11
  export function createCliProgram() {
12
12
  const program = new Command();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.10",
4
4
  "description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,7 +13,14 @@
13
13
  "watch": "tsc -w",
14
14
  "prepublishOnly": "npm run build"
15
15
  },
16
+ "files": [
17
+ "dist",
18
+ "bin",
19
+ "templates",
20
+ "README.md"
21
+ ],
16
22
  "dependencies": {
23
+ "@testspectra/matchers": "workspace:*",
17
24
  "chalk": "^5.3.0",
18
25
  "chokidar": "^3.6.0",
19
26
  "commander": "^12.1.0",
@@ -0,0 +1,6 @@
1
+ export default async function applyDiscount(couponCode: string): Promise<void> {
2
+ const couponInput = await $('~coupon_input');
3
+ await couponInput.setValue(couponCode);
4
+ const applyBtn = await $('~btn_apply');
5
+ await applyBtn.click();
6
+ }
@@ -0,0 +1,6 @@
1
+ export default async function applyDiscount(couponCode: string): Promise<void> {
2
+ const couponInput = await $('#coupon-code');
3
+ await couponInput.setValue(couponCode);
4
+ const applyBtn = await $('#apply-coupon-btn');
5
+ await applyBtn.click();
6
+ }
@@ -0,0 +1,6 @@
1
+ export default async function verifyOtp(otp: string): Promise<void> {
2
+ const otpInput = await $('~otp_input');
3
+ await otpInput.setValue(otp);
4
+ const verifyBtn = await $('~verify_btn');
5
+ await verifyBtn.click();
6
+ }
@@ -0,0 +1,6 @@
1
+ export default async function verifyOtp(otp: string): Promise<void> {
2
+ const otpInput = await $('#otp');
3
+ await otpInput.setValue(otp);
4
+ const verifyBtn = await $('#verify-btn');
5
+ await verifyBtn.click();
6
+ }
@@ -0,0 +1 @@
1
+ "DISCOUNT50"
@@ -0,0 +1 @@
1
+ "Laptop Gaming"
@@ -0,0 +1 @@
1
+ {"username": "tomsmith", "role": "admin"}
@@ -0,0 +1,4 @@
1
+ export default async function (): Promise<void> {
2
+ // Global before setup (executed once before all suites)
3
+ await browser.maximizeWindow();
4
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@testspectra/template-default",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "spectra run",
8
+ "type-check": "tsc -b"
9
+ },
10
+ "devDependencies": {
11
+ "@testspectra/cli": "workspace:*",
12
+ "@types/node": "^20.14.0",
13
+ "@wdio/globals": "^9.2.8",
14
+ "@wdio/mocha-framework": "^9.2.8",
15
+ "webdriverio": "^9.2.8",
16
+ "typescript": "^5.4.5"
17
+ }
18
+ }
@@ -0,0 +1,32 @@
1
+ export default class LoginPage {
2
+ static get usernameInput(): ChainablePromiseElement {
3
+ return $('~username_input');
4
+ }
5
+
6
+ static get passwordInput(): ChainablePromiseElement {
7
+ return $('~password_input');
8
+ }
9
+
10
+ static get rememberMeCheckbox(): ChainablePromiseElement {
11
+ return $('~remember_me_checkbox');
12
+ }
13
+
14
+ static get submitButton(): ChainablePromiseElement {
15
+ return $('~login_button');
16
+ }
17
+
18
+ static get welcomeText(): ChainablePromiseElement {
19
+ return $('~welcome_text');
20
+ }
21
+
22
+ static async open() {
23
+ // Mobile app launch handled by Appium capabilities
24
+ }
25
+
26
+ static async login(username: string, pass: string) {
27
+ await Spectra.waitForElement(this.usernameInput);
28
+ await Spectra.type(this.usernameInput, username, { clearFirst: true });
29
+ await Spectra.type(this.passwordInput, pass, { clearFirst: true });
30
+ await Spectra.click(this.submitButton);
31
+ }
32
+ }