@testspectra/cli 1.0.3 → 1.0.5

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/README.md CHANGED
@@ -16,7 +16,7 @@ The **TestSpectra CLI** (`spectra` / `testspectra`) is the command-line orchestr
16
16
  │ │
17
17
  ▼ ▼
18
18
  ┌──────────────────────┐ ┌────────────────────────┐
19
- │ spectra watch / run │ │ Solution TSConfigs │
19
+ │ spectra run / CLI │ │ Solution TSConfigs │
20
20
  │ (CLI Orchestration) │ │ (tsconfig.*.json) │
21
21
  └───────────┬──────────┘ └────────────┬───────────┘
22
22
  │ │
@@ -63,14 +63,19 @@ Resolution order:
63
63
  - **`mobile`**: `mobile` → `common`
64
64
  - **`common`**: `common`
65
65
 
66
- ### C. Solution-Style TypeScript Configuration (`tsconfig.json`)
66
+ ### C. Solution-Style TypeScript Configuration & Language Service Plugin
67
67
  To avoid ambient type clashes between platforms (such as different method signatures across Web and Mobile Page Objects), TestSpectra uses standard TypeScript **Project References**:
68
- - **`tsconfig.json`**: Root project reference orchestrator pointing to platform configs.
68
+ - **`tsconfig.json`**: Root project reference orchestrator with `@testspectra/cli` plugin registered.
69
69
  - **`tsconfig.web.json`**: Scoped exclusively to Web files and `.testspectra/types/web.d.ts`.
70
70
  - **`tsconfig.android.json`**: Scoped exclusively to Android files and `.testspectra/types/android.d.ts`.
71
71
  - **`tsconfig.ios.json`**: Scoped exclusively to iOS files and `.testspectra/types/ios.d.ts`.
72
72
  - **`fixtures.d.ts`**: Single platform-agnostic declaration file referenced by all platform declarations.
73
73
 
74
+ #### Zero-Terminal Background Type Generation (TS Plugin)
75
+ With the built-in TypeScript Server plugin (`@testspectra/cli`), background type generation is completely automatic:
76
+ - When you open the project in **VS Code**, **Cursor**, or **WebStorm**, the IDE's TypeScript Server automatically boots the `@testspectra/cli` Language Service plugin.
77
+ - The plugin generates and updates `.testspectra/types/` ambient declarations in the background in real-time as you author Page Objects, Actions, Steps, and Fixtures.
78
+
74
79
  Project verification is executed with standard `tsc`:
