automutate
Advanced tools
Comparing version 0.3.1 to 0.3.3
{ | ||
"name": "automutate", | ||
"version": "0.3.1", | ||
"description": "Generic framework to fix linting errors in code.", | ||
"version": "0.3.3", | ||
"description": "Applies waves of mutations provided by other tools, such as linters.", | ||
"index": "lib/index.js", | ||
@@ -6,0 +6,0 @@ "directories": { |
@@ -5,5 +5,5 @@ # automutate | ||
There are [various](https://github.com/eslint/eslint) [linters](https://github.com/palantir/tslint) [in](https://github.com/stylelint/stylelint) [the](https://github.com/lesshint/lesshint) [world](https://github.com/sasstools/sass-lint) and most are adding or have added ways to `--fix` rule failures automatically. | ||
There are [many](https://github.com/eslint/eslint) [linters](https://github.com/palantir/tslint) [in](https://github.com/stylelint/stylelint) [the](https://github.com/lesshint/lesshint) [world](https://github.com/sasstools/sass-lint) and most are adding or have added ways to `--fix` rule failures automatically. | ||
This is great but hard to do for a couple of reasons: | ||
* **Overlapping rule failures** - The possibility of mutations appling to overlapping sets of characters requires logic to handle applying one, then re-runing linting, and so on. | ||
* **Overlapping rule failures** - The possibility of mutations appling to overlapping sets of characters requires logic to handle applying one, then re-running linting, and so on. | ||
* **Code bloat verses duplication** - Most linters either provide hooks to apply fixes themselves (which can result in code bloat) or have an external project (which duplicates logic for finding rules). | ||
@@ -51,3 +51,4 @@ | ||
"type": "text-insert" | ||
}] | ||
} | ||
] | ||
} | ||
@@ -54,0 +55,0 @@ ``` |
@@ -6,3 +6,3 @@ "use strict"; | ||
/** | ||
* Generates AutoMutator instances for testing. | ||
* Creates AutoMutators for testing. | ||
*/ | ||
@@ -19,10 +19,13 @@ class AutoMutatorFactory { | ||
/** | ||
* @param Name of a .less file to automutate. | ||
* Creates an AutoMutator for testing. | ||
* | ||
* @param fileName Name of a .less file to automutate. | ||
* @param settingsFileName Name of its settings file, if any. | ||
* @returns A new AutoMutator for testing. | ||
*/ | ||
create(fileName) { | ||
create(fileName, settingsFileName) { | ||
const logger = new logger_1.Logger(); | ||
return new automutator_1.AutoMutator(new fileMutationsApplier_1.FileMutationsApplier(logger), this.mutationsProviderFactory(fileName), logger); | ||
return new automutator_1.AutoMutator(new fileMutationsApplier_1.FileMutationsApplier(logger), this.mutationsProviderFactory(fileName, settingsFileName), logger); | ||
} | ||
} | ||
exports.AutoMutatorFactory = AutoMutatorFactory; |
@@ -10,10 +10,11 @@ import { AutoMutator } from "../../lib/automutator"; | ||
* @param fileName Name of a file to mutate. | ||
* @param settingsFileName Name of its settings file, if any. | ||
* @returns A mutation provider for the file. | ||
*/ | ||
export interface IMutationsProviderFactory { | ||
(fileName: string): IMutationsProvider; | ||
(fileName: string, settingsFileName?: string): IMutationsProvider; | ||
} | ||
/** | ||
* Generates AutoMutator instances for testing. | ||
* Creates AutoMutators for testing. | ||
*/ | ||
@@ -36,6 +37,9 @@ export class AutoMutatorFactory { | ||
/** | ||
* @param Name of a .less file to automutate. | ||
* Creates an AutoMutator for testing. | ||
* | ||
* @param fileName Name of a .less file to automutate. | ||
* @param settingsFileName Name of its settings file, if any. | ||
* @returns A new AutoMutator for testing. | ||
*/ | ||
public create(fileName: string) { | ||
public create(fileName: string, settingsFileName?: string) { | ||
const logger = new Logger(); | ||
@@ -45,5 +49,5 @@ | ||
new FileMutationsApplier(logger), | ||
this.mutationsProviderFactory(fileName), | ||
this.mutationsProviderFactory(fileName, settingsFileName), | ||
logger); | ||
} | ||
} |
@@ -8,9 +8,9 @@ # Case testers | ||
1. A method to create a mutation provider for each file. | ||
2. The file extension to read files in test cases from. | ||
2. Names of files that test cases are composed of. | ||
Its `create` method takes in a a directory path containing test case directories. | ||
Its `describe` method takes in a a directory path containing test case directories. | ||
## Sample Usage | ||
Define a test file with TypeScript similar to the following: | ||
Define a test file with JavaScript or TypeScript similar to the following: | ||
@@ -25,7 +25,13 @@ ```typescript | ||
const testsFactory = new TestsFactory( | ||
fileName => new MyMutationsProvider(fileName), | ||
".txt"); | ||
(fileName, settingsFileName) => new MyMutationsProvider(fileName, settingsFileName) | ||
{ | ||
actual: "actual.txt", | ||
expected: "expected.txt", | ||
original: "original.txt", | ||
settings: "settings.txt" | ||
}); | ||
await testsFactory.create(path.join(__dirname, "cases")); | ||
await testsFactory.describe(path.join(__dirname, "cases")); | ||
})(); | ||
``` | ||
@@ -32,0 +38,0 @@ |
@@ -30,3 +30,3 @@ "use strict"; | ||
* @returns A Promise for running the test case. | ||
* @todo Promise-ify this | ||
* @todo Make this async (parallel)... | ||
*/ | ||
@@ -37,4 +37,4 @@ run() { | ||
yield this.arrangeFiles(); | ||
const autoMutator = this.autoMutatorFactory.create(this.settings.actual); | ||
const expectedContents = fs.readFileSync(this.settings.expected).toString(); | ||
const autoMutator = this.autoMutatorFactory.create(this.settings.actual, this.settings.settings); | ||
// Act | ||
@@ -48,13 +48,10 @@ yield autoMutator.run(); | ||
/** | ||
* Resets the expected file to the original file contents. | ||
* Resets the test case files. | ||
* | ||
* @returns A Promise for resetting the expected file. | ||
* @todo How to react to the reader finishing piping? | ||
* @returns A Promise for the resetting the test case files. | ||
* @todo Make this async (parallel)... | ||
*/ | ||
arrangeFiles() { | ||
return new Promise(resolve => { | ||
const reader = fs.createReadStream(this.settings.original); | ||
reader.on("close", resolve); | ||
reader.pipe(fs.createWriteStream(this.settings.actual)); | ||
setTimeout(() => reader.close(), 100); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
fs.writeFileSync(this.settings.actual, fs.readFileSync(this.settings.original)); | ||
}); | ||
@@ -61,0 +58,0 @@ } |
@@ -8,7 +8,7 @@ import { expect } from "chai"; | ||
/** | ||
* Settings for a single test case. | ||
* File names or contents for test cases. | ||
*/ | ||
export interface ITestCaseSettings { | ||
/** | ||
* File path for the mutation result. | ||
* File name or contents for the mutation result. | ||
*/ | ||
@@ -18,3 +18,3 @@ actual: string; | ||
/** | ||
* File path for what the mutation result should be. | ||
* File name or contents for what the mutation result should be. | ||
*/ | ||
@@ -24,5 +24,10 @@ expected: string; | ||
/** | ||
* File path for the original file contents. | ||
* File name or contents for the original file contents. | ||
*/ | ||
original: string; | ||
/** | ||
* File name or contents for the settings file. | ||
*/ | ||
settings: string; | ||
} | ||
@@ -35,10 +40,10 @@ | ||
/** | ||
* Settings for the test case. | ||
* Generates AutoMutator instances for testing. | ||
*/ | ||
private readonly settings: ITestCaseSettings; | ||
private readonly autoMutatorFactory: AutoMutatorFactory; | ||
/** | ||
* Generates AutoMutator instances for testing. | ||
* Settings for the test case. | ||
*/ | ||
private readonly autoMutatorFactory: AutoMutatorFactory; | ||
private readonly settings: ITestCaseSettings; | ||
@@ -60,3 +65,3 @@ /** | ||
* @returns A Promise for running the test case. | ||
* @todo Promise-ify this | ||
* @todo Make this async (parallel)... | ||
*/ | ||
@@ -66,4 +71,4 @@ public async run(): Promise<void> { | ||
await this.arrangeFiles(); | ||
const autoMutator: AutoMutator = this.autoMutatorFactory.create(this.settings.actual); | ||
const expectedContents: string = fs.readFileSync(this.settings.expected).toString(); | ||
const autoMutator: AutoMutator = this.autoMutatorFactory.create(this.settings.actual, this.settings.settings); | ||
@@ -79,18 +84,10 @@ // Act | ||
/** | ||
* Resets the expected file to the original file contents. | ||
* Resets the test case files. | ||
* | ||
* @returns A Promise for resetting the expected file. | ||
* @todo How to react to the reader finishing piping? | ||
* @returns A Promise for the resetting the test case files. | ||
* @todo Make this async (parallel)... | ||
*/ | ||
private arrangeFiles(): Promise<void> { | ||
return new Promise<void>(resolve => { | ||
const reader = fs.createReadStream(this.settings.original); | ||
reader.on("close", resolve); | ||
reader.pipe(fs.createWriteStream(this.settings.actual)); | ||
setTimeout(() => reader.close(), 100); | ||
}); | ||
private async arrangeFiles(): Promise<void> { | ||
fs.writeFileSync(this.settings.actual, fs.readFileSync(this.settings.original)); | ||
} | ||
} |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments)).next()); | ||
}); | ||
}; | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
const testCaseFactory_1 = require("./testCaseFactory"); | ||
const autoMutatorFactory_1 = require("./autoMutatorFactory"); | ||
const casesCrawler_1 = require("./casesCrawler"); | ||
const testCase_1 = require("./testCase"); | ||
/** | ||
@@ -24,24 +16,41 @@ * Creates tests for provided cases. | ||
*/ | ||
constructor(mutationsProviderFactory, extension) { | ||
this.caseFactory = new testCaseFactory_1.TestCaseFactory(new autoMutatorFactory_1.AutoMutatorFactory(mutationsProviderFactory), extension); | ||
constructor(mutationsProviderFactory, settings) { | ||
this.autoMutatorFactory = new autoMutatorFactory_1.AutoMutatorFactory(mutationsProviderFactory); | ||
this.settings = settings; | ||
this.casesCrawler = new casesCrawler_1.CasesCrawler(this.settings.original, (directoryName) => this.runTest(directoryName)); | ||
} | ||
/** | ||
* Creates tests for the a cases directory. | ||
* Describes tests for the cases directory. | ||
* | ||
* @param casesPath Path to the test cases. | ||
* @returns A Promise for creating tests for the cases directory. | ||
* @todo Promise-ify this. | ||
*/ | ||
create(casesPath) { | ||
const caseNames = fs.readdirSync(casesPath); | ||
describe("cases", () => { | ||
for (const caseName of caseNames) { | ||
it(caseName, () => __awaiter(this, void 0, void 0, function* () { | ||
return (yield this.caseFactory.create(path.join(casesPath, caseName))) | ||
.run(); | ||
})); | ||
} | ||
}); | ||
describe(casesPath) { | ||
this.casesCrawler.crawl("cases", casesPath); | ||
} | ||
/** | ||
* Creates and runs a test case. | ||
* | ||
* @param casePath Path to the test case. | ||
* @returns A Promise for running the test case. | ||
*/ | ||
runTest(casePath) { | ||
return (new testCase_1.TestCase(this.createTestCaseSettings(casePath), this.autoMutatorFactory)) | ||
.run(); | ||
} | ||
/** | ||
* Creates settings for a test case. | ||
* | ||
* @param casePath Path to a test case. | ||
* @returns Settings for the test case. | ||
*/ | ||
createTestCaseSettings(casePath) { | ||
return { | ||
actual: path.join(casePath, this.settings.actual), | ||
expected: path.join(casePath, this.settings.expected), | ||
original: path.join(casePath, this.settings.original), | ||
settings: path.join(casePath, this.settings.settings) | ||
}; | ||
} | ||
} | ||
exports.TestsFactory = TestsFactory; |
@@ -1,7 +0,6 @@ | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import { TestCaseFactory } from "./testCaseFactory"; | ||
import { IMutationsProviderFactory } from "./autoMutatorFactory"; | ||
import { AutoMutatorFactory } from "./autoMutatorFactory"; | ||
import { AutoMutatorFactory, IMutationsProviderFactory } from "./autoMutatorFactory"; | ||
import { CasesCrawler } from "./casesCrawler"; | ||
import { ITestCaseSettings, TestCase } from "./testCase"; | ||
@@ -15,5 +14,15 @@ /** | ||
*/ | ||
private readonly caseFactory: TestCaseFactory; | ||
private readonly autoMutatorFactory: AutoMutatorFactory; | ||
/** | ||
* Settings for the test cases. | ||
*/ | ||
private readonly settings: ITestCaseSettings; | ||
/** | ||
* Crawls a directory structure for test case settings. | ||
*/ | ||
private readonly casesCrawler: CasesCrawler; | ||
/** | ||
* Initializes a new instance of the TestsFactory class. | ||
@@ -24,27 +33,45 @@ * | ||
*/ | ||
public constructor(mutationsProviderFactory: IMutationsProviderFactory, extension: string) { | ||
this.caseFactory = new TestCaseFactory( | ||
new AutoMutatorFactory(mutationsProviderFactory), | ||
extension); | ||
public constructor(mutationsProviderFactory: IMutationsProviderFactory, settings: ITestCaseSettings) { | ||
this.autoMutatorFactory = new AutoMutatorFactory(mutationsProviderFactory); | ||
this.settings = settings; | ||
this.casesCrawler = new CasesCrawler( | ||
this.settings.original, | ||
(directoryName: string): Promise<void> => this.runTest(directoryName)); | ||
} | ||
/** | ||
* Creates tests for the a cases directory. | ||
* Describes tests for the cases directory. | ||
* | ||
* @param casesPath Path to the test cases. | ||
* @returns A Promise for creating tests for the cases directory. | ||
* @todo Promise-ify this. | ||
*/ | ||
public create(casesPath: string): void { | ||
const caseNames: string[] = fs.readdirSync(casesPath); | ||
public describe(casesPath: string): void { | ||
this.casesCrawler.crawl("cases", casesPath); | ||
} | ||
describe("cases", (): void => { | ||
for (const caseName of caseNames) { | ||
it(caseName, async (): Promise<void> => { | ||
return (await this.caseFactory.create(path.join(casesPath, caseName))) | ||
.run(); | ||
}); | ||
} | ||
}); | ||
/** | ||
* Creates and runs a test case. | ||
* | ||
* @param casePath Path to the test case. | ||
* @returns A Promise for running the test case. | ||
*/ | ||
private runTest(casePath: string): Promise<void> { | ||
return (new TestCase(this.createTestCaseSettings(casePath), this.autoMutatorFactory)) | ||
.run(); | ||
} | ||
/** | ||
* Creates settings for a test case. | ||
* | ||
* @param casePath Path to a test case. | ||
* @returns Settings for the test case. | ||
*/ | ||
private createTestCaseSettings(casePath: string): ITestCaseSettings { | ||
return { | ||
actual: path.join(casePath, this.settings.actual), | ||
expected: path.join(casePath, this.settings.expected), | ||
original: path.join(casePath, this.settings.original), | ||
settings: path.join(casePath, this.settings.settings) | ||
}; | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
198990
4181
75