Skip to content

Added rudimentary end-to-end test infrastructure #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"env": {
"es6": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "@typescript-eslint/tslint"],
"rules": {
"@typescript-eslint/array-type": "error",
"@typescript-eslint/interface-name-prefix": "error",
"@typescript-eslint/member-ordering": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-param-reassign": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-use-before-declare": "off",
"@typescript-eslint/promise-function-async": "off",
"@typescript-eslint/unbound-method": "off",
"arrow-body-style": "off",
"default-case": "off",
"linebreak-style": "off",
"no-bitwise": "off",
"no-empty": "off",
"no-empty-functions": "off",
"no-magic-numbers": "off",
"prefer-template": "off",
"@typescript-eslint/tslint/config": [
"error",
{
"rules": {
"no-implicit-dependencies": [true, "dev"],
"strict-boolean-expressions": [
true,
"allow-boolean-or-undefined",
"allow-number"
]
}
}
]
}
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
coverage/
node_modules/
src/**/*.js
test/*.js
test/tests/**/.eslintrc*
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test/tests/**/*
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"**/*.js.map": true,
"**/*.js": { "when": "$(basename).ts" }
},
"prettier.ignorePath": ".prettierignore",
"typescript.tsdk": "node_modules/typescript/lib"
}
14 changes: 14 additions & 0 deletions docs/Architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Architecture

## CLI Architecture

- CLI usage starts in `bin/tslint-to-eslint-config`, which immediately calls `src/cli/main.ts`.
- CLI settings are parsed and read in `src/cli/runCli.ts`.
- Application logic is run by `src/conversion/convertConfig.ts`.

## Dependencies

Methods are created using a variant of [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_Injection).
Any method with dependencies that should be stubbed out during tests, such as file system bindings, logging, or other methods,
takes in a first argument of name `dependencies`.
Its dependencies object is manually created in `src/cli/main.ts` and bound to the method with `bind`.
31 changes: 4 additions & 27 deletions docs/Development.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,9 @@ cd tslint-to-eslint-config
npm i
```

Compile with `npm run tsc` and run tests with `npm run test`.
Compile with `npm run tsc` and run tests with `npm run test:unit`.

### CLI Architecture
## More Reading

- CLI usage starts in `bin/tslint-to-eslint-config`, which immediately calls `src/cli/main.ts`.
- CLI settings are parsed and read in `src/cli/runCli.ts`.
- Application logic is run by `src/conversion/convertConfig.ts`.

### Dependencies

Methods are created using a variant of [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_Injection).
Any method with dependencies that should be stubbed out during tests, such as file system bindings, logging, or other methods,
takes in a first argument of name `dependencies`.
Its dependencies object is manually created in `src/cli/main.ts` and bound to the method with `bind`.

### Unit Tests

Each `src/**/*.ts` source file should have an equivalent `*.test.ts` next to it.
Tests should include comments above each section of the "AAA" testing format:

- `// Arrange`: Set up dependencies for the code under test
- `// Act`: Perform the logic under test
- `// Assert`: Check the results of the acted logic

`tslint-to-eslint-config` requires 100% unit test coverage, excluding the following:

- `src/cli/main.ts`
- `src/adapters/*.ts`
- `src/**/*.stubs.ts`
- [Architecture](./Architecture.md)
- [Testing](./Testing.md)
20 changes: 20 additions & 0 deletions docs/Testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Testing

## Unit Tests

```
npm run test:unit
```

Each `src/**/*.ts` source file should have an equivalent `*.test.ts` next to it.
Tests should include comments above each section of the "AAA" testing format:

- `// Arrange`: Set up dependencies for the code under test
- `// Act`: Perform the logic under test
- `// Assert`: Check the results of the acted logic

`tslint-to-eslint-config` requires 100% unit test coverage, excluding the following:

- `src/cli/main.ts`
- `src/adapters/*.ts`
- `src/**/*.stubs.ts`
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ module.exports = {
},
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
testRegex: "((\\.|/)test)\\.tsx?$",
testRegex: "src(.*)\\.test\\.tsx?$",
testEnvironment: "node",
};
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@
},
"scripts": {
"eslint": "eslint \"./src/*.ts\" \"./src/**/*.ts\" --report-unused-disable-directives",
"prettier": "prettier \"./src/*.{js,json,ts,xml,yaml}\" \"./src/**/*.{js,json,ts,xml,yaml}\"",
"prettier": "prettier \"./src/*.{js,json,ts,xml,yaml}\" \"./src/**/*.{js,json,ts,xml,yaml}\" --ignore-path .prettierignore",
"prettier:write": "npm run prettier -- --write",
"test": "jest",
"test:unit": "jest",
"test:end-to-end": "jest --config=test/jest.config.js",
"test:end-to-end:accept": "jest --config=test/jest.config.js --globals=\"{\\\"acceptTestChanges\\\": true }\"",
"tsc": "tsc"
},
"version": "0.1.7"
Expand Down
2 changes: 2 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export const isError = <Item>(item: Item | Error): item is Error => item instanc
export type RemoveErrors<Items> = {
[P in keyof Items]: Exclude<Items[P], Error>;
};

