Skip to content

Commit c6454fb

Browse files
author
Josh Goldberg
authored
Added rudimentary end-to-end test infrastructure (#81)
* Added rudimentary end-to-end test infrastructure * Removed added test .js files
1 parent e9ff058 commit c6454fb

20 files changed

+339
-30
lines changed

.eslintrc.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"env": {
3+
"es6": true,
4+
"node": true
5+
},
6+
"parser": "@typescript-eslint/parser",
7+
"parserOptions": {
8+
"project": "tsconfig.json",
9+
"sourceType": "module"
10+
},
11+
"plugins": ["@typescript-eslint", "@typescript-eslint/tslint"],
12+
"rules": {
13+
"@typescript-eslint/array-type": "error",
14+
"@typescript-eslint/interface-name-prefix": "error",
15+
"@typescript-eslint/member-ordering": "off",
16+
"@typescript-eslint/no-explicit-any": "off",
17+
"@typescript-eslint/no-param-reassign": "off",
18+
"@typescript-eslint/no-parameter-properties": "off",
19+
"@typescript-eslint/no-use-before-declare": "off",
20+
"@typescript-eslint/promise-function-async": "off",
21+
"@typescript-eslint/unbound-method": "off",
22+
"arrow-body-style": "off",
23+
"default-case": "off",
24+
"linebreak-style": "off",
25+
"no-bitwise": "off",
26+
"no-empty": "off",
27+
"no-empty-functions": "off",
28+
"no-magic-numbers": "off",
29+
"prefer-template": "off",
30+
"@typescript-eslint/tslint/config": [
31+
"error",
32+
{
33+
"rules": {
34+
"no-implicit-dependencies": [true, "dev"],
35+
"strict-boolean-expressions": [
36+
true,
37+
"allow-boolean-or-undefined",
38+
"allow-number"
39+
]
40+
}
41+
}
42+
]
43+
}
44+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
coverage/
66
node_modules/
77
src/**/*.js
8+
test/*.js
9+
test/tests/**/.eslintrc*

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test/tests/**/*

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"**/*.js.map": true,
55
"**/*.js": { "when": "$(basename).ts" }
66
},
7+
"prettier.ignorePath": ".prettierignore",
78
"typescript.tsdk": "node_modules/typescript/lib"
89
}

docs/Architecture.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Architecture
2+
3+
## CLI Architecture
4+
5+
- CLI usage starts in `bin/tslint-to-eslint-config`, which immediately calls `src/cli/main.ts`.
6+
- CLI settings are parsed and read in `src/cli/runCli.ts`.
7+
- Application logic is run by `src/conversion/convertConfig.ts`.
8+
9+
## Dependencies
10+
11+
Methods are created using a variant of [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_Injection).
12+
Any method with dependencies that should be stubbed out during tests, such as file system bindings, logging, or other methods,
13+
takes in a first argument of name `dependencies`.
14+
Its dependencies object is manually created in `src/cli/main.ts` and bound to the method with `bind`.

docs/Development.md

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,9 @@ cd tslint-to-eslint-config
1717
npm i
1818
```
1919

20-
Compile with `npm run tsc` and run tests with `npm run test`.
20+
Compile with `npm run tsc` and run tests with `npm run test:unit`.
2121

22-
### CLI Architecture
22+
## More Reading
2323

24-
- CLI usage starts in `bin/tslint-to-eslint-config`, which immediately calls `src/cli/main.ts`.
25-
- CLI settings are parsed and read in `src/cli/runCli.ts`.
26-
- Application logic is run by `src/conversion/convertConfig.ts`.
27-
28-
### Dependencies
29-
30-
Methods are created using a variant of [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_Injection).
31-
Any method with dependencies that should be stubbed out during tests, such as file system bindings, logging, or other methods,
32-
takes in a first argument of name `dependencies`.
33-
Its dependencies object is manually created in `src/cli/main.ts` and bound to the method with `bind`.
34-
35-
### Unit Tests
36-
37-
Each `src/**/*.ts` source file should have an equivalent `*.test.ts` next to it.
38-
Tests should include comments above each section of the "AAA" testing format:
39-
40-
- `// Arrange`: Set up dependencies for the code under test
41-
- `// Act`: Perform the logic under test
42-
- `// Assert`: Check the results of the acted logic
43-
44-
`tslint-to-eslint-config` requires 100% unit test coverage, excluding the following:
45-
46-
- `src/cli/main.ts`
47-
- `src/adapters/*.ts`
48-
- `src/**/*.stubs.ts`
24+
- [Architecture](./Architecture.md)
25+
- [Testing](./Testing.md)