75
80
  ```bash
76
81
  tsc -b
@@ -103,8 +108,9 @@ export default defineConfig({
103
108
  | Command | Description |
104
109
  | :--- | :--- |
105
110
  | `spectra init` | Scaffolds a complete multi-platform project from scratch with tsconfigs, specs, page objects, actions, steps, and fixtures. |
106
- | `spectra watch` | Starts a file-system watcher that regenerates `.testspectra/types/` ambient declarations on changes in debounced mode. |
107
111
  | `spectra run` | Generates declaration types and invokes the native Rust test runner to execute the test suite. |
112
+ | `spectra doctor` | Verifies local environment prerequisites (ADB, Java, Chrome, Bun, Node). |
113
+ | `spectra devices` | Lists connected Android/iOS devices and local browsers. |
108
114
 
109
115
  ---
110
116
 
@@ -98,7 +98,6 @@ export default defineConfig({
98
98
  private: true,
99
99
  type: "module",
100
100
  scripts: {
101
- dev: "spectra watch",
102
101
  test: "spectra run",
103
102
  "type-check": "tsc -b",
104
103
  },
@@ -118,7 +117,6 @@ export default defineConfig({
118
117
  ...existing,
119
118
  scripts: {
120
119
  ...(existing.scripts || {}),
121
- dev: existing.scripts?.dev || "spectra watch",
122
120
  test: existing.scripts?.test || "spectra run",
123
121
  "type-check": existing.scripts?.["type-check"] || "tsc -b",
124
122
  },
@@ -144,13 +142,16 @@ export default defineConfig({
144
142
  fs.writeFileSync(gitignorePath, gitignoreContent, "utf-8");
145
143
  console.log(`\x1b[32m[TestSpectra]\x1b[0m Created .gitignore`);
146
144
  }
147
- // 4. Create platform tsconfigs with Solution-Style Project References
145
+ // 4. Create platform tsconfigs with Solution-Style Project References & TS Plugin
148
146
  const tsconfigRoot = {
149
147
  compilerOptions: {
150
148
  target: "ES2022",
151
149
  module: "NodeNext",
152
150
  moduleResolution: "NodeNext",
153
151
  types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
152
+ plugins: [
153
+ { name: "@testspectra/cli" }
154
+ ],
154
155
  skipLibCheck: true,
155
156
  strict: true,
156
157
  noEmit: true,
package/dist/index.d.ts CHANGED
@@ -2,5 +2,6 @@ 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 { init as initTsPlugin, default as tsPlugin } from "./plugin.js";
5
6
  export declare function createCliProgram(): Command;
6
7
  export declare function runCli(args?: string[]): Promise<void>;
package/dist/index.js CHANGED
@@ -3,16 +3,16 @@ import { devicesCommand } from "./commands/devices.js";
3
3
  import { doctorCommand } from "./commands/doctor.js";
4
4
  import { initCommand } from "./commands/init.js";
5
5
  import { runCommand } from "./commands/run.js";
6
- import { watchCommand } from "./commands/watch.js";
7
6
  export * from "./config/schema.js";
8
7
  export * from "./config/loader.js";
9
8
  export * from "./types/generator.js";
9
+ export { init as initTsPlugin, default as tsPlugin } from "./plugin.js";
10
10
  export function createCliProgram() {
11
11
  const program = new Command();
12
12
  program
13
13
  .name("spectra")
14
14
  .description("TestSpectra Zero-Config Cross-Platform Test Runner CLI")
15
- .version("2.0.0");
15
+ .version("1.0.0");
16
16
  program
17
17
  .command("init")
18
18
  .description("Initialize testspectra.config.json and project directories")
@@ -38,11 +38,6 @@ export function createCliProgram() {
38
38
  .option("-w, --workdir <dir>", "Custom app data / temp workdir")
39
39
  .option("-o, --output <path>", "Custom test report JSON output path")
40
40
  .action(runCommand);
41
- program
42
- .command("watch")
43
- .description("Watch page-objects, actions, steps, fixtures and auto-regenerate ambient types")
44
- .option("--once", "Generate ambient types once and exit")
45
- .action(watchCommand);
46
41
  return program;
47
42
  }
48
43
  export async function runCli(args = process.argv) {
@@ -0,0 +1,15 @@
1
+ import type ts from "typescript";
2
+ /**
3
+ * TypeScript Language Service Plugin Factory for TestSpectra.
4
+ * Runs silently inside TSServer (VS Code, Cursor, WebStorm, Neovim, etc.).
5
+ *
6
+ * It generates and refreshes `.testspectra/types/` ambient declarations
7
+ * automatically in the background whenever the project is opened or files change.
8
+ */
9
+ declare function init(modules: {
10
+ typescript: typeof ts;
11
+ }): {
12
+ create: (info: ts.server.PluginCreateInfo) => ts.LanguageService;
13
+ };
14
+ export default init;
15
+ export { init };
package/dist/plugin.js ADDED
@@ -0,0 +1,65 @@
1
+ import { TypeGenerator } from "./types/generator.js";
2
+ /**
3
+ * TypeScript Language Service Plugin Factory for TestSpectra.
4
+ * Runs silently inside TSServer (VS Code, Cursor, WebStorm, Neovim, etc.).
5
+ *
6
+ * It generates and refreshes `.testspectra/types/` ambient declarations
7
+ * automatically in the background whenever the project is opened or files change.
8
+ */
9
+ function init(modules) {
10
+ const tsModule = modules.typescript;
11
+ function create(info) {
12
+ const project = info.project;
13
+ const projectDir = project.getCurrentDirectory();
14
+ // 1. Initial Generation on project load
15
+ try {
16
+ TypeGenerator.writeDeclarationFiles(projectDir);
17
+ info.project.projectService.logger.info(`[TestSpectra TS Plugin] Initialized ambient declarations in ${projectDir}`);
18
+ }
19
+ catch (err) {
20
+ info.project.projectService.logger.info(`[TestSpectra TS Plugin] Failed initial type generation: ${err?.message}`);
21
+ }
22
+ // 2. Watch for entity file changes inside TSServer
23
+ const watchedFolders = ["page-objects", "pageobjects", "actions", "steps", "fixtures", "hooks"];
24
+ // Debounce type regeneration
25
+ let debounceTimer = null;
26
+ const triggerRegeneration = (pathChanged) => {
27
+ if (debounceTimer)
28
+ clearTimeout(debounceTimer);
29
+ debounceTimer = setTimeout(() => {
30
+ try {
31
+ TypeGenerator.writeDeclarationFiles(projectDir);
32
+ info.project.projectService.logger.info(`[TestSpectra TS Plugin] Regenerated ambient declarations after change in ${pathChanged}`);
33
+ }
34
+ catch (err) {
35
+ info.project.projectService.logger.info(`[TestSpectra TS Plugin] Error during regeneration: ${err?.message}`);
36
+ }
37
+ }, 200);
38
+ };
39
+ // 3. Proxy language service methods to detect file modifications / completions
40
+ const proxy = Object.create(null);
41
+ for (const k of Object.keys(info.languageService)) {
42
+ const x = info.languageService[k];
43
+ // @ts-ignore
44
+ proxy[k] = (...args) => x.apply(info.languageService, args);
45
+ }
46
+ // Intercept getCompletionsAtPosition to ensure fresh types
47
+ const originalGetCompletions = info.languageService.getCompletionsAtPosition;
48
+ proxy.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
49
+ const isEntity = watchedFolders.some((f) => fileName.includes(f));
50
+ if (isEntity) {
51
+ triggerRegeneration(fileName);
52
+ }
53
+ return originalGetCompletions.apply(info.languageService, [
54
+ fileName,
55
+ position,
56
+ options,
57
+ formattingSettings,
58
+ ]);
59
+ };
60
+ return proxy;
61
+ }
62
+ return { create };
63
+ }
64
+ export default init;
65
+ export { init };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -103,7 +103,6 @@ export default defineConfig({
103
103
  private: true,
104
104
  type: "module",
105
105
  scripts: {
106
- dev: "spectra watch",
107
106
  test: "spectra run",
108
107
  "type-check": "tsc -b",
109
108
  },
@@ -123,7 +122,6 @@ export default defineConfig({
123
122
  ...existing,
124
123
  scripts: {
125
124
  ...(existing.scripts || {}),
126
- dev: existing.scripts?.dev || "spectra watch",
127
125
  test: existing.scripts?.test || "spectra run",
128
126
  "type-check": existing.scripts?.["type-check"] || "tsc -b",
129
127
  },
@@ -150,13 +148,16 @@ export default defineConfig({
150
148
  console.log(`\x1b[32m[TestSpectra]\x1b[0m Created .gitignore`);
151
149
  }
152
150
 
153
- // 4. Create platform tsconfigs with Solution-Style Project References
151
+ // 4. Create platform tsconfigs with Solution-Style Project References & TS Plugin
154
152
  const tsconfigRoot = {
155
153
  compilerOptions: {
156
154
  target: "ES2022",
157
155
  module: "NodeNext",
158
156
  moduleResolution: "NodeNext",
159
157
  types: ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
158
+ plugins: [
159
+ { name: "@testspectra/cli" }
160
+ ],
160
161
  skipLibCheck: true,
161
162
  strict: true,
162
163
  noEmit: true,
package/src/index.ts CHANGED
@@ -3,11 +3,11 @@ import { devicesCommand } from "./commands/devices.js";
3
3
  import { doctorCommand } from "./commands/doctor.js";
4
4
  import { initCommand } from "./commands/init.js";
5
5
  import { runCommand } from "./commands/run.js";
6
- import { watchCommand } from "./commands/watch.js";
7
6
 
8
7
  export * from "./config/schema.js";
9
8
  export * from "./config/loader.js";
10
9
  export * from "./types/generator.js";
10
+ export { init as initTsPlugin, default as tsPlugin } from "./plugin.js";
11
11
 
12
12
  export function createCliProgram() {
13
13
  const program = new Command();
@@ -15,7 +15,7 @@ export function createCliProgram() {
15
15
  program
16
16
  .name("spectra")
17
17
  .description("TestSpectra Zero-Config Cross-Platform Test Runner CLI")
18
- .version("2.0.0");
18
+ .version("1.0.0");
19
19
 
20
20
  program
21
21
  .command("init")
@@ -46,12 +46,6 @@ export function createCliProgram() {
46
46
  .option("-o, --output <path>", "Custom test report JSON output path")
47
47
  .action(runCommand);
48
48
 
49
- program
50
- .command("watch")
51
- .description("Watch page-objects, actions, steps, fixtures and auto-regenerate ambient types")
52
- .option("--once", "Generate ambient types once and exit")
53
- .action(watchCommand);
54
-
55
49
  return program;
56
50
  }
57
51
 
package/src/plugin.ts ADDED
@@ -0,0 +1,81 @@
1
+ import type ts from "typescript";
2
+ import { TypeGenerator } from "./types/generator.js";
3
+
4
+ /**
5
+ * TypeScript Language Service Plugin Factory for TestSpectra.
6
+ * Runs silently inside TSServer (VS Code, Cursor, WebStorm, Neovim, etc.).
7
+ *
8
+ * It generates and refreshes `.testspectra/types/` ambient declarations
9
+ * automatically in the background whenever the project is opened or files change.
10
+ */
11
+ function init(modules: { typescript: typeof ts }) {
12
+ const tsModule = modules.typescript;
13
+
14
+ function create(info: ts.server.PluginCreateInfo) {
15
+ const project = info.project;
16
+ const projectDir = project.getCurrentDirectory();
17
+
18
+ // 1. Initial Generation on project load
19
+ try {
20
+ TypeGenerator.writeDeclarationFiles(projectDir);
21
+ info.project.projectService.logger.info(
22
+ `[TestSpectra TS Plugin] Initialized ambient declarations in ${projectDir}`
23
+ );
24
+ } catch (err: any) {
25
+ info.project.projectService.logger.info(
26
+ `[TestSpectra TS Plugin] Failed initial type generation: ${err?.message}`
27
+ );
28
+ }
29
+
30
+ // 2. Watch for entity file changes inside TSServer
31
+ const watchedFolders = ["page-objects", "pageobjects", "actions", "steps", "fixtures", "hooks"];
32
+
33
+ // Debounce type regeneration
34
+ let debounceTimer: NodeJS.Timeout | null = null;
35
+ const triggerRegeneration = (pathChanged: string) => {
36
+ if (debounceTimer) clearTimeout(debounceTimer);
37
+ debounceTimer = setTimeout(() => {
38
+ try {
39
+ TypeGenerator.writeDeclarationFiles(projectDir);
40
+ info.project.projectService.logger.info(
41
+ `[TestSpectra TS Plugin] Regenerated ambient declarations after change in ${pathChanged}`
42
+ );
43
+ } catch (err: any) {
44
+ info.project.projectService.logger.info(
45
+ `[TestSpectra TS Plugin] Error during regeneration: ${err?.message}`
46
+ );
47
+ }
48
+ }, 200);
49
+ };
50
+
51
+ // 3. Proxy language service methods to detect file modifications / completions
52
+ const proxy: ts.LanguageService = Object.create(null);
53
+ for (const k of Object.keys(info.languageService) as Array<keyof ts.LanguageService>) {
54
+ const x = info.languageService[k]!;
55
+ // @ts-ignore
56
+ proxy[k] = (...args: Array<{}>) => x.apply(info.languageService, args);
57
+ }
58
+
59
+ // Intercept getCompletionsAtPosition to ensure fresh types
60
+ const originalGetCompletions = info.languageService.getCompletionsAtPosition;
61
+ proxy.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
62
+ const isEntity = watchedFolders.some((f) => fileName.includes(f));
63
+ if (isEntity) {
64
+ triggerRegeneration(fileName);
65
+ }
66
+ return originalGetCompletions.apply(info.languageService, [
67
+ fileName,
68
+ position,
69
+ options,
70
+ formattingSettings,
71
+ ]);
72
+ };
73
+
74
+ return proxy;
75
+ }
76
+
77
+ return { create };
78
+ }
79
+
80
+ export default init;
81
+ export { init };
Binary file
Binary file
@@ -1,34 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { TypeGenerator } from "../types/generator.js";
4
-
5
- export function watchCommand(options: { once?: boolean } = {}) {
6
- const cwd = process.cwd();
7
-
8
- // Run initial generation
9
- TypeGenerator.writeDeclarationFiles(cwd);
10
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Generated ambient types in .testspectra/types/`);
11
-
12
- if (options.once) {
13
- return;
14
- }
15
-
16
- console.log(`\x1b[34m[TestSpectra]\x1b[0m Watching for changes in page-objects/, actions/, steps/, fixtures/...`);
17
-
18
- const watchDirs = ["page-objects", "pageobjects", "actions", "steps", "fixtures"];
19
- let debounceTimeout: NodeJS.Timeout | null = null;
20
-
21
- for (const dir of watchDirs) {
22
- const fullPath = path.join(cwd, dir);
23
- if (fs.existsSync(fullPath)) {
24
- fs.watch(fullPath, { recursive: true }, (_eventType, filename) => {
25
- if (!filename || filename.startsWith(".")) return;
26
- if (debounceTimeout) clearTimeout(debounceTimeout);
27
- debounceTimeout = setTimeout(() => {
28
- TypeGenerator.writeDeclarationFiles(cwd);
29
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Updated ambient declarations (.testspectra/types/) due to ${filename}`);
30
- }, 150);
31
- });
32
- }
33
- }
34
- }