@testspectra/cli 1.0.43 → 1.0.45
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/dist/plugin.js +84 -17
- package/dist/types/generator.js +25 -34
- package/package.json +2 -2
- package/templates/default/package.json +1 -1
- package/templates/default/tsconfig.spectra.android.json +4 -1
- package/templates/default/tsconfig.spectra.ios.json +4 -1
- package/templates/default/tsconfig.spectra.web.json +4 -1
- package/templates/nx/package.json +1 -1
- package/templates/nx/tsconfig.spectra.android.json +4 -1
- package/templates/nx/tsconfig.spectra.ios.json +4 -1
- package/templates/nx/tsconfig.spectra.shared.json +4 -1
- package/templates/nx/tsconfig.spectra.web.json +4 -1
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
1
3
|
import { TypeGenerator } from "./types/generator.js";
|
|
4
|
+
/**
|
|
5
|
+
* Traverses upwards from `startDir` to locate the TestSpectra workspace root.
|
|
6
|
+
* Looks for indicators: `spectra.config.ts`, `.testspectra`, `pnpm-workspace.yaml`, or `nx.json`.
|
|
7
|
+
*/
|
|
8
|
+
function findWorkspaceRoot(startDir) {
|
|
9
|
+
let cur = path.resolve(startDir);
|
|
10
|
+
while (cur !== path.dirname(cur)) {
|
|
11
|
+
if (fs.existsSync(path.join(cur, "spectra.config.ts")) ||
|
|
12
|
+
fs.existsSync(path.join(cur, ".testspectra")) ||
|
|
13
|
+
fs.existsSync(path.join(cur, "pnpm-workspace.yaml")) ||
|
|
14
|
+
fs.existsSync(path.join(cur, "nx.json"))) {
|
|
15
|
+
return cur;
|
|
16
|
+
}
|
|
17
|
+
cur = path.dirname(cur);
|
|
18
|
+
}
|
|
19
|
+
return startDir;
|
|
20
|
+
}
|
|
2
21
|
/**
|
|
3
22
|
* TypeScript Language Service Plugin Factory for TestSpectra.
|
|
4
23
|
* Runs silently inside TSServer (VS Code, Cursor, WebStorm, Neovim, etc.).
|
|
@@ -11,31 +30,74 @@ function init(modules) {
|
|
|
11
30
|
function create(info) {
|
|
12
31
|
const project = info.project;
|
|
13
32
|
const projectDir = project.getCurrentDirectory();
|
|
33
|
+
const workspaceRoot = findWorkspaceRoot(projectDir);
|
|
34
|
+
const log = (msg) => {
|
|
35
|
+
try {
|
|
36
|
+
info.project.projectService.logger.info(`[TestSpectra TS Plugin] ${msg}`);
|
|
37
|
+
}
|
|
38
|
+
catch { }
|
|
39
|
+
};
|
|
14
40
|
// 1. Initial Generation on project load
|
|
15
41
|
try {
|
|
16
|
-
TypeGenerator.writeDeclarationFiles(
|
|
17
|
-
|
|
42
|
+
TypeGenerator.writeDeclarationFiles(workspaceRoot);
|
|
43
|
+
log(`Initialized ambient declarations in workspace root: ${workspaceRoot}`);
|
|
18
44
|
}
|
|
19
45
|
catch (err) {
|
|
20
|
-
|
|
46
|
+
log(`Failed initial type generation: ${err?.message}`);
|
|
21
47
|
}
|
|
22
|
-
//
|
|
23
|
-
const watchedFolders = ["page-objects", "pageobjects", "actions", "steps", "fixtures", "hooks"];
|
|
24
|
-
// Debounce type regeneration
|
|
48
|
+
// Debounced regeneration handler
|
|
25
49
|
let debounceTimer = null;
|
|
26
|
-
const triggerRegeneration = (
|
|
27
|
-
|
|
28
|
-
clearTimeout(debounceTimer);
|
|
29
|
-
debounceTimer = setTimeout(() => {
|
|
50
|
+
const triggerRegeneration = (reason, sync = false) => {
|
|
51
|
+
const doRegen = () => {
|
|
30
52
|
try {
|
|
31
|
-
TypeGenerator.writeDeclarationFiles(
|
|
32
|
-
|
|
53
|
+
TypeGenerator.writeDeclarationFiles(workspaceRoot);
|
|
54
|
+
log(`Regenerated ambient declarations (${reason})`);
|
|
33
55
|
}
|
|
34
56
|
catch (err) {
|
|
35
|
-
|
|
57
|
+
log(`Error during regeneration: ${err?.message}`);
|
|
36
58
|
}
|
|
37
|
-
}
|
|
59
|
+
};
|
|
60
|
+
if (sync) {
|
|
61
|
+
doRegen();
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
if (debounceTimer)
|
|
65
|
+
clearTimeout(debounceTimer);
|
|
66
|
+
debounceTimer = setTimeout(doRegen, 80);
|
|
67
|
+
}
|
|
38
68
|
};
|
|
69
|
+
// 2. Active File System Watcher on the entire workspace
|
|
70
|
+
try {
|
|
71
|
+
if (fs.existsSync(workspaceRoot)) {
|
|
72
|
+
const watcher = fs.watch(workspaceRoot, { recursive: true }, (eventType, filename) => {
|
|
73
|
+
if (!filename)
|
|
74
|
+
return;
|
|
75
|
+
const fn = filename.toString();
|
|
76
|
+
// Skip internal and build directories
|
|
77
|
+
if (fn.includes(".testspectra") ||
|
|
78
|
+
fn.includes("node_modules") ||
|
|
79
|
+
fn.includes(".git") ||
|
|
80
|
+
fn.includes("dist") ||
|
|
81
|
+
fn.includes(".nx")) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Match relevant entity files
|
|
85
|
+
if (fn.includes("fixtures") ||
|
|
86
|
+
fn.includes("page-objects") ||
|
|
87
|
+
fn.includes("pageobjects") ||
|
|
88
|
+
fn.includes("actions") ||
|
|
89
|
+
fn.includes("steps") ||
|
|
90
|
+
fn.includes("hooks") ||
|
|
91
|
+
fn.includes("spectra.config")) {
|
|
92
|
+
triggerRegeneration(`fs.watch '${eventType}' on ${fn}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
watcher.on("error", () => { });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
log(`Note: Recursive fs.watch not supported or failed: ${err?.message}`);
|
|
100
|
+
}
|
|
39
101
|
// 3. Proxy language service methods to detect file modifications / completions
|
|
40
102
|
const proxy = Object.create(null);
|
|
41
103
|
for (const k of Object.keys(info.languageService)) {
|
|
@@ -46,9 +108,9 @@ function init(modules) {
|
|
|
46
108
|
// Intercept getCompletionsAtPosition to ensure fresh types
|
|
47
109
|
const originalGetCompletions = info.languageService.getCompletionsAtPosition;
|
|
48
110
|
proxy.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
51
|
-
triggerRegeneration(fileName);
|
|
111
|
+
const ext = path.extname(fileName);
|
|
112
|
+
if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".mjs") {
|
|
113
|
+
triggerRegeneration(`completions at ${path.basename(fileName)}`, false);
|
|
52
114
|
}
|
|
53
115
|
return originalGetCompletions.apply(info.languageService, [
|
|
54
116
|
fileName,
|
|
@@ -57,6 +119,11 @@ function init(modules) {
|
|
|
57
119
|
formattingSettings,
|
|
58
120
|
]);
|
|
59
121
|
};
|
|
122
|
+
// Intercept getQuickInfoAtPosition for fresh hover info
|
|
123
|
+
const originalGetQuickInfo = info.languageService.getQuickInfoAtPosition;
|
|
124
|
+
proxy.getQuickInfoAtPosition = (fileName, position) => {
|
|
125
|
+
return originalGetQuickInfo.apply(info.languageService, [fileName, position]);
|
|
126
|
+
};
|
|
60
127
|
return proxy;
|
|
61
128
|
}
|
|
62
129
|
return { create };
|
package/dist/types/generator.js
CHANGED
|
@@ -191,26 +191,6 @@ export class TypeGenerator {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
// 2. Built-in Spectra Static Methods & Custom Actions
|
|
194
|
-
content += `\ninterface SpectraStaticInstance {\n`;
|
|
195
|
-
content += ` get(target: any): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
196
|
-
content += ` getAll(selector: string): import('@testspectra/matchers').MultiElementRunner;\n`;
|
|
197
|
-
content += ` navigate(url: string): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
198
|
-
content += ` back(): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
199
|
-
content += ` refresh(): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
200
|
-
content += ` click(target: any, textOrOptions?: string | import('@testspectra/matchers').ClickOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
201
|
-
content += ` doubleClick(target: any, textOrOptions?: string | import('@testspectra/matchers').ClickOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
202
|
-
content += ` longPress(target: any, options?: import('@testspectra/matchers').LongPressOptions | number): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
203
|
-
content += ` type(target: any, value: string, options?: import('@testspectra/matchers').TypeOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
204
|
-
content += ` clear(target: any): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
205
|
-
content += ` select(target: any, value: string): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
206
|
-
content += ` hover(target: any): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
207
|
-
content += ` pressKey(key: import('@testspectra/matchers').KeyOption | string): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
208
|
-
content += ` dragDrop(sourceTarget: any, destTarget: any): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
209
|
-
content += ` scroll(options?: import('@testspectra/matchers').ScrollOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
210
|
-
content += ` swipe(options: import('@testspectra/matchers').SwipeOptions): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
211
|
-
content += ` wait(durationMs: number): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
212
|
-
content += ` waitForElement(target: any, timeoutMs?: number): import('@testspectra/matchers').SingleElementRunner;\n`;
|
|
213
|
-
content += `}\n\n`;
|
|
214
194
|
content += `interface TestSpectraCustomActions {\n`;
|
|
215
195
|
const declaredActions = new Set();
|
|
216
196
|
for (const actionsDir of actionsDirs) {
|
|
@@ -262,7 +242,7 @@ export class TypeGenerator {
|
|
|
262
242
|
}
|
|
263
243
|
}
|
|
264
244
|
content += `}\n`;
|
|
265
|
-
content += `type TestSpectraActions =
|
|
245
|
+
content += `type TestSpectraActions = import('@testspectra/matchers').SpectraStatic & TestSpectraCustomActions;\n`;
|
|
266
246
|
content += `declare const Spectra: TestSpectraActions;\n\n`;
|
|
267
247
|
// 3. Built-in Steps Declaration
|
|
268
248
|
content += `interface TestSpectraSteps {\n`;
|
|
@@ -327,20 +307,31 @@ export class TypeGenerator {
|
|
|
327
307
|
content += `// Managed automatically by TestSpectra Language Service Plugin.\n\n`;
|
|
328
308
|
content += `interface TestSpectraFixtures {\n`;
|
|
329
309
|
const declaredFixtures = new Set();
|
|
330
|
-
|
|
331
|
-
if (!fs.existsSync(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
310
|
+
function scanFixtureDir(dir) {
|
|
311
|
+
if (!fs.existsSync(dir))
|
|
312
|
+
return;
|
|
313
|
+
try {
|
|
314
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
315
|
+
for (const entry of entries) {
|
|
316
|
+
if (entry.name.startsWith("."))
|
|
317
|
+
continue;
|
|
318
|
+
if (entry.isDirectory()) {
|
|
319
|
+
scanFixtureDir(path.join(dir, entry.name));
|
|
320
|
+
}
|
|
321
|
+
else if (entry.isFile()) {
|
|
322
|
+
const parsed = path.parse(entry.name);
|
|
323
|
+
const varName = parsed.name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
324
|
+
if (declaredFixtures.has(varName))
|
|
325
|
+
continue;
|
|
326
|
+
content += ` readonly ${varName}: string;\n`;
|
|
327
|
+
declaredFixtures.add(varName);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
343
330
|
}
|
|
331
|
+
catch { }
|
|
332
|
+
}
|
|
333
|
+
for (const fixturesDir of fixturesDirs) {
|
|
334
|
+
scanFixtureDir(fixturesDir);
|
|
344
335
|
}
|
|
345
336
|
content += `}\n`;
|
|
346
337
|
content += `declare const Fixture: TestSpectraFixtures;\n`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testspectra/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.45",
|
|
4
4
|
"description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@clack/prompts": "^1.7.0",
|
|
24
|
-
"@testspectra/matchers": "^1.0.
|
|
24
|
+
"@testspectra/matchers": "^1.0.45",
|
|
25
25
|
"chalk": "^5.3.0",
|
|
26
26
|
"commander": "^12.1.0",
|
|
27
27
|
"dotenv": "^16.4.5",
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"strict": true,
|
|
10
|
-
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
|
10
|
+
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{ "name": "@testspectra/cli" }
|
|
13
|
+
]
|
|
11
14
|
},
|
|
12
15
|
"include": [
|
|
13
16
|
".testspectra/types/android.d.ts",
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"strict": true,
|
|
10
|
-
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
|
10
|
+
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{ "name": "@testspectra/cli" }
|
|
13
|
+
]
|
|
11
14
|
},
|
|
12
15
|
"include": [
|
|
13
16
|
".testspectra/types/ios.d.ts",
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"strict": true,
|
|
10
|
-
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
|
10
|
+
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{ "name": "@testspectra/cli" }
|
|
13
|
+
]
|
|
11
14
|
},
|
|
12
15
|
"include": [
|
|
13
16
|
".testspectra/types/web.d.ts",
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"strict": true,
|
|
10
|
-
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
|
10
|
+
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{ "name": "@testspectra/cli" }
|
|
13
|
+
]
|
|
11
14
|
},
|
|
12
15
|
"include": [
|
|
13
16
|
".testspectra/types/android.d.ts",
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"strict": true,
|
|
10
|
-
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
|
10
|
+
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{ "name": "@testspectra/cli" }
|
|
13
|
+
]
|
|
11
14
|
},
|
|
12
15
|
"include": [
|
|
13
16
|
".testspectra/types/ios.d.ts",
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"strict": true,
|
|
10
|
-
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
|
10
|
+
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{ "name": "@testspectra/cli" }
|
|
13
|
+
]
|
|
11
14
|
},
|
|
12
15
|
"include": [
|
|
13
16
|
".testspectra/types/shared/common.d.ts",
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
"noEmit": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"strict": true,
|
|
10
|
-
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
|
10
|
+
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"],
|
|
11
|
+
"plugins": [
|
|
12
|
+
{ "name": "@testspectra/cli" }
|
|
13
|
+
]
|
|
11
14
|
},
|
|
12
15
|
"include": [
|
|
13
16
|
".testspectra/types/web.d.ts",
|