Skip to content
This repository was archived by the owner on Mar 25, 2021. It is now read-only.

Commit 0b83cbe

Browse files
ajafffadidahiya
authored andcommitted
Refactor executableTests to use runner.ts where possible (#3374)
1 parent 22ec110 commit 0b83cbe

File tree

6 files changed

+327
-430
lines changed

6 files changed

+327
-430
lines changed

src/runner.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ export interface Options {
9696
rulesDirectory?: string | string[];
9797

9898
/**
99-
* That TSLint produces the correct output for the specified directory.
99+
* Run the tests in the given directories to ensure a (custom) TSLint rule's output matches the expected output.
100+
* When this property is `true` the `files` property is used to specify the directories from which the tests should be executed.
100101
*/
101-
test?: string;
102+
test?: boolean;
102103

103104
/**
104105
* Whether to enable type checking when linting a project.
@@ -122,7 +123,7 @@ export async function run(options: Options, logger: Logger): Promise<Status> {
122123
return await runWorker(options, logger);
123124
} catch (error) {
124125
if (error instanceof FatalError) {
125-
logger.error(error.message);
126+
logger.error(`${error.message}\n`);
126127
return Status.FatalError;
127128
}
128129
throw error;
@@ -142,7 +143,7 @@ async function runWorker(options: Options, logger: Logger): Promise<Status> {
142143
if (options.test) {
143144
const test = await import("./test");
144145
const results = test.runTests((options.files || []).map(trimSingleQuotes), options.rulesDirectory);
145-
return test.consoleTestResultsHandler(results) ? Status.Ok : Status.FatalError;
146+
return test.consoleTestResultsHandler(results, logger) ? Status.Ok : Status.FatalError;
146147
}
147148

148149
if (options.config && !fs.existsSync(options.config)) {
@@ -151,20 +152,20 @@ async function runWorker(options: Options, logger: Logger): Promise<Status> {
151152

152153
const { output, errorCount } = await runLinter(options, logger);
153154
if (output && output.trim()) {
154-
logger.log(output);
155+
logger.log(`${output}\n`);
155156
}
156157
return options.force || errorCount === 0 ? Status.Ok : Status.LintError;
157158
}
158159

159160
async function runLinter(options: Options, logger: Logger): Promise<LintResult> {
160-
const { files, program } = resolveFilesAndProgram(options);
161+
const { files, program } = resolveFilesAndProgram(options, logger);
161162
// if type checking, run the type checker
162163
if (program && options.typeCheck) {
163164
const diagnostics = ts.getPreEmitDiagnostics(program);
164165
if (diagnostics.length !== 0) {
165166
const message = diagnostics.map((d) => showDiagnostic(d, program, options.outputAbsolutePaths)).join("\n");
166167
if (options.force) {
167-
logger.error(message);
168+
logger.error(`${message}\n`);
168169
} else {
169170
throw new FatalError(message);
170171
}
@@ -173,12 +174,15 @@ async function runLinter(options: Options, logger: Logger): Promise<LintResult>
173174
return doLinting(options, files, program, logger);
174175
}
175176

176-
function resolveFilesAndProgram({ files, project, exclude, outputAbsolutePaths }: Options): { files: string[]; program?: ts.Program } {
177+
function resolveFilesAndProgram(
178+
{ files, project, exclude, outputAbsolutePaths }: Options,
179+
logger: Logger,
180+
): { files: string[]; program?: ts.Program } {
177181
// remove single quotes which break matching on Windows when glob is passed in single quotes
178182
exclude = exclude.map(trimSingleQuotes);
179183

180184
if (project === undefined) {
181-
return { files: resolveGlobs(files, exclude, outputAbsolutePaths) };
185+
return { files: resolveGlobs(files, exclude, outputAbsolutePaths, logger) };
182186
}
183187

184188
const projectPath = findTsconfig(project);
@@ -202,7 +206,7 @@ function resolveFilesAndProgram({ files, project, exclude, outputAbsolutePaths }
202206
if (fs.existsSync(file)) {
203207
throw new FatalError(`'${file}' is not included in project.`);
204208
}
205-
console.warn(`'${file}' does not exist. This will be an error in TSLint 6.`); // TODO make this an error in v6.0.0
209+
logger.error(`'${file}' does not exist. This will be an error in TSLint 6.\n`); // TODO make this an error in v6.0.0
206210
}
207211
}
208212
}
@@ -217,15 +221,15 @@ function filterFiles(files: string[], patterns: string[], include: boolean): str
217221
return files.filter((file) => include === matcher.some((pattern) => pattern.match(file)));
218222
}
219223

220-
function resolveGlobs(files: string[], ignore: string[], outputAbsolutePaths?: boolean): string[] {
224+
function resolveGlobs(files: string[], ignore: string[], outputAbsolutePaths: boolean | undefined, logger: Logger): string[] {
221225
const results = flatMap(
222226
files,
223227
(file) => glob.sync(trimSingleQuotes(file), { ignore, nodir: true }),
224228
);
225229
// warn if `files` contains non-existent files, that are not patters and not excluded by any of the exclude patterns
226230
for (const file of filterFiles(files, ignore, false)) {
227-
if (!glob.hasMagic(file)) {
228-
console.warn(`'${file}' does not exist. This will be an error in TSLint 6.`); // TODO make this an error in v6.0.0
231+
if (!glob.hasMagic(file) && !results.some(createMinimatchFilter(file))) {
232+
logger.error(`'${file}' does not exist. This will be an error in TSLint 6.\n`); // TODO make this an error in v6.0.0
229233
}
230234
}
231235
const cwd = process.cwd();
@@ -248,17 +252,17 @@ async function doLinting(
248252
let configFile: IConfigurationFile | undefined;
249253

250254
for (const file of files) {
255+
const folder = path.dirname(file);
256+
if (lastFolder !== folder) {
257+
configFile = findConfiguration(possibleConfigAbsolutePath, folder).results;
258+
lastFolder = folder;
259+
}
251260
if (isFileExcluded(file)) {
252261
continue;
253262
}
254263

255264
const contents = program !== undefined ? program.getSourceFile(file).text : await tryReadFile(file, logger);
256265
if (contents !== undefined) {
257-
const folder = path.dirname(file);
258-
if (lastFolder !== folder) {
259-
configFile = findConfiguration(possibleConfigAbsolutePath, folder).results;
260-
lastFolder = folder;
261-
}
262266
linter.lint(file, contents, configFile);
263267
}
264268
}
@@ -287,7 +291,7 @@ async function tryReadFile(filename: string, logger: Logger): Promise<string | u
287291
// MPEG transport streams use the '.ts' file extension. They use 0x47 as the frame
288292
// separator, repeating every 188 bytes. It is unlikely to find that pattern in
289293
// TypeScript source, so tslint ignores files with the specific pattern.
290-
logger.error(`${filename}: ignoring MPEG transport stream`);
294+
logger.error(`${filename}: ignoring MPEG transport stream\n`);
291295
return undefined;
292296
}
293297
} finally {

src/test.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import * as ts from "typescript";
2525

2626
import { Replacement } from "./language/rule/rule";
2727
import * as Linter from "./linter";
28+
import { Logger } from "./runner";
2829
import { denormalizeWinPath, mapDefined, readBufferWithDetectedEncoding } from "./utils";
2930
import { LintError } from "./verify/lintError";
3031
import * as parse from "./verify/parse";
@@ -195,60 +196,57 @@ export function runTest(testDirectory: string, rulesDirectory?: string | string[
195196
return results;
196197
}
197198

198-
export function consoleTestResultsHandler(testResults: TestResult[]): boolean {
199+
export function consoleTestResultsHandler(testResults: TestResult[], logger: Logger): boolean {
199200
let didAllTestsPass = true;
200201

201202
for (const testResult of testResults) {
202-
if (!consoleTestResultHandler(testResult)) {
203+
if (!consoleTestResultHandler(testResult, logger)) {
203204
didAllTestsPass = false;
204205
}
205206
}
206207

207208
return didAllTestsPass;
208209
}
209210

210-
export function consoleTestResultHandler(testResult: TestResult): boolean {
211+
export function consoleTestResultHandler(testResult: TestResult, logger: Logger): boolean {
211212
// needed to get colors to show up when passing through Grunt
212213
(chalk as any).enabled = true;
213214

214215
let didAllTestsPass = true;
215216

216217
for (const fileName of Object.keys(testResult.results)) {
217218
const results = testResult.results[fileName];
218-
process.stdout.write(`${fileName}:`);
219+
logger.log(`${fileName}:`);
219220

220-
/* tslint:disable:no-console */
221221
if (results.skipped) {
222-
console.log(chalk.yellow(` Skipped, requires typescript ${results.requirement}`));
222+
logger.log(chalk.yellow(` Skipped, requires typescript ${results.requirement}\n`));
223223
} else {
224224
const markupDiffResults = diff.diffLines(results.markupFromMarkup, results.markupFromLinter);
225225
const fixesDiffResults = diff.diffLines(results.fixesFromLinter, results.fixesFromMarkup);
226226
const didMarkupTestPass = !markupDiffResults.some((hunk) => hunk.added === true || hunk.removed === true);
227227
const didFixesTestPass = !fixesDiffResults.some((hunk) => hunk.added === true || hunk.removed === true);
228228

229229
if (didMarkupTestPass && didFixesTestPass) {
230-
console.log(chalk.green(" Passed"));
230+
logger.log(chalk.green(" Passed\n"));
231231
} else {
232-
console.log(chalk.red(" Failed!"));
232+
logger.log(chalk.red(" Failed!\n"));
233233
didAllTestsPass = false;
234234
if (!didMarkupTestPass) {
235-
displayDiffResults(markupDiffResults, MARKUP_FILE_EXTENSION);
235+
displayDiffResults(markupDiffResults, MARKUP_FILE_EXTENSION, logger);
236236
}
237237
if (!didFixesTestPass) {
238-
displayDiffResults(fixesDiffResults, FIXES_FILE_EXTENSION);
238+
displayDiffResults(fixesDiffResults, FIXES_FILE_EXTENSION, logger);
239239
}
240240
}
241241
}
242-
/* tslint:enable:no-console */
243242
}
244243

245244
return didAllTestsPass;
246245
}
247246