docs/Testing.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Testing
2+
3+
## Unit Tests
4+
5+
```
6+
npm run test:unit
7+
```
8+
9+
Each `src/**/*.ts` source file should have an equivalent `*.test.ts` next to it.
10+
Tests should include comments above each section of the "AAA" testing format:
11+
12+
- `// Arrange`: Set up dependencies for the code under test
13+
- `// Act`: Perform the logic under test
14+
- `// Assert`: Check the results of the acted logic
15+
16+
`tslint-to-eslint-config` requires 100% unit test coverage, excluding the following:
17+
18+
- `src/cli/main.ts`
19+
- `src/adapters/*.ts`
20+
- `src/**/*.stubs.ts`

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ module.exports = {
1717
},
1818
},
1919
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
20-
testRegex: "((\\.|/)test)\\.tsx?$",
20+
testRegex: "src(.*)\\.test\\.tsx?$",
2121
testEnvironment: "node",
2222
};

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@
5959
},
6060
"scripts": {
6161
"eslint": "eslint \"./src/*.ts\" \"./src/**/*.ts\" --report-unused-disable-directives",
62-
"prettier": "prettier \"./src/*.{js,json,ts,xml,yaml}\" \"./src/**/*.{js,json,ts,xml,yaml}\"",
62+
"prettier": "prettier \"./src/*.{js,json,ts,xml,yaml}\" \"./src/**/*.{js,json,ts,xml,yaml}\" --ignore-path .prettierignore",
6363
"prettier:write": "npm run prettier -- --write",
64-
"test": "jest",
64+
"test:unit": "jest",
65+
"test:end-to-end": "jest --config=test/jest.config.js",
66+
"test:end-to-end:accept": "jest --config=test/jest.config.js --globals=\"{\\\"acceptTestChanges\\\": true }\"",
6567
"tsc": "tsc"
6668
},
6769
"version": "0.1.7"

src/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ export const isError = <Item>(item: Item | Error): item is Error => item instanc
55
export type RemoveErrors<Items> = {
66
[P in keyof Items]: Exclude<Items[P], Error>;
77
};
8+
9+
export type PromiseValue<T> = T extends Promise<infer R> ? R : never;

test/createTestArgs.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
import { promisify } from "util";
4+
5+
const readdir = promisify(fs.readdir);
6+
7+
export const createTestArgs = async (cwd: string) => {
8+
const items = new Set(await readdir(cwd));
9+
const flags = [
10+
"--config",
11+
path.join(cwd, ".eslintrc.json"),
12+
"--tslint",
13+
path.join(cwd, "tslint.json"),
14+
];
15+
16+
if (items.has("tslint.json")) {
17+
}
18+
19+
return flags.map(flag => `"${flag}"`).join(" ");
20+
};

test/createTests.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as cp from "child_process";
2+
import * as fs from "fs";
3+
import * as path from "path";
4+
import { promisify } from "util";
5+
6+
import { createTestArgs } from "./createTestArgs";
7+
import { PromiseValue } from "../src/utils";
8+
import { assertFileContents } from "./expectFileContains";
9+
10+
const exec = promisify(cp.exec);
11+
const readFile = promisify(fs.readFile);
12+
13+
export const createTests = (testName: string, accept: boolean) => {
14+
const cwd = path.join(__dirname, "tests", testName);
15+
const cwdPath = (fileName: string) => path.join(cwd, fileName);
16+
const readTestFile = async (fileName: string) => (await readFile(cwdPath(fileName))).toString();
17+
18+
return () => {
19+
let result: PromiseValue<ReturnType<typeof exec>>;
20+
beforeAll(async () => {
21+
// Arrange
22+
const args = await createTestArgs(cwd);
23+
24+
// Act
25+
result = await exec(`ts-node bin/tslint-to-eslint-config ${args}`);
26+
});
27+
28+
test("configuration output", async () => {
29+
await assertFileContents(
30+
cwdPath("expected.json"),
31+
await readTestFile(".eslintrc.json"),
32+
accept,
33+
);
34+
});
35+
36+
// test("info log output", () => {});
37+
38+
test("stderr", async () => {
39+
await assertFileContents(cwdPath("stderr.txt"), result.stderr, accept);
40+
});
41+
42+
test("stdout", async () => {
43+
await assertFileContents(cwdPath("stdout.txt"), result.stdout, accept);
44+
});
45+
};
46+
};

test/expectFileContains.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as fs from "fs";
2+
import { promisify } from "util";
3+
4+
const readFile = promisify(fs.readFile);
5+
const writeFile = promisify(fs.writeFile);
6+
7+
const standardizeEndlines = (text: string) => text.replace(/\r/g, "");
8+
9+
export const assertFileContents = async (
10+
filePath: string,
11+
actual: string | Buffer,
12+
accept: boolean,
13+
) => {
14+
await (accept ? acceptFileContents : expectFileContents)(filePath, actual.toString());
15+
};
16+
17+
const acceptFileContents = async (filePath: string, actual: string) => {
18+
await writeFile(filePath, actual);
19+
};
20+
21+
const expectFileContents = async (filePath: string, actual: string) => {
22+
const expected = (await readFile(filePath)).toString();
23+
24+
expect(standardizeEndlines(actual)).toEqual(standardizeEndlines(expected));
25+
};

test/jest.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
3+
testRegex: "test/runEndToEndTests.ts",
4+
testEnvironment: "node",
5+
};

test/runEndToEndTests.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
4+
import { createTests } from "./createTests";
5+
6+
const testNames = fs.readdirSync(path.join(__dirname, "tests"));
7+
8+
const accept = "acceptTestChanges" in globalThis;
9+
10+
for (const testName of testNames) {
11+
describe(testName, createTests(testName, accept));
12+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"env": {
3+
"es6": true,
4+
"node": true
5+
},
6+
"parser": "@typescript-eslint/parser",
7+
"parserOptions": {
8+
"project": "tsconfig.json",
9+
"sourceType": "module"
10+
},
11+
"plugins": [
12+
"@typescript-eslint",
13+
"@typescript-eslint/tslint"
14+
],
15+
"rules": {
16+
"@typescript-eslint/array-type": "error",
17+
"@typescript-eslint/interface-name-prefix": "error",
18+
"@typescript-eslint/member-ordering": "off",
19+
"@typescript-eslint/no-explicit-any": "off",
20+
"@typescript-eslint/no-param-reassign": "off",
21+
"@typescript-eslint/no-parameter-properties": "off",
22+
"@typescript-eslint/no-use-before-declare": "off",
23+
"@typescript-eslint/promise-function-async": "off",
24+
"@typescript-eslint/unbound-method": "off",
25+
"arrow-body-style": "off",
26+
"default-case": "off",
27+
"linebreak-style": "off",
28+
"no-bitwise": "off",
29+
"no-empty": "off",
30+
"no-empty-functions": "off",
31+
"no-magic-numbers": "off",
32+
"prefer-template": "off",
33+
"@typescript-eslint/tslint/config": [
34+
"error",
35+
{
36+
"rules": {
37+
"no-implicit-dependencies": [
38+
true,
39+
"dev"
40+
],
41+
"strict-boolean-expressions": [
42+
true,
43+
"allow-boolean-or-undefined",
44+
"allow-number"
45+
]
46+
}
47+
}
48+
]
49+
}
50+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"env": {
3+
"es6": true,
4+
"node": true
5+
},
6+
"parser": "@typescript-eslint/parser",
7+
"parserOptions": {
8+
"project": "tsconfig.json",
9+
"sourceType": "module"
10+
},
11+
"plugins": [
12+
"@typescript-eslint",
13+
"@typescript-eslint/tslint"
14+
],
15+
"rules": {
16+
"@typescript-eslint/array-type": "error",
17+
"@typescript-eslint/interface-name-prefix": "error",
18+
"@typescript-eslint/member-ordering": "off",
19+
"@typescript-eslint/no-explicit-any": "off",
20+
"@typescript-eslint/no-param-reassign": "off",
21+
"@typescript-eslint/no-parameter-properties": "off",
22+
"@typescript-eslint/no-use-before-declare": "off",
23+
"@typescript-eslint/promise-function-async": "off",
24+
"@typescript-eslint/unbound-method": "off",
25+
"arrow-body-style": "off",
26+
"default-case": "off",
27+
"linebreak-style": "off",
28+
"no-bitwise": "off",
29+
"no-empty": "off",
30+
"no-empty-functions": "off",
31+
"no-magic-numbers": "off",
32+
"prefer-template": "off",
33+
"@typescript-eslint/tslint/config": [
34+
"error",
35+
{
36+
"rules": {
37+
"no-implicit-dependencies": [
38+
true,
39+
"dev"
40+
],
41+
"strict-boolean-expressions": [
42+
true,
43+
"allow-boolean-or-undefined",
44+
"allow-number"
45+
]
46+
}
47+
}
48+
]
49+
}
50+
}

test/tests/standalone tslint.json/stderr.txt

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
✨ 17 rules replaced with their ESLint equivalents. ✨
2+
️👀 2 rules do not yet have ESLint equivalents; defaulting to eslint-plugin-tslint. 👀
3+
✅ All is well! ✅

0 commit comments

Comments
 (0)