@testrigor/testrigor-cli 0.0.51-beta → 1.0.0
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.
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CommandBase } from "../../command-base";
|
|
2
|
+
export default class ValidateSyntax extends CommandBase<typeof ValidateSyntax> {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
private TESTRIGOR_API2_URL;
|
|
6
|
+
static flags: {
|
|
7
|
+
steps: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
"test-cases-path": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
|
+
"base-url-api": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
+
"rules-path": import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
|
+
"explicit-mutations": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
static args: {
|
|
14
|
+
testSuiteId: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
private resolveStepsToValidate;
|
|
18
|
+
private reportValidationResults;
|
|
19
|
+
private callValidateSyntax;
|
|
20
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const node_fetch_commonjs_1 = require("node-fetch-commonjs");
|
|
5
|
+
const command_base_1 = require("../../command-base");
|
|
6
|
+
const environments_1 = require("../../config/environments");
|
|
7
|
+
const parser_1 = require("../../util/parser");
|
|
8
|
+
const core_2 = require("@oclif/core");
|
|
9
|
+
class ValidateSyntax extends command_base_1.CommandBase {
|
|
10
|
+
async run() {
|
|
11
|
+
const flags = this.flags;
|
|
12
|
+
const args = this.args;
|
|
13
|
+
const baseUrlApi = flags["base-url-api"] || environments_1.environments.prod.TESTRIGOR_API_URL;
|
|
14
|
+
this.TESTRIGOR_API2_URL = `${baseUrlApi.replace(/\/$/, "")}/api/v1`;
|
|
15
|
+
if (!flags.steps && !flags["test-cases-path"]) {
|
|
16
|
+
this.error("Provide either --steps or --test-cases-path");
|
|
17
|
+
}
|
|
18
|
+
const token = await this.resolveToken();
|
|
19
|
+
const testSuiteId = await this.resolveTestSuiteId(args.testSuiteId);
|
|
20
|
+
const casesToValidate = await this.resolveStepsToValidate(flags.steps, flags["test-cases-path"]);
|
|
21
|
+
if (casesToValidate.length === 0) {
|
|
22
|
+
this.error("No test case files matched --test-cases-path");
|
|
23
|
+
}
|
|
24
|
+
const rules = flags["rules-path"]
|
|
25
|
+
? await (0, parser_1.parseRules)(flags["rules-path"])
|
|
26
|
+
: [];
|
|
27
|
+
const explicitMutations = flags["explicit-mutations"];
|
|
28
|
+
await this.reportValidationResults(testSuiteId, token, casesToValidate, rules, explicitMutations);
|
|
29
|
+
}
|
|
30
|
+
async resolveStepsToValidate(steps, testCasesPath) {
|
|
31
|
+
if (steps) {
|
|
32
|
+
return [{ description: "provided steps", customSteps: steps }];
|
|
33
|
+
}
|
|
34
|
+
const testCases = await (0, parser_1.parseTestCases)(testCasesPath);
|
|
35
|
+
return testCases.map((testCase) => ({
|
|
36
|
+
description: testCase.description,
|
|
37
|
+
customSteps: testCase.customSteps,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
async reportValidationResults(testSuiteId, token, cases, rules, explicitMutations) {
|
|
41
|
+
let hasAnyInvalidTestCases = false;
|
|
42
|
+
for (const testCase of cases) {
|
|
43
|
+
const response = await this.callValidateSyntax(testSuiteId, testCase.customSteps, token, rules, explicitMutations);
|
|
44
|
+
if (response.data && response.data.length > 0) {
|
|
45
|
+
const parserErrors = response.data;
|
|
46
|
+
core_2.ux.info(`${testCase.description}: invalid`);
|
|
47
|
+
for (const parserError of parserErrors) {
|
|
48
|
+
core_2.ux.info(` ${parserError.command} - ${parserError.errorMessage}`);
|
|
49
|
+
}
|
|
50
|
+
hasAnyInvalidTestCases = true;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
core_2.ux.info(`${testCase.description}: valid`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (hasAnyInvalidTestCases) {
|
|
57
|
+
this.exit(1);
|
|
58
|
+
}
|
|
59
|
+
core_2.ux.info("All provided steps are valid.");
|
|
60
|
+
this.exit(0);
|
|
61
|
+
}
|
|
62
|
+
async callValidateSyntax(testSuiteId, steps, token, rules, explicitMutations) {
|
|
63
|
+
const body = { steps };
|
|
64
|
+
if (rules.length > 0) {
|
|
65
|
+
body.rules = rules;
|
|
66
|
+
}
|
|
67
|
+
if (explicitMutations) {
|
|
68
|
+
body.explicitMutations = explicitMutations;
|
|
69
|
+
}
|
|
70
|
+
const response = await (0, node_fetch_commonjs_1.default)(`${this.TESTRIGOR_API2_URL}/apps/${testSuiteId}/validate-syntax`, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
body: JSON.stringify(body),
|
|
73
|
+
headers: {
|
|
74
|
+
"Content-Type": "application/json",
|
|
75
|
+
Accept: "application/json",
|
|
76
|
+
"auth-token": token,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`Failed to validate syntax: ${response.status} ${response.statusText}`);
|
|
81
|
+
}
|
|
82
|
+
return (await response.json());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.default = ValidateSyntax;
|
|
86
|
+
ValidateSyntax.description = "Validate step syntax against a test suite's reusable rules, without running the steps";
|
|
87
|
+
ValidateSyntax.examples = [
|
|
88
|
+
'testrigor test-suite validate-syntax <test-suite-id> --steps "click \\"Login\\""',
|
|
89
|
+
'testrigor test-suite validate-syntax <test-suite-id> --test-cases-path "test_cases/**/*.{yaml,txt}"',
|
|
90
|
+
];
|
|
91
|
+
ValidateSyntax.flags = {
|
|
92
|
+
steps: core_1.Flags.string({
|
|
93
|
+
description: "Raw step text to validate directly",
|
|
94
|
+
exclusive: ["test-cases-path"],
|
|
95
|
+
}),
|
|
96
|
+
"test-cases-path": core_1.Flags.string({
|
|
97
|
+
description: "Test case files to validate (same glob format as `test-suite run --test-cases-path`)",
|
|
98
|
+
exclusive: ["steps"],
|
|
99
|
+
}),
|
|
100
|
+
"base-url-api": core_1.Flags.string({
|
|
101
|
+
description: "Base URL for API2 (e.g., https://api2.testrigor.com)",
|
|
102
|
+
}),
|
|
103
|
+
"rules-path": core_1.Flags.string({
|
|
104
|
+
description: "Ad hoc reusable rules to validate against, for rules not yet saved to this suite (same glob format as `test-suite run --rules-path`)",
|
|
105
|
+
}),
|
|
106
|
+
"explicit-mutations": core_1.Flags.boolean({
|
|
107
|
+
description: "Validate using only the supplied rules instead of merging them with the suite's saved rules",
|
|
108
|
+
default: false,
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
ValidateSyntax.args = {
|
|
112
|
+
testSuiteId: core_1.Args.string({
|
|
113
|
+
name: "testSuiteId",
|
|
114
|
+
description: "The test suite id (optional if set in config)",
|
|
115
|
+
required: false,
|
|
116
|
+
}),
|
|
117
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -51,6 +51,13 @@ export type Rule = {
|
|
|
51
51
|
name: string;
|
|
52
52
|
labels: string[];
|
|
53
53
|
};
|
|
54
|
+
export type ParserError = {
|
|
55
|
+
command?: string;
|
|
56
|
+
errorMessage?: string;
|
|
57
|
+
};
|
|
58
|
+
export type ValidateSyntaxApiResponse = Api2Wrapper & {
|
|
59
|
+
data?: ParserError[];
|
|
60
|
+
};
|
|
54
61
|
export type ChromeExtensionInput = {
|
|
55
62
|
name: string;
|
|
56
63
|
file: string;
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0
|
|
2
|
+
"version": "1.0.0",
|
|
3
3
|
"commands": {
|
|
4
4
|
"authenticate": {
|
|
5
5
|
"id": "authenticate",
|
|
@@ -275,6 +275,103 @@
|
|
|
275
275
|
"required": false
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
|
+
},
|
|
279
|
+
"test-suite:validate-syntax": {
|
|
280
|
+
"id": "test-suite:validate-syntax",
|
|
281
|
+
"description": "Validate step syntax against a test suite's reusable rules, without running the steps",
|
|
282
|
+
"strict": true,
|
|
283
|
+
"pluginName": "@testrigor/testrigor-cli",
|
|
284
|
+
"pluginAlias": "@testrigor/testrigor-cli",
|
|
285
|
+
"pluginType": "core",
|
|
286
|
+
"aliases": [],
|
|
287
|
+
"hiddenAliases": [],
|
|
288
|
+
"examples": [
|
|
289
|
+
"testrigor test-suite validate-syntax <test-suite-id> --steps \"click \\\"Login\\\"\"",
|
|
290
|
+
"testrigor test-suite validate-syntax <test-suite-id> --test-cases-path \"test_cases/**/*.{yaml,txt}\""
|
|
291
|
+
],
|
|
292
|
+
"flags": {
|
|
293
|
+
"auth-token": {
|
|
294
|
+
"name": "auth-token",
|
|
295
|
+
"type": "option",
|
|
296
|
+
"description": "Personal authentication token",
|
|
297
|
+
"required": false,
|
|
298
|
+
"helpGroup": "GLOBAL",
|
|
299
|
+
"multiple": false
|
|
300
|
+
},
|
|
301
|
+
"token": {
|
|
302
|
+
"name": "token",
|
|
303
|
+
"type": "option",
|
|
304
|
+
"char": "t",
|
|
305
|
+
"description": "Suite authentication token",
|
|
306
|
+
"required": false,
|
|
307
|
+
"helpGroup": "GLOBAL",
|
|
308
|
+
"multiple": false
|
|
309
|
+
},
|
|
310
|
+
"min-port": {
|
|
311
|
+
"name": "min-port",
|
|
312
|
+
"type": "option",
|
|
313
|
+
"description": "Min port for tunnel",
|
|
314
|
+
"multiple": false,
|
|
315
|
+
"default": 40000
|
|
316
|
+
},
|
|
317
|
+
"max-port": {
|
|
318
|
+
"name": "max-port",
|
|
319
|
+
"type": "option",
|
|
320
|
+
"description": "Max port for tunnel",
|
|
321
|
+
"multiple": false,
|
|
322
|
+
"default": 50000
|
|
323
|
+
},
|
|
324
|
+
"verbose": {
|
|
325
|
+
"name": "verbose",
|
|
326
|
+
"type": "boolean",
|
|
327
|
+
"description": "Verbose output",
|
|
328
|
+
"helpGroup": "GLOBAL",
|
|
329
|
+
"allowNo": false
|
|
330
|
+
},
|
|
331
|
+
"steps": {
|
|
332
|
+
"name": "steps",
|
|
333
|
+
"type": "option",
|
|
334
|
+
"description": "Raw step text to validate directly",
|
|
335
|
+
"multiple": false,
|
|
336
|
+
"exclusive": [
|
|
337
|
+
"test-cases-path"
|
|
338
|
+
]
|
|
339
|
+
},
|
|
340
|
+
"test-cases-path": {
|
|
341
|
+
"name": "test-cases-path",
|
|
342
|
+
"type": "option",
|
|
343
|
+
"description": "Test case files to validate (same glob format as `test-suite run --test-cases-path`)",
|
|
344
|
+
"multiple": false,
|
|
345
|
+
"exclusive": [
|
|
346
|
+
"steps"
|
|
347
|
+
]
|
|
348
|
+
},
|
|
349
|
+
"base-url-api": {
|
|
350
|
+
"name": "base-url-api",
|
|
351
|
+
"type": "option",
|
|
352
|
+
"description": "Base URL for API2 (e.g., https://api2.testrigor.com)",
|
|
353
|
+
"multiple": false
|
|
354
|
+
},
|
|
355
|
+
"rules-path": {
|
|
356
|
+
"name": "rules-path",
|
|
357
|
+
"type": "option",
|
|
358
|
+
"description": "Ad hoc reusable rules to validate against, for rules not yet saved to this suite (same glob format as `test-suite run --rules-path`)",
|
|
359
|
+
"multiple": false
|
|
360
|
+
},
|
|
361
|
+
"explicit-mutations": {
|
|
362
|
+
"name": "explicit-mutations",
|
|
363
|
+
"type": "boolean",
|
|
364
|
+
"description": "Validate using only the supplied rules instead of merging them with the suite's saved rules",
|
|
365
|
+
"allowNo": false
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
"args": {
|
|
369
|
+
"testSuiteId": {
|
|
370
|
+
"name": "testSuiteId",
|
|
371
|
+
"description": "The test suite id (optional if set in config)",
|
|
372
|
+
"required": false
|
|
373
|
+
}
|
|
374
|
+
}
|
|
278
375
|
}
|
|
279
376
|
}
|
|
280
377
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testrigor/testrigor-cli",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "TestRigor Command Line",
|
|
5
5
|
"author": "Ivo Parun Rua @parunruaivo",
|
|
6
6
|
"bin": {
|
|
@@ -77,6 +77,9 @@
|
|
|
77
77
|
"test-suite:run": {
|
|
78
78
|
"description": "Run all tests of a Test Suite"
|
|
79
79
|
},
|
|
80
|
+
"test-suite:validate-syntax": {
|
|
81
|
+
"description": "Validate step syntax against a test suite's reusable rules, without running the steps"
|
|
82
|
+
},
|
|
80
83
|
"authenticate": {
|
|
81
84
|
"description": "Set your testRigor personal authentication token"
|
|
82
85
|
}
|