248-
function displayDiffResults(diffResults: diff.IDiffResult[], extension: string) {
249-
/* tslint:disable:no-console */
250-
console.log(chalk.green(`Expected (from ${extension} file)`));
251-
console.log(chalk.red("Actual (from TSLint)"));
247+
function displayDiffResults(diffResults: diff.IDiffResult[], extension: string, logger: Logger) {
248+
logger.log(chalk.green(`Expected (from ${extension} file)\n`));
249+
logger.log(chalk.red("Actual (from TSLint)\n"));
252250

253251
for (const diffResult of diffResults) {
254252
let color = chalk.grey;
@@ -257,7 +255,6 @@ function displayDiffResults(diffResults: diff.IDiffResult[], extension: string)
257255
} else if (diffResult.removed) {
258256
color = chalk.red.underline;
259257
}
260-
process.stdout.write(color(diffResult.value));
258+
logger.log(color(diffResult.value));
261259
}
262-
/* tslint:enable:no-console */
263260
}

src/tslint-cli.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ interface Argv {
3838
formattersDir: string;
3939
format?: string;
4040
typeCheck?: boolean;
41-
test?: string;
41+
test?: boolean;
4242
version?: boolean;
4343
}
4444

@@ -244,16 +244,9 @@ if (argv.typeCheck) {
244244
}
245245
}
246246

247-
let log: (message: string) => void;
248-
if (argv.out != undefined) {
249-
const outputStream = fs.createWriteStream(argv.out, {
250-
flags: "w+",
251-
mode: 420,
252-
});
253-
log = (message) => outputStream.write(`${message}\n`);
254-
} else {
255-
log = console.log;
256-
}
247+
const outputStream: NodeJS.WritableStream = argv.out === undefined
248+
? process.stdout
249+
: fs.createWriteStream(argv.out, {flags: "w+", mode: 420});
257250

258251
run(
259252
{
@@ -273,8 +266,12 @@ run(
273266
typeCheck: argv.typeCheck,
274267
},
275268
{
276-
log,
277-
error: (m) => console.error(m),
269+
log(m) {
270+
outputStream.write(m);
271+
},
272+
error(m) {
273+
process.stdout.write(m);
274+
},
278275
})
279276
.then((rc) => {
280277
process.exitCode = rc;

0 commit comments

Comments
 (0)