Skip to content

Commit

Permalink
feat(vest): Add a parser export
Browse files Browse the repository at this point in the history
  • Loading branch information
ealush committed Nov 10, 2021
1 parent 9d8fd21 commit be1cbf6
Show file tree
Hide file tree
Showing 16 changed files with 387 additions and 47 deletions.
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ module.exports = {
'@typescript-eslint/explicit-module-boundary-types': 'off',
},
},
{
excludedFiles: ['./**/__tests__/**/*.*'],
files: ['*.ts'],
rules: {
'jest/no-export': 0,
},
},
],
parser: '@typescript-eslint/parser',
plugins: ['jest'],
Expand Down
4 changes: 4 additions & 0 deletions packages/vest/src/core/test/VestTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export default class VestTest {
return this.isFailing() || this.isWarning();
}

isTested(): boolean {
return this.hasFailures() || this.isPassing();
}

isFailing(): boolean {
return this.status === STATUS_FAILED;
}
Expand Down
1 change: 0 additions & 1 deletion packages/vest/src/core/test/test.each.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { TStringable } from 'utilityTypes';
import VestTest, { TTestResult } from 'VestTest';
import type { TTestBase } from 'test';

/* eslint-disable jest/no-export */
export default function bindTestEach(test: TTestBase): (table: any[]) => {
(fieldName: TStringable, message: TStringable, cb: TEachCb): VestTest[];
(fieldName: TStringable, cb: TEachCb): VestTest[];
Expand Down
1 change: 0 additions & 1 deletion packages/vest/src/core/test/test.memo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
useSetTestAtCursor,
} from 'stateHooks';
import type { TTestBase } from 'test';
/* eslint-disable jest/no-export */

