Skip to content

Commit 17cb559

Browse files
committed
Implement CLI testing suite
1 parent c33f77b commit 17cb559

File tree

11 files changed

+163
-47
lines changed

11 files changed

+163
-47
lines changed

examples/cli/pwd.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
new CLITest("Print working directory (pwd)")
2+
.actual("pwd")
3+
.expected({
4+
stdout: process.cwd()
5+
});
6+
7+
new CLITest("pwd with an undefined argument")
8+
.actual("pwd", [ "-x" ])
9+
.expected({
10+
stderr: [
11+
"/bin/sh: line 0: pwd: -x: invalid option",
12+
"pwd: usage: pwd [-LP]"
13+
]
14+
});

packages/@cli/README.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,40 @@ npx rjs-test cli <tests-path>
1212

1313
> Integrated in [`rapidjs-org/testing`](https://github.com/rapidjs-org/testing).
1414
15+
## Configuration
16+
17+
In order to define common CLI aspects, they can be defined through the static `.configure()` method:
18+
19+
``` ts
20+
HTTPTest.configure(configuration: RequestOptions & {
21+
commonBinary?: string;
22+
});
23+
```
24+
1525
## Test Anatomy
1626

1727
### Expressions
1828

19-
(#### Actual)
29+
#### Actual
2030

2131
``` ts
22-
.actual(expression: any)
23-
.expected(expression: any)
32+
.actual(binary: string, arg?: string[])
33+
.actual(args: string[]) // imply binary if defined a common
2434
```
2535

26-
(#### Expected)
27-
28-
### Value-based Assertion
36+
#### Expected
2937

3038
``` ts
31-
new <Suite>Test("Example label")
32-
.actual(<expression>)
33-
.expected(<expression>);
39+
.expected(binary: string, arg?: string[])
40+
.expected(args: string[]) // imply binary if defined a common
3441
```
3542

36-
### Error-based Assertion
43+
### Value-based Assertion
3744

3845
``` ts
39-
new <Suite>Test("Example label")
46+
new CLITest("List files")
4047
.actual(<expression>)
41-
.error("<error-message>", <ErrorConstructor>);
48+
.expected(<expression>);
4249
```
4350

4451
## Comparison Strategy
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
process.chdir(__dirname);
2+
3+
4+
CLITest.configure({
5+
commonBinary: "pwd"
6+
});

packages/@cli/metatest/cli.test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// With common bin
2+
new CLITest("Print working directory (1)")
3+
.actual()
4+
.expected({
5+
stdout: __dirname
6+
});
7+
8+
// With explicit bin
9+
new CLITest("Print working directory (2)")
10+
.actual("pwd")
11+
.expected({
12+
stdout: __dirname
13+
});
14+
15+
// With args
16+
new CLITest("Echo 'foo' (1)")
17+
.actual("echo", [ "foo" ])
18+
.expected({
19+
stdout: "foo",
20+
stderr: null
21+
});
22+
23+
// Condensed
24+
new CLITest("Echo 'foo' (2)")
25+
.actual("echo foo")
26+
.expected("foo");
27+
28+
// stderr
29+
new CLITest("Print working directory; with undefined argument")
30+
.actual("pwd -x")
31+
.expected({
32+
stderr: [
33+
"/bin/sh: line 0: pwd: -x: invalid option",
34+
"pwd: usage: pwd [-LP]"
35+
]
36+
});

packages/@cli/package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@
2121
"debug": "npx tsc --project ./tsconfig.debug.json",
2222
"debug:watch": "npm run debug -- --watch",
2323
"build": "rm -rf ./build/ && rm -rf ./types/ && npx tsc --project ./tsconfig.build.json",
24-
"metatest:single-file": "../../bin.sh http ./metatest/http-1.test.js",
25-
"metatest": "../../bin.sh http ./metatest/",
26-
"test": "npm run build && npm run metatest && npm run metatest:single-file",
24+
"metatest": "../../bin.sh cli ./metatest/",
25+
"test": "npm run build && npm run metatest",
2726
"release:minor": "npm run test && npx release --minor",
2827
"release:patch": "npm run test && npx release --patch"
2928
},
@@ -33,6 +32,6 @@
3332
"typescript": "^5.3.3"
3433
},
3534
"dependencies": {
36-
"@rapidjs.org/testing": "^0.1.0"
35+
"@rapidjs.org/testing": "0.x"
3736
}
3837
}

packages/@cli/src/CLITest.ts

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,98 @@
1+
import { exec } from "child_process";
2+
13
import { Test } from "@rapidjs.org/testing";
24

35
import { TColor } from "../../common.types";
46

