1
0
Fork 0

format failed results better

This commit is contained in:
ssube 2021-07-21 18:28:22 -05:00
parent 3383718aac
commit 141999b881
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
4 changed files with 86 additions and 17 deletions

23
LICENSE.md Normal file
View File

@ -0,0 +1,23 @@
# The MIT License (MIT)
The MIT License (MIT)
Copyright (c) 2021 Sean Sube
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,4 +1,9 @@
{
"name": "mocha-froth",
"version": "0.1.0",
"main": "out/src/index.js",
"author": "ssube <seansube@gmail.com>",
"license": "MIT",
"devDependencies": {
"@types/chai-as-promised": "^7.1.4",
"@types/mocha": "^8.2.3",

View File

@ -1,31 +1,19 @@
import { Arbitrary, check, property } from 'fast-check';
import { Arbitrary, check, property, RunDetails, Parameters } from 'fast-check';
export type Check<T> = (this: Mocha.Context, t: T) => boolean;
export type Check<T> = (this: Mocha.Context, val: T) => boolean;
export type WrappedIt<T> = (name: string, check: Check<T>) => void;
export type Suite<T> = (it: WrappedIt<T>) => void;
export function over<T>(name: string, strategy: Arbitrary<T>, suite: Suite<T>): void {
export function over<T>(name: string, strategy: Arbitrary<T>, suite: Suite<T>, parameters?: Parameters<[T]>): void {
describe(name, () => {
suite((name, test) => {
it(name, function (this: Mocha.Context): Promise<void> {
const ctx = this;
return new Promise((res, rej) => {
const result = check(property(strategy, (t) => test.call(ctx, t)));
const result = check(property(strategy, (val) => test.call(ctx, val)), parameters);
if (result.failed) {
if (result.counterexample !== null) {
const examples = result.counterexample.map(it => {
if (typeof it === 'string') {
return `'${it}'`;
} else {
return it;
}
}).join(',');
const error = `failed after ${result.numRuns} runs and ${result.numShrinks} shrinks, failing on: ${examples}`;
rej(new Error(error));
} else {
rej(new Error(`failed after ${result.numRuns} runs and ${result.numShrinks} shrinks, without counterexamples`));
}
rej(new Error(formatDetails(result)));
} else {
res();
}
@ -34,3 +22,37 @@ export function over<T>(name: string, strategy: Arbitrary<T>, suite: Suite<T>):
});
});
}
export function formatDetails<T>(details: RunDetails<[T]>): string {
const prefix = formatPrefix(details);
const counts = `${prefix} after ${details.numRuns} runs and ${details.numShrinks} shrinks`;
if (details.counterexample !== null) {
const examples = details.counterexample.map((val) => {
if (isString(val)) {
return `'${val}'`;
} else {
return val;
}
}).join(',');
return `${counts}, failing on: ${examples}`;
} else {
return `${counts}, without counterexamples`;
}
}
export function formatPrefix<T>(details: RunDetails<[T]>): string {
if (isString(details.error)) {
if (details.error.startsWith('Error:')) {
return 'Property failed by throwing an error';
}
return details.error;
}
return 'Property failed without a reason';
}
export function isString(val: unknown): val is string {
return typeof val === 'string';
}

View File

@ -12,12 +12,31 @@ describe('some foo', () => {
it('should be even', (bar: number) => {
return bar % 2 === 0;
});
it('should not throw', (t: number) => {
if (t.toString()[3] === '9') {
throw new Error('not a real number!');
}
return true;
});
});
over('some IDs', uuid(), (it) => {
beforeEach(() => {
console.log('before each ID test');
});
it('should be a good one', (id: string) => {
return id[9] !== 'a';
});
it('should be long enough', (id: string) => {
return id.length > 2;
});
}, {
examples: [['a']],
numRuns: 1_000_000_000,
});
over('even numbers', integer().filter(n => n % 2 === 0), (it) => {