@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.
- package/dist/commands/init.js +58 -398
- package/dist/commands/run.js +18 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +8 -1
- package/templates/default/actions/applyDiscount/mobile.action.ts +6 -0
- package/templates/default/actions/applyDiscount/web.action.ts +6 -0
- package/templates/default/actions/verifyOtp/mobile.action.ts +6 -0
- package/templates/default/actions/verifyOtp/web.action.ts +6 -0
- package/templates/default/fixtures/couponCode.json +1 -0
- package/templates/default/fixtures/searchQuery.json +1 -0
- package/templates/default/fixtures/userData.json +1 -0
- package/templates/default/global-hooks/before.web.hook.ts +4 -0
- package/templates/default/package.json +18 -0
- package/templates/default/page-objects/LoginPage/mobile.ts +32 -0
- package/templates/default/page-objects/LoginPage/web.ts +32 -0
- package/templates/default/page-objects/ProductPage/mobile.ts +39 -0
- package/templates/default/page-objects/ProductPage/web.ts +38 -0
- package/templates/default/page-objects/SettingsPage/common.ts +26 -0
- package/templates/default/spectra.config.ts +61 -0
- package/templates/default/steps/loginUser/mobile.step.ts +4 -0
- package/templates/default/steps/loginUser/web.step.ts +4 -0
- package/templates/default/steps/searchProduct/mobile.step.ts +5 -0
- package/templates/default/steps/searchProduct/web.step.ts +5 -0
- package/templates/default/suites/Auth/TC-AUTH-01/android.test.ts +6 -0
- package/templates/default/suites/Auth/TC-AUTH-01/ios.test.ts +6 -0
- package/templates/default/suites/Auth/TC-AUTH-01/web.test.ts +13 -0
- package/templates/default/suites/Auth/TC-AUTH-02/android.test.ts +5 -0
- package/templates/default/suites/Auth/TC-AUTH-02/web.test.ts +5 -0
- package/templates/default/suites/Auth/hooks/before.android.hook.ts +3 -0
- package/templates/default/suites/Auth/hooks/before.ios.hook.ts +3 -0
- package/templates/default/suites/Auth/hooks/before.web.hook.ts +3 -0
- package/templates/default/suites/Product/TC-PROD-01/mobile.test.ts +8 -0
- package/templates/default/suites/Product/TC-PROD-01/web.test.ts +8 -0
- package/templates/default/suites/Product/TC-PROD-02/mobile.test.ts +6 -0
- package/templates/default/suites/Product/TC-PROD-02/web.test.ts +6 -0
- package/templates/default/suites/Product/hooks/before.web.hook.ts +3 -0
- package/templates/default/suites/Settings/TC-SET-01/common.test.ts +8 -0
- package/templates/default/tsconfig.android.json +39 -0
- package/templates/default/tsconfig.ios.json +39 -0
- package/templates/default/tsconfig.json +20 -0
- package/templates/default/tsconfig.web.json +45 -0
- package/CLI_IMPLEMENTATION_PLAN.md +0 -369
- package/src/commands/devices.ts +0 -41
- package/src/commands/doctor.ts +0 -57
- package/src/commands/init.ts +0 -470
- package/src/commands/run.ts +0 -102
- package/src/config/loader.ts +0 -82
- package/src/config/schema.ts +0 -489
- package/src/index.ts +0 -56
- package/src/plugin.ts +0 -81
- package/src/runner/bridge.ts +0 -146
- package/src/runner/reporter.ts +0 -64
- package/src/step/index.ts +0 -6
- package/src/step/matchers.ts +0 -131
- package/src/step/proto.ts +0 -163
- package/src/step/runner/collection.ts +0 -73
- package/src/step/runner/single.ts +0 -166
- package/src/step/spectra.ts +0 -185
- package/src/step/types.ts +0 -113
- package/src/types/generator.ts +0 -240
- package/src/types/webdriverio.d.ts +0 -46
- package/testspectra-cli-1.0.5.tgz +0 -0
- package/testspectra-cli-1.0.6.tgz +0 -0
- package/testspectra-cli-1.0.7.tgz +0 -0
- package/tsconfig.json +0 -17
package/dist/commands/init.js
CHANGED
|
@@ -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.
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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.
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
//
|
|
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
|
}
|
package/dist/commands/run.js
CHANGED
|
@@ -31,7 +31,24 @@ export async function runCommand(specPath, options = {}) {
|
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
|
-
// Scan
|
|
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 "
|
|
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 "
|
|
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.
|
|
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 @@
|
|
|
1
|
+
"DISCOUNT50"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"Laptop Gaming"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"username": "tomsmith", "role": "admin"}
|
|
@@ -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
|
+
}
|