Skip to content
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

fix(betterer 🐛): tidy up result and diff APIs #248

Merged
merged 2 commits into from
Aug 25, 2020
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
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