Skip to content

Commit 656e88d

Browse files
committed
added: [vest] hook vest.get
1 parent a7dde44 commit 656e88d

File tree

19 files changed

+327
-63
lines changed

19 files changed

+327
-63
lines changed

config/jest/jest.setup.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
const glob = require('glob');
22

3+
const {
4+
default: isDeepCopy,
5+
} = require('../../packages/vest/testUtils/isDeepCopy');
36
const { packagePath } = require('../../util');
47

58
global.isWatchMode = (process.argv || []).some(
@@ -16,4 +19,8 @@ test.skipOnWatch = (...args) => {
1619

1720
return test(...args);
1821
};
22+
23+
expect.extend({
24+
isDeepCopyOf: isDeepCopy,
25+
});
1926
/* eslint-enable */

docs/result.md

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,47 @@ A result object would look somewhat like this:
66

77
```js
88
{
9-
'name': 'formName', // The name of the validation suite
10-
'errorCount': 0, // Overall count of errors in the suite
11-
'warnCount': 0, // Overall count of warnings in the suite
12-
'tests': Object { // An object containing all non-skipped tests
13-
'fieldName': Object { // Name of each field
14-
'errorCount': 0, // Error count per field
15-
'errors': Array [], // Array of error messages fer field (may be undefined)
16-
'warnings': Array [], // Array of warning messages fer field (may be undefined)
17-
'warnCount': 0, // Warning count per field
9+
'name': 'formName', // The name of the validation suite
10+
'errorCount': Number 0, // Overall count of errors in the suite
11+
'warnCount': Number 0, // Overall count of warnings in the suite
12+
'testCount': Number 0, // Overall test count for the suite (passing, failing and warning)
13+
'tests': Object { // An object containing all non-skipped tests
14+
'fieldName': Object { // Name of each field
15+
'errorCount': Number 0, // Error count per field
16+
'errors': Array [], // Array of error messages fer field (may be undefined)
17+
'warnings': Array [], // Array of warning messages fer field (may be undefined)
18+
'warnCount': Number 0, // Warning count per field
19+
'testCount': Number 0, // Overall test count for the field (passing, failing and warning)
1820
},
21+
'groups': Object { // An object containing groups declared in the suite
22+
'fieldName': Object { // Subset of res.tests[fieldName] only containing tests
23+
/*... */ // only containing tests that ran within the group
24+
}
25+
}
1926
}
2027
}
2128
```
2229

30+
## Accessing the last result object with `vest.get`
31+
32+
Alternatively, if you need to access your validation results out of context - for example, from a different UI component or function, you can use `vest.get`.
33+
34+
Vest exposes the `vest.get` function that is able to retrieve the most recent validation result of [**stateful**](./state) suites (suites created using vest.create()).
35+
36+
In case your validations did not run yet, `vest.get` returns an empty validation result object - which can be helpful when trying to access validation result object when rendering the initial UI, or setting it in the initial state of your components.
37+
38+
vest.get takes a single argument: the suite name. It is used to identify which validation result to retrieve.
39+
40+
```js
41+
import vest from 'vest';
42+
43+
const res = vest.get('suite_name');
44+
45+
res.hasErrors('fieldName');
46+
```
47+
48+
# Result Object Methods:
49+
2350
Along with these values, the result object exposes the following methods:
2451

2552
## `hasErrors` and `hasWarnings` functions

packages/vest/src/__snapshots__/spec.js.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ Object {
141141

142142
exports[`Vest exports All vest exports exist 1`] = `
143143
Object {
144-
"Enforce": undefined,
145144
"VERSION": Any<String>,
146145
"create": [Function],
147146
"draft": [Function],
148147
"enforce": [Function],
148+
"get": [Function],
149149
"group": [Function],
150150
"only": [Function],
151151
"reset": [Function],
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Test createSuite module Initial run Should initialize with an empty state object 1`] = `
4+
Array [
5+
Object {
6+
"doneCallbacks": Array [],
7+
"exclusive": Object {},
8+
"fieldCallbacks": Object {},
9+
"groups": Object {},
10+
"lagging": Array [],
11+
"name": "initial_run_spec",
12+
"pending": Array [],
13+
"suiteId": "initial_run_spec",
14+
"testObjects": Array [],
15+
"tests": Object {},
16+
},
17+
undefined,
18+
]
19+
`;

packages/vest/src/core/createSuite/index.js

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import runWithContext from '../../lib/runWithContext';
33
import singleton from '../../lib/singleton';
44
import validateSuiteParams from '../../lib/validateSuiteParams';
55
import produce from '../produce';
6+
import { getSuite } from '../state';
67
import getSuiteState from '../state/getSuiteState';
78
import registerSuite from '../state/registerSuite';
89
import mergeExcludedTests from '../test/lib/mergeExcludedTests';
@@ -17,19 +18,29 @@ import runAsyncTest from '../test/runAsyncTest';
1718
const createSuite = (name, tests) => {
1819
validateSuiteParams('vest.create', name, tests);
1920

20-
// returns validator function
21-
return (...args) => {
22-
const shouldCreateId = !singleton.useContext()?.suiteId;
21+
const ctx = singleton.useContext();
22+
23+
const ctxRef = {
24+
suiteId: ctx?.suiteId || name,
25+
operationMode: ctx?.operationMode || OPERATION_MODE_STATEFUL,
26+
name,
27+
};
2328

24-
const ctxRef = singleton.useContext() ?? {
25-
name,
26-
operationMode: OPERATION_MODE_STATEFUL,
27-
tests,
28-
...(shouldCreateId && {
29-
suiteId: name,
30-
}),
31-
};
29+
/**
30+
* Initialize empty suite state. This is not required for vest
31+
* itself, but it is handy to have a default value when using
32+
* UI frameworks that might try to get the validation results
33+
* during initial render. This is irrelevant for stateless mode.
34+
*/
35+
if (
36+
ctxRef.operationMode === OPERATION_MODE_STATEFUL &&
37+
!getSuite(ctxRef.suiteId)
38+
) {
39+
runWithContext(ctxRef, registerSuite);
40+
}
3241

42+
// returns validator function
43+
return (...args) => {
3344
const output = runWithContext(ctxRef, context => {
3445
registerSuite();
3546
const { suiteId } = context;

packages/vest/src/core/createSuite/spec.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import faker from 'faker';
22
import { noop } from 'lodash';
33
import mock from '../../../testUtils/mock';
4+
import resetState from '../../../testUtils/resetState';
5+
import { OPERATION_MODE_STATELESS } from '../../constants';
6+
import runWithContext from '../../lib/runWithContext';
7+
import { getSuite } from '../state';
48
import createSuite from '.';
59

610
describe('Test createSuite module', () => {
@@ -57,4 +61,50 @@ describe('Test createSuite module', () => {
5761
expect(testsCallback).toHaveBeenCalledWith(...params);
5862
});
5963
});
64+
65+
describe('Initial run', () => {
66+
const testsCb = jest.fn();
67+
const suiteId = 'initial_run_spec';
68+
const runCreateSuite = () => createSuite(suiteId, testsCb);
69+
70+
afterEach(() => {
71+
resetState();
72+
});
73+
74+
it('Should initialize with an empty state object', () => {
75+
expect(getSuite(suiteId)).toBeUndefined();
76+
runCreateSuite();
77+
const state = getSuite(suiteId);
78+
expect(state).toHaveLength(2);
79+
expect(state[1]).toBeUndefined();
80+
expect(state[0].suiteId).toBe(suiteId);
81+
expect(state[0].name).toBe(suiteId);
82+
expect(state[0].testObjects).toHaveLength(0);
83+
expect(state[0].pending).toHaveLength(0);
84+
expect(state[0].lagging).toHaveLength(0);
85+
expect(Object.keys(state[0].exclusive)).toHaveLength(0);
86+
expect(Object.keys(state[0].tests)).toHaveLength(0);
87+
expect(Object.keys(state[0].groups)).toHaveLength(0);
88+
expect(Object.keys(state[0].doneCallbacks)).toHaveLength(0);
89+
expect(Object.keys(state[0].fieldCallbacks)).toHaveLength(0);
90+
91+
expect(getSuite(suiteId)).toMatchSnapshot();
92+
});
93+
94+
it('Should return without calling tests callback', () => {
95+
const validate = runCreateSuite();
96+
expect(testsCb).not.toHaveBeenCalled();
97+
validate();
98+
expect(testsCb).toHaveBeenCalled();
99+
});
100+
101+
describe('When in stateless mode', () => {
102+
it('Should return without creating initial state', () => {
103+
runWithContext({ operationMode: OPERATION_MODE_STATELESS }, () => {
104+
runCreateSuite();
105+
});
106+
});
107+
expect(getSuite(suiteId)).toBeUndefined();
108+
});
109+
});
60110
});

packages/vest/src/core/produce/spec.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import _ from 'lodash';
22
import collector from '../../../testUtils/collector';
3-
import isDeepCopy from '../../../testUtils/isDeepCopy';
43
import resetState from '../../../testUtils/resetState';
54
import runRegisterSuite from '../../../testUtils/runRegisterSuite';
65
import runSpec from '../../../testUtils/runSpec';
@@ -87,8 +86,7 @@ runSpec(vest => {
8786
});
8887

8988
it('Should create a deep copy of subset of the state', () => {
90-
isDeepCopy(
91-
_.pick(getSuiteState(suiteId), KEPT_PROPERTIES),
89+
expect(_.pick(getSuiteState(suiteId), KEPT_PROPERTIES)).isDeepCopyOf(
9290
_.pick(produced, KEPT_PROPERTIES)
9391
);
9492
});
@@ -444,12 +442,16 @@ runSpec(vest => {
444442

445443
it('Should pass produced result to callback', () => {
446444
produced.done(doneCallback_1).done(doneCallback_2);
447-
isDeepCopy(doneCallback_1.mock.calls[0][0], produce(state));
448-
isDeepCopy(doneCallback_2.mock.calls[0][0], produce(state));
445+
expect(doneCallback_1.mock.calls[0][0]).isDeepCopyOf(
446+
produce(state)
447+
);
448+
expect(doneCallback_2.mock.calls[0][0]).isDeepCopyOf(
449+
produce(state)
450+
);
449451
});
450452

451453
it('Should return produced result', () => {
452-
isDeepCopy(produced.done(doneCallback_1), produce(state));
454+
expect(produced.done(doneCallback_1)).isDeepCopyOf(produce(state));
453455
});
454456
});
455457

@@ -466,13 +468,16 @@ runSpec(vest => {
466468
it('Should pass produced result to callback', () => {
467469
produced.done('field_1', doneCallback_1).done(doneCallback_2);
468470

469-
isDeepCopy(doneCallback_1.mock.calls[0][0], produce(state));
470-
isDeepCopy(doneCallback_2.mock.calls[0][0], produce(state));
471+
expect(doneCallback_1.mock.calls[0][0]).isDeepCopyOf(
472+
produce(state)
473+
);
474+
expect(doneCallback_2.mock.calls[0][0]).isDeepCopyOf(
475+
produce(state)
476+
);
471477
});
472478

473479
it('Should return produced result', () => {
474-
isDeepCopy(
475-
produced.done('field_1', doneCallback_1),
480+
expect(produced.done('field_1', doneCallback_1)).isDeepCopyOf(
476481
produce(state)
477482
);
478483
});
@@ -516,7 +521,7 @@ runSpec(vest => {
516521
expect(doneCallback_1).not.toHaveBeenCalled();
517522
});
518523
it('Should return produced output', () => {
519-
isDeepCopy(produced.done(doneCallback_1), produce(state));
524+
expect(produced.done(doneCallback_1)).isDeepCopyOf(produce(state));
520525
});
521526

522527
it('Should add callback to `doneCallBacks` array', () =>

packages/vest/src/core/state/registerSuite/spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { getState } from '..';
2-
import isDeepCopy from '../../../../testUtils/isDeepCopy';
32
import resetState from '../../../../testUtils/resetState';
43
import runRegisterSuite from '../../../../testUtils/runRegisterSuite';
54
import { OPERATION_MODE_STATEFUL } from '../../../constants';
@@ -58,7 +57,7 @@ describe('registerSuite', () => {
5857
runRegisterSuite(context);
5958
});
6059
it('Should merge previous pending and lagging into lagging', () => {
61-
isDeepCopy(suite[0].lagging, [...pending, ...lagging]);
60+
expect(suite[0].lagging).isDeepCopyOf([...pending, ...lagging]);
6261
});
6362

6463
it('Should match snapshot', () => {
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import produce from '../../core/produce';
2-
import getSuiteState from '../../core/state/getSuiteState';
31
import singleton from '../../lib/singleton';
42
import throwError from '../../lib/throwError';
53
import { ERROR_HOOK_CALLED_OUTSIDE } from '../constants';
4+
import get from '../get';
65

76
/**
87
* @returns {Object} Current output object.
@@ -14,8 +13,8 @@ const draft = () => {
1413
throwError('draft ' + ERROR_HOOK_CALLED_OUTSIDE);
1514
return;
1615
}
17-
const state = getSuiteState(ctx.suiteId);
18-
return produce(state, { draft: true });
16+
17+
return get(ctx.suiteId);
1918
};
2019

2120
export default draft;

packages/vest/src/hooks/draft/spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import faker from 'faker';
2-
import isDeepCopy from '../../../testUtils/isDeepCopy';
32
import runSpec from '../../../testUtils/runSpec';
43

54
runSpec(vest => {
@@ -20,7 +19,7 @@ runSpec(vest => {
2019
createSuite(() => {
2120
const a = vest.draft();
2221
const b = vest.draft();
23-
isDeepCopy(a, b);
22+
expect(a).isDeepCopyOf(b);
2423
});
2524
});
2625
it('Should only contain has/get callbacks', () => {

0 commit comments

Comments
 (0)