Skip to content

Commit

Permalink
fix(betterer 🐛): tidy up result and diff APIs (#248)
Browse files Browse the repository at this point in the history
* simplify BettererRun object
* better types for BettererDiffer

BREAKING CHANGE: Changes to the public API
  • Loading branch information
phenomnomnominal authored Aug 25, 2020
1 parent ea0d88d commit 63f23fa
Show file tree
Hide file tree
Showing 33 changed files with 475 additions and 425 deletions.
4 changes: 2 additions & 2 deletions .betterer.results
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ exports[`new eslint rules`] = {
[62, 4, 68, "Promises must be handled appropriately or explicitly marked as ignored with the \`void\` operator.", "280309533"],
[63, 4, 71, "Promises must be handled appropriately or explicitly marked as ignored with the \`void\` operator.", "2936064845"]
],
"packages/extension/src/server/validator.ts:1579883463": [
"packages/extension/src/server/validator.ts:3834209490": [
[48, 10, 90, "Promises must be handled appropriately or explicitly marked as ignored with the \`void\` operator.", "1821734533"],
[96, 12, 94, "Promises must be handled appropriately or explicitly marked as ignored with the \`void\` operator.", "2449957056"]
[94, 12, 94, "Promises must be handled appropriately or explicitly marked as ignored with the \`void\` operator.", "2449957056"]
]
}`
};
54 changes: 33 additions & 21 deletions goldens/api/@betterer/betterer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ export declare type BettererContext = {

export declare type BettererDeserialise<DeserialisedType, SerialisedType = DeserialisedType> = (serialised: SerialisedType) => DeserialisedType;

export declare type BettererDiffer = (run: BettererRun) => void;
export declare type BettererDiff<DeserialisedType = unknown, DiffType = null> = {
expected: DeserialisedType;
result: DeserialisedType;
diff: DiffType;
log: () => void;
};

export declare type BettererDiffer<DeserialisedType, DiffType> = (expected: DeserialisedType, result: DeserialisedType) => DiffType;

export declare type BettererFileGlobs = ReadonlyArray<string | ReadonlyArray<string>>;

Expand Down Expand Up @@ -88,23 +95,24 @@ export declare type BettererFiles = {
getFileΔ(absolutePath: string): BettererFile | void;
};

export declare class BettererFileTest extends BettererTest<BettererFiles, BettererFileIssuesMapSerialised> {
get diff(): BettererFileTestDiff | null;
export declare type BettererFilesDiff = Record<string, {
fixed?: ReadonlyArray<BettererFileIssueDeserialised>;
existing?: ReadonlyArray<BettererFileIssueDeserialised>;
neww?: ReadonlyArray<BettererFileIssueRaw>;
}>;

export declare class BettererFileTest extends BettererTest<BettererFiles, BettererFileIssuesMapSerialised, BettererFileTestDiff> {
isBettererFileTest: string;
constructor(_resolver: BettererFileResolver, fileTest: BettererFileTestFunction);
exclude(...excludePatterns: BettererFilePatterns): this;
include(...includePatterns: BettererFileGlobs): this;
}

export declare type BettererFileTestDiff = Record<string, {
fixed?: ReadonlyArray<BettererFileIssueDeserialised>;
existing?: ReadonlyArray<BettererFileIssueDeserialised>;
neww?: ReadonlyArray<BettererFileIssueRaw>;
}>;
export declare type BettererFileTestDiff = BettererDiff<BettererFiles, BettererFilesDiff>;

export declare type BettererFileTestFunction = (files: BettererFilePaths) => MaybeAsync<BettererFileIssuesMapRaw>;

export declare type BettererPrinter<SerialisedType> = (run: BettererRun, serialised: SerialisedType) => MaybeAsync<string>;
export declare type BettererPrinter<SerialisedType> = (serialised: SerialisedType) => MaybeAsync<string>;

export declare type BettererReporter = {
contextStart?(context: BettererContext): void;
Expand All @@ -118,12 +126,17 @@ export declare type BettererReporter = {

export declare type BettererReporterNames = ReadonlyArray<string>;

export declare type BettererResult = {
isNew: boolean;
value: unknown;
};

export declare type BettererRun = {
readonly expected: unknown | typeof NO_PREVIOUS_RESULT;
readonly diff: BettererDiff;
readonly expected: BettererResult;
readonly files: BettererFilePaths;
readonly name: string;
readonly result: unknown;
readonly shouldPrint: boolean;
readonly result: BettererResult;
readonly test: BettererTest;
readonly timestamp: number;
readonly isBetter: boolean;
Expand All @@ -135,7 +148,6 @@ export declare type BettererRun = {
readonly isSkipped: boolean;
readonly isUpdated: boolean;
readonly isWorse: boolean;
diff(): void;
};

export declare type BettererRuns = ReadonlyArray<BettererRun>;
Expand All @@ -161,16 +173,16 @@ export declare type BettererStats = {
readonly worse: BettererTestNames;
};

export declare class BettererTest<DeserialisedType = unknown, SerialisedType = DeserialisedType> extends BettererTestState {
export declare class BettererTest<DeserialisedType = unknown, SerialisedType = DeserialisedType, DiffType = null> extends BettererTestState {
readonly constraint: BettererTestConstraint<DeserialisedType>;
readonly deadline: number;
differ?: BettererDiffer;
readonly differ?: BettererDiffer<DeserialisedType, DiffType>;
readonly goal: BettererTestGoal<DeserialisedType>;
isBettererTest: string;
printer?: BettererPrinter<SerialisedType>;
serialiser?: BettererSerialiser<DeserialisedType, SerialisedType>;
readonly isBettererTest = "isBettererTest";
readonly printer?: BettererPrinter<SerialisedType>;
readonly serialiser?: BettererSerialiser<DeserialisedType, SerialisedType>;
readonly test: BettererTestFunction<DeserialisedType>;
constructor(options: BettererTestOptions<DeserialisedType, SerialisedType>);
constructor(options: BettererTestOptions<DeserialisedType, SerialisedType, DiffType>);
}

export declare type BettererTestConstraint<DeserialisedType> = (result: DeserialisedType, expected: DeserialisedType) => MaybeAsync<BettererConstraintResult>;
Expand All @@ -181,12 +193,12 @@ export declare type BettererTestGoal<DeserialisedType> = (result: DeserialisedTy

export declare type BettererTestNames = Array<string>;

export declare type BettererTestOptions<DeserialisedType = unknown, SerialisedType = DeserialisedType> = {
export declare type BettererTestOptions<DeserialisedType = unknown, SerialisedType = DeserialisedType, DiffType = null> = {
constraint: BettererTestConstraint<DeserialisedType>;
deadline?: Date | string;
goal?: DeserialisedType | BettererTestGoal<DeserialisedType>;
test: BettererTestFunction<DeserialisedType>;
differ?: BettererDiffer;
differ?: BettererDiffer<DeserialisedType, DiffType>;
printer?: BettererPrinter<SerialisedType>;
serialiser?: BettererSerialiser<DeserialisedType, SerialisedType>;
} & BettererTestStateOptions;
Expand Down
65 changes: 35 additions & 30 deletions packages/betterer/src/context/context.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as assert from 'assert';

import { BettererConfig, BettererConfigFilters, BettererConfigPaths } from '../config';
import { BettererConfig } from '../config';
import { COULDNT_READ_CONFIG } from '../errors';
import { BettererReporter } from '../reporters';
import { requireUncached } from '../require';
import { print, read, write, NO_PREVIOUS_RESULT, BettererExpectedResults, BettererExpectedResult } from '../results';
import { BettererResults, BettererDiff } from '../results';
import {
BettererTest,
isBettererTest,
Expand All @@ -26,6 +26,7 @@ enum BettererContextStatus {
}

export class BettererContextΩ implements BettererContext {
private _results: BettererResults;
private _stats: BettererStatsΩ | null = null;
private _tests: BettererTestMap = {};
private _status = BettererContextStatus.notReady;
Expand All @@ -34,6 +35,7 @@ export class BettererContextΩ implements BettererContext {
private _finish: Resolve | null = null;

constructor(public readonly config: BettererConfig, private _reporter?: BettererReporter) {
this._results = new BettererResults(config);
this._reporter?.contextStart?.(this);
}

Expand All @@ -42,8 +44,9 @@ export class BettererContextΩ implements BettererContext {
await this._running;
}
assert(this._status === BettererContextStatus.notReady || this._status === BettererContextStatus.end);
this._tests = this._initTests(this.config.configPaths);
this._initFilters(this.config.filters);

this._tests = this._initTests();
this._initFilters();
this._status = BettererContextStatus.ready;
}

Expand All @@ -56,20 +59,20 @@ export class BettererContextΩ implements BettererContext {
public async runnerStart(files: BettererFilePaths = []): Promise<BettererRunsΩ> {
assert.equal(this._status, BettererContextStatus.ready);
this._stats = new BettererStatsΩ();
const expectedRaw = await this._initExpected();
const runs = Object.keys(this._tests)
.filter((name) => {
const test = this._tests[name];
// Only run BettererFileTests when a list of files is given:
return !files.length || isBettererFileTest(test);
})
.map((name) => {
let expected: BettererExpectedResult | null = null;
if (Object.hasOwnProperty.call(expectedRaw, name)) {
expected = expectedRaw[name];
}
return new BettererRunΩ(this, name, this._tests[name], expected || NO_PREVIOUS_RESULT, files);
});
await this._initObsolete();
const runs = await Promise.all(
Object.keys(this._tests)
.filter((name) => {
const test = this._tests[name];
// Only run BettererFileTests when a list of files is given:
return !files.length || isBettererFileTest(test);
})
.map(async (name) => {
const test = this._tests[name];
const expected = await this._results.getResult(name, test);
return new BettererRunΩ(this, name, test, expected, files);
})
);
this._reporter?.runsStart?.(runs, files);
this._status = BettererContextStatus.running;
this._running = new Promise((resolve) => {
Expand Down Expand Up @@ -124,14 +127,16 @@ export class BettererContextΩ implements BettererContext {
this._stats.skipped.push(run.name);
}

public runUpdate(run: BettererRunΩ): void {
public runUpdate(run: BettererRunΩ): BettererDiff {
assert(this._stats);
this._stats.updated.push(run.name);
return this._results.getDiff(run);
}

public runWorse(run: BettererRunΩ): void {
public runWorse(run: BettererRunΩ): BettererDiff {
assert(this._stats);
this._stats.worse.push(run.name);
return this._results.getDiff(run);
}

public runEnd(run: BettererRunΩ): void {
Expand All @@ -145,18 +150,18 @@ export class BettererContextΩ implements BettererContext {
public async process(runs: BettererRunsΩ): Promise<BettererStatsΩ> {
assert.equal(this._status, BettererContextStatus.end);
assert(this._stats);
const printed: Array<string> = await Promise.all(runs.filter((run) => run.shouldPrint).map((run) => print(run)));
const printed = await this._results.print(runs);
try {
await write(printed, this.config.resultsPath);
await this._results.write(printed);
} catch (error) {
this._reporter?.contextError?.(this, error, printed);
}
return this._stats;
}

private _initTests(configPaths: BettererConfigPaths): BettererTestMap {
private _initTests(): BettererTestMap {
let tests: BettererTestMap = {};
configPaths.map((configPath) => {
this.config.configPaths.map((configPath) => {
const more = this._getTests(configPath);
tests = { ...tests, ...more };
});
Expand All @@ -181,7 +186,7 @@ export class BettererContextΩ implements BettererContext {
if (isBettererTest(maybeTest)) {
test = maybeTest;
} else {
test = new BettererTest(testOptions[name] as BettererTestOptions<unknown, unknown>);
test = new BettererTest(testOptions[name] as BettererTestOptions);
}
assert(test);
tests[name] = test;
Expand All @@ -192,17 +197,17 @@ export class BettererContextΩ implements BettererContext {
}
}

private async _initExpected(): Promise<BettererExpectedResults> {
private async _initObsolete(): Promise<void> {
assert(this._stats);
const expectedRaw = await read(this.config.resultsPath);
const obsolete = Object.keys(expectedRaw).filter(
const resultNames = await this._results.getResultNames();
const obsolete = resultNames.filter(
(expectedName) => !Object.keys(this._tests).find((name) => name === expectedName)
);
this._stats.obsolete.push(...obsolete);
return expectedRaw;
}

private _initFilters(filters: BettererConfigFilters): void {
private _initFilters(): void {
const { filters } = this.config;
if (filters.length) {
Object.keys(this._tests).forEach((name) => {
if (!filters.some((filter) => filter.test(name))) {
Expand Down
Loading

0 comments on commit 63f23fa

Please sign in to comment.