@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 +10 -4
- package/dist/commands/init.js +4 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -7
- package/dist/plugin.d.ts +15 -0
- package/dist/plugin.js +65 -0
- package/package.json +1 -1
- package/src/commands/init.ts +4 -3
- package/src/index.ts +2 -8
- package/src/plugin.ts +81 -0
- package/testspectra-cli-1.0.4.tgz +0 -0
- package/testspectra-cli-1.0.5.tgz +0 -0
- package/src/commands/watch.ts +0 -34
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
|
|
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
|
|
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
|
|
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
|
|
package/dist/commands/init.js
CHANGED
|
@@ -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("
|
|
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) {
|
package/dist/plugin.d.ts
ADDED
|
@@ -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
package/src/commands/init.ts
CHANGED
|
@@ -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("
|
|
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
|
package/src/commands/watch.ts
DELETED
|
@@ -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
|
-
}
|