5-
interface IObject {
6-
[key: string]: string | number | boolean | IObject;
7+
interface IOutput {
8+
stdout: string;
9+
stderr: string;
10+
}
11+
12+
interface TConfiguration {
13+
commonBinary?: string;
714
}
815

9-
export class CLITest extends Test<IObject> {
16+
export class CLITest extends Test<IOutput> {
1017
public static readonly suiteTitle: string = "CLI";
1118
public static readonly suiteColor: TColor = [73, 220, 177]; // 108, 55, 55
1219

13-
constructor(title: string) {
14-
super(title);
20+
private static configuration: TConfiguration = {};
21+
22+
public static configure(configuration: TConfiguration) {
23+
CLITest.configuration = {
24+
...CLITest.configuration,
25+
...configuration
26+
};
1527
}
1628

17-
protected evalActualExpression(obj: IObject): Promise<IObject> {
18-
return new Promise((resolve) => {
19-
resolve(
20-
Object.fromEntries(
21-
Object.entries(obj).map((entry: [string, unknown]) => [entry[0].toLowerCase(), entry[1]])
22-
) as IObject
23-
);
29+
private normalizeOutput(output: Partial<IOutput>): IOutput {
30+
const normalizeStdValue = (value: string) => {
31+
return (value ?? "")
32+
.replace(/(\n|\r)+/g, "\n")
33+
.replace(/(\t| )+/g, " ")
34+
.trim()
35+
?? null;
36+
}
37+
return {
38+
stdout: normalizeStdValue(output.stdout),
39+
stderr: normalizeStdValue(output.stderr)
40+
};
41+
}
42+
43+
protected evalActualExpression(args: string[]): Promise<IOutput>;
44+
protected evalActualExpression(binary: string, args?: string[]): Promise<IOutput>;
45+
protected evalActualExpression(binaryOrArgs: string|string[], args: string[] = []): Promise<IOutput> {
46+
// TODO: Pipe chain abstraction (?)
47+
const effectiveBinary: string = (!binaryOrArgs || Array.isArray(binaryOrArgs))
48+
? CLITest.configuration.commonBinary
49+
: binaryOrArgs;
50+
if(!effectiveBinary) throw new SyntaxError("Missing binary to execute");
51+
52+
const effectiveArgs: string[] = (args ?? [ binaryOrArgs ].flat()) ?? [];
53+
54+
return new Promise((resolve, reject) => {
55+
exec([ effectiveBinary, effectiveArgs ].flat().join(" "), (err: Error, stdout: string, stderr: string) => {
56+
if(err && !stderr) {
57+
reject(err);
58+
59+
return;
60+
}
61+
62+
resolve(this.normalizeOutput({
63+
stdout, stderr
64+
}));
65+
});
2466
});
2567
}
2668

27-
protected isEqual(actual: IObject, expected: IObject): boolean {
28-
return !Object.keys(this.filterComparedValues(actual, expected).actual).length;
69+
protected evalExpectedExpression(expectedOutput: Partial<IOutput>|string): IOutput {
70+
return this.normalizeOutput(
71+
(typeof(expectedOutput) === "string")
72+
? {
73+
stdout: expectedOutput
74+
}
75+
: {
76+
stdout: [ expectedOutput.stdout ].flat().join("\n"),
77+
stderr: [ expectedOutput.stderr ].flat().join("\n")
78+
}
79+
);
2980
}
3081

31-
protected filterComparedValues(actual: IObject, expected: IObject) {
32-
return {
33-
actual: actual,
34-
expected: expected
82+
protected getDifference(actual: IOutput, expected: IOutput) {
83+
const filterObj = (sourceObj: IOutput, targetObj: IOutput) => {
84+
return {
85+
...(sourceObj.stdout !== targetObj.stdout)
86+
? { stdout: sourceObj.stdout }
87+
: {},
88+
...(sourceObj.stderr !== targetObj.stderr)
89+
? { stderr: sourceObj.stderr }
90+
: {},
91+
}
3592
};
93+
return {
94+
actual: filterObj(actual, expected),
95+
expected: filterObj(expected, actual)
96+
}
3697
}
3798
}

packages/@http/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@
3333
"typescript": "^5.3.3"
3434
},
3535
"dependencies": {
36-
"@rapidjs.org/testing": "^0.1.0"
36+
"@rapidjs.org/testing": "0.x"
3737
}
3838
}

packages/@http/src/HTTPTest.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,7 @@ export class HTTPTest extends Test<IResponse> {
3535
...configuration
3636
};
3737
}
38-
39-
constructor(title: string) {
40-
super(title);
41-
}
42-
38+
4339
protected evalActualExpression(
4440
path: string,
4541
options: TConfiguration & {

packages/@unit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@
3232
"typescript": "^5.3.3"
3333
},
3434
"dependencies": {
35-
"@rapidjs.org/testing": "^0.1.0"
35+
"@rapidjs.org/testing": "0.x"
3636
}
3737
}

packages/@unit/src/UnitTest.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@ import { TColor } from "../../common.types";
55
export class UnitTest extends Test {
66
public static readonly suiteTitle: string = "Unit";
77
public static readonly suiteColor: TColor = [38, 155, 227];
8-
9-
constructor(title: string) {
10-
super(title);
11-
}
128
}

0 commit comments

Comments
 (0)