// eslint-disable-next-line max-lines-per-function
export default function bindTestMemo(test: TTestBase): {
Expand Down
2 changes: 1 addition & 1 deletion packages/vest/src/exports/__tests__/classnames.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('Utility: classnames', () => {
'invalid_string tested_string warning_string'.split(' ').sort()
);
expect(genClass('field_3').split(' ').sort()).toEqual(
'tested_string warning_string'.split(' ').sort()
'tested_string valid_string warning_string'.split(' ').sort()
);
expect(genClass('field_4').split(' ').sort()).toEqual(
'tested_string valid_string'.split(' ').sort()
Expand Down
208 changes: 208 additions & 0 deletions packages/vest/src/exports/__tests__/parser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import * as suiteDummy from '../../../testUtils/suiteDummy';

import { parse } from 'parser';
import * as vest from 'vest';

describe('parser.parse', () => {
describe('parse().invalid', () => {
it('Should return true when provided suite result is failing and no field name is provided', () => {
expect(parse(suiteDummy.failing()).invalid()).toBe(true);
});

it('Should return false when provided suite result is passing and no field name is provided', () => {
expect(parse(suiteDummy.passing()).invalid()).toBe(false);
});

it('Should return true when provided field is failing', () => {
expect(parse(suiteDummy.failing('username')).invalid('username')).toBe(
true
);
});

it('Should return false when provided field is passing', () => {
expect(parse(suiteDummy.passing('username')).invalid('username')).toBe(
false
);
});
});

describe('parse().tested', () => {
it('Should return true if any field is tested but no field is provided', () => {
expect(parse(suiteDummy.passing()).tested()).toBe(true);
});
it('Should return true if no field is tested', () => {
expect(parse(suiteDummy.untested()).tested()).toBe(false);
expect(parse(suiteDummy.untested()).tested('field')).toBe(false);
});
it('Should return true if provided field is tested', () => {
expect(parse(suiteDummy.passing('username')).tested('username')).toBe(
true
);
expect(parse(suiteDummy.failing('username')).tested('username')).toBe(
true
);
});
});

describe('parse().untested', () => {
it('Should return true if no field is tested', () => {
expect(parse(suiteDummy.untested()).untested()).toBe(true);
expect(parse(suiteDummy.untested()).untested('username')).toBe(true);
});

it('Should return true if provided field is untested while others are', () => {
expect(
parse(
vest.create(() => {
vest.test('x', () => {});
vest.skipWhen(true, () => {
vest.test('untested', () => {});
});
})()
).untested('untested')
).toBe(true);
});

it('Should return false if any field is tested', () => {
expect(parse(suiteDummy.passing('username')).untested()).toBe(false);
});

it('Should return false if provided field is tested', () => {
expect(parse(suiteDummy.passing('username')).untested('username')).toBe(
false
);
});
});

describe('parse().valid', () => {
it('Should return true if all fields are passing', () => {
expect(parse(suiteDummy.passing(['f1', 'f2', 'f3'])).valid()).toBe(true);
expect(parse(suiteDummy.passing(['f1', 'f2', 'f3'])).valid('f2')).toBe(
true
);
});

it('Should return true if all required fields have been tested and are passing', () => {
expect(
parse(suiteDummy.passingWithUntestedOptional('optional')).valid()
).toBe(true);
});

it('Should return true if all fields, including optional, pass', () => {
expect(parse(suiteDummy.passingWithOptional('optional')).valid()).toBe(
true
);
});

it('Should return false if suite has errors', () => {
expect(parse(suiteDummy.failing()).valid()).toBe(false);
});

it('Should return false if suite has failing optional tests', () => {
expect(parse(suiteDummy.failingOptional()).valid()).toBe(false);
});

it('Should return true if suite only has warnings', () => {
expect(parse(suiteDummy.warning(['f1', 'f2', 'f3'])).valid()).toBe(true);
});

it('Should return false if no tests ran', () => {
expect(parse(suiteDummy.untested()).valid()).toBe(false);
});

it('should return false if not all required fields ran', () => {
expect(
parse(
vest.create(() => {
vest.test('x', () => {});
vest.test('untested', () => {});
vest.skipWhen(true, () => {
vest.test('untested', () => {});
});
})()
).valid()
).toBe(false);
});

describe('With field name', () => {
it('Should return false when field is untested', () => {
expect(
parse(
vest.create(() => {
vest.skipWhen(true, () => {
vest.test('f1', () => {});
});
})()
).valid('f1')
).toBe(false);
});

it('Should return true if optional field is untested', () => {
expect(
parse(
vest.create(() => {
vest.optional('f1');
vest.skipWhen(true, () => {
vest.test('f1', () => {});
});
})()
).valid('f1')
).toBe(true);
});

it('Should return true if field is passing', () => {
expect(
parse(
vest.create(() => {
vest.test('f1', () => {});
vest.test('f2', () => false);
})()
).valid('f1')
).toBe(true);
});

it('Should return false if field is failing', () => {
expect(
parse(
vest.create(() => {
vest.test('f1', () => {});
vest.test('f2', () => false);
})()
).valid('f2')
).toBe(false);
expect(
parse(
vest.create(() => {
vest.test('f1', () => {});
vest.test('f2', () => {});
vest.test('f2', () => false);
})()
).valid('f2')
).toBe(false);
});

it('Should return true if field is warning', () => {
expect(
parse(
vest.create(() => {
vest.test('f1', () => {
vest.warn();
return false;
});
})()
).valid('f1')
).toBe(true);
});
});
});

describe('parse().warning', () => {
it('Should return true when the suite has warnings', () => {
expect(parse(suiteDummy.warning()).warning()).toBe(true);
});

it('Should return false when the suite is not warnings', () => {
expect(parse(suiteDummy.failing()).warning()).toBe(false);
});
});
});
27 changes: 4 additions & 23 deletions packages/vest/src/exports/classnames.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { greaterThan } from 'greaterThan';
import hasOwnProperty from 'hasOwnProperty';
import isFunction from 'isFunction';
import throwError from 'throwError';

import { parse } from 'parser';
import type { IVestResult } from 'produce';
import type { TDraftResult } from 'produceDraft';

Expand All @@ -13,31 +13,12 @@ export default function classNames(
classes: TSupportedClasses = {}
): (fieldName: string) => string {
if (!res || !isFunction(res.hasErrors)) {
throw new Error(
throwError(
"[vest/classNames]: Expected first argument to be Vest's result object."
);
}

const testedStorage: Record<string, boolean> = {};

const selectors = {
invalid: (key: string): boolean =>
selectors.tested(key) && res.hasErrors(key),
tested: (key: string): boolean => {
if (hasOwnProperty(testedStorage, key)) return testedStorage[key];

testedStorage[key] =
hasOwnProperty(res.tests, key) &&
greaterThan(res.tests[key].testCount, 0);

return selectors.tested(key);
},
untested: (key: string): boolean => !selectors.tested(key),
valid: (key: string): boolean =>
selectors.tested(key) && !res.hasWarnings(key) && !res.hasErrors(key),
warning: (key: string): boolean =>
selectors.tested(key) && res.hasWarnings(key),
};
const selectors = parse(res);

return (key: string): string => {
const classesArray: string[] = [];
Expand Down
38 changes: 38 additions & 0 deletions packages/vest/src/exports/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { greaterThan } from 'greaterThan';
import hasOwnProperty from 'hasOwnProperty';

import type { IVestResult } from 'produce';
import type { TDraftResult } from 'produceDraft';

export function parse(res: IVestResult | TDraftResult): {
valid: (fieldName?: string) => boolean;
tested: (fieldName?: string) => boolean;
invalid: (fieldName?: string) => boolean;
untested: (fieldName?: string) => boolean;
} {
const testedStorage: Record<string, boolean> = {};

const selectors = {
invalid: res.hasErrors,
tested: (fieldName?: string): boolean => {
if (!fieldName) {
return greaterThan(res.testCount, 0);
}

if (hasOwnProperty(testedStorage, fieldName))
return testedStorage[fieldName];

testedStorage[fieldName] =
hasOwnProperty(res.tests, fieldName) &&
greaterThan(res.tests[fieldName].testCount, 0);

return selectors.tested(fieldName);
},
untested: (fieldName?: string): boolean =>
res.testCount === 0 || !selectors.tested(fieldName),
valid: res.isValid,
warning: res.hasWarnings,
};

return selectors;
}
5 changes: 2 additions & 3 deletions packages/vest/src/exports/promisify.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import isFunction from 'isFunction';
import throwError from 'throwError';

import { IVestResult } from 'produce';
import { TDraftResult } from 'produceDraft';
Expand All @@ -7,9 +8,7 @@ const promisify =
(validatorFn: (...args: any[]) => IVestResult) =>
(...args: any[]): Promise<TDraftResult> => {
if (!isFunction(validatorFn)) {
throw new Error(
'[vest/promisify]: Expected validatorFn to be a function.'
);
throwError('[vest/promisify]: Expected validatorFn to be a function.');
}

return new Promise(resolve => validatorFn(...args).done(resolve));
Expand Down
10 changes: 9 additions & 1 deletion packages/vest/src/produce/__tests__/isValid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ describe('isValid', () => {
});
});

describe('When fieldname is specified', () => {
describe('When field name is specified', () => {
it('Should return false when field did not run yet', () => {
expect(
create(() => {
Expand All @@ -254,6 +254,14 @@ describe('isValid', () => {
).toBe(false);
});

it('Should return false when testing for a field that does not exist', () => {
expect(
create(() => {
test('field_1', () => {});
})().isValid('field 2')
).toBe(false);
});

it("Should return false when some of the field's tests ran", () => {
expect(
create(() => {
Expand Down

0 comments on commit be1cbf6

Please sign in to comment.