export type PromiseValue<T> = T extends Promise<infer R> ? R : never;
20 changes: 20 additions & 0 deletions test/createTestArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as fs from "fs";
import * as path from "path";
import { promisify } from "util";

const readdir = promisify(fs.readdir);

export const createTestArgs = async (cwd: string) => {
const items = new Set(await readdir(cwd));
const flags = [
"--config",
path.join(cwd, ".eslintrc.json"),
"--tslint",
path.join(cwd, "tslint.json"),
];

if (items.has("tslint.json")) {
}

return flags.map(flag => `"${flag}"`).join(" ");
};
46 changes: 46 additions & 0 deletions test/createTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as cp from "child_process";
import * as fs from "fs";
import * as path from "path";
import { promisify } from "util";

import { createTestArgs } from "./createTestArgs";
import { PromiseValue } from "../src/utils";
import { assertFileContents } from "./expectFileContains";

const exec = promisify(cp.exec);
const readFile = promisify(fs.readFile);

export const createTests = (testName: string, accept: boolean) => {
const cwd = path.join(__dirname, "tests", testName);
const cwdPath = (fileName: string) => path.join(cwd, fileName);
const readTestFile = async (fileName: string) => (await readFile(cwdPath(fileName))).toString();

return () => {
let result: PromiseValue<ReturnType<typeof exec>>;
beforeAll(async () => {
// Arrange
const args = await createTestArgs(cwd);

// Act
result = await exec(`ts-node bin/tslint-to-eslint-config ${args}`);
});

test("configuration output", async () => {
await assertFileContents(
cwdPath("expected.json"),
await readTestFile(".eslintrc.json"),
accept,
);
});

// test("info log output", () => {});

test("stderr", async () => {
await assertFileContents(cwdPath("stderr.txt"), result.stderr, accept);
});

test("stdout", async () => {
await assertFileContents(cwdPath("stdout.txt"), result.stdout, accept);
});
};
};
25 changes: 25 additions & 0 deletions test/expectFileContains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as fs from "fs";
import { promisify } from "util";

const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);

const standardizeEndlines = (text: string) => text.replace(/\r/g, "");

export const assertFileContents = async (
filePath: string,
actual: string | Buffer,
accept: boolean,
) => {
await (accept ? acceptFileContents : expectFileContents)(filePath, actual.toString());
};

const acceptFileContents = async (filePath: string, actual: string) => {
await writeFile(filePath, actual);
};

const expectFileContents = async (filePath: string, actual: string) => {
const expected = (await readFile(filePath)).toString();

expect(standardizeEndlines(actual)).toEqual(standardizeEndlines(expected));
};
5 changes: 5 additions & 0 deletions test/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
testRegex: "test/runEndToEndTests.ts",
testEnvironment: "node",
};
12 changes: 12 additions & 0 deletions test/runEndToEndTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as fs from "fs";
import * as path from "path";

import { createTests } from "./createTests";

const testNames = fs.readdirSync(path.join(__dirname, "tests"));

const accept = "acceptTestChanges" in globalThis;

for (const testName of testNames) {
describe(testName, createTests(testName, accept));
}
50 changes: 50 additions & 0 deletions test/tests/standalone tslint.json/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"env": {
"es6": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"@typescript-eslint/tslint"
],
"rules": {
"@typescript-eslint/array-type": "error",
"@typescript-eslint/interface-name-prefix": "error",
"@typescript-eslint/member-ordering": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-param-reassign": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-use-before-declare": "off",
"@typescript-eslint/promise-function-async": "off",
"@typescript-eslint/unbound-method": "off",
"arrow-body-style": "off",
"default-case": "off",
"linebreak-style": "off",
"no-bitwise": "off",
"no-empty": "off",
"no-empty-functions": "off",
"no-magic-numbers": "off",
"prefer-template": "off",
"@typescript-eslint/tslint/config": [
"error",
{
"rules": {
"no-implicit-dependencies": [
true,
"dev"
],
"strict-boolean-expressions": [
true,
"allow-boolean-or-undefined",
"allow-number"
]
}
}
]
}
}
50 changes: 50 additions & 0 deletions test/tests/standalone tslint.json/expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"env": {
"es6": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"@typescript-eslint/tslint"
],
"rules": {
"@typescript-eslint/array-type": "error",
"@typescript-eslint/interface-name-prefix": "error",
"@typescript-eslint/member-ordering": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-param-reassign": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-use-before-declare": "off",
"@typescript-eslint/promise-function-async": "off",
"@typescript-eslint/unbound-method": "off",
"arrow-body-style": "off",
"default-case": "off",
"linebreak-style": "off",
"no-bitwise": "off",
"no-empty": "off",
"no-empty-functions": "off",
"no-magic-numbers": "off",
"prefer-template": "off",
"@typescript-eslint/tslint/config": [
"error",
{
"rules": {
"no-implicit-dependencies": [
true,
"dev"
],
"strict-boolean-expressions": [
true,
"allow-boolean-or-undefined",
"allow-number"
]
}
}
]
}
}
Empty file.
3 changes: 3 additions & 0 deletions test/tests/standalone tslint.json/stdout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
✨ 17 rules replaced with their ESLint equivalents. ✨
️👀 2 rules do not yet have ESLint equivalents; defaulting to eslint-plugin-tslint. 👀
✅ All is well! ✅
Loading