Skip to content

Commit

Permalink
feat(vest): use key prop to retain test state after reorder (#732)
Browse files Browse the repository at this point in the history
  • Loading branch information
ealush committed Dec 5, 2021
1 parent ddceca7 commit b0a9a14
Show file tree
Hide file tree
Showing 30 changed files with 605 additions and 67 deletions.
6 changes: 5 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:import/typescript',
],
files: ['*.ts'],
files: ['packages/**/*.ts'],
parserOptions: {
project: ['./tsconfig.json'], // Specify it only for TypeScript files
},
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
Expand Down Expand Up @@ -91,6 +94,7 @@ module.exports = {
'no-multi-spaces': 1,
'no-prototype-builtins': 0,
'no-trailing-spaces': [2, { ignoreComments: false }],
'no-undef': 2,
'no-unneeded-ternary': 2,
'no-unused-expressions': 2,
'no-useless-catch': 2,
Expand Down
21 changes: 14 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,44 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
## vest: [3.2.8] - 2021-11-02

### Fixed and improved
- 9555d1b types: Add types for skipWhen (ealush)

- 9555d1b types: Add types for skipWhen (ealush)

## vest: [3.2.7] - 2021-10-16

### Fixed and improved
- c2964e0 patch: add skipWhen for forward compatibility (ealush)

- c2964e0 patch: add skipWhen for forward compatibility (ealush)

## n4s: [3.1.0] - 2021-10-09

### Added
- 5adc337 feat: add isBlank rule (Luca Pizzini)

- 5adc337 feat: add isBlank rule (Luca Pizzini)

## vest: [3.2.6] - 2021-10-09

### Fixed and improved
- e538e5b patch: remove incorrect messaging from group initialization error (ealush)

- e538e5b patch: remove incorrect messaging from group initialization error (ealush)

## vest: [3.2.5] - 2021-07-31

### Fixed and improved
- 3bb7f76 patch: Fix crashing of setFnName in IE11 (#671) (Václav Jančařík)

- 3bb7f76 patch: Fix crashing of setFnName in IE11 (#671) (Václav Jančařík)

## vest: [3.2.4] - 2021-07-19

### Fixed and improved
- ba6c296 patch: remove unused optional references from the state (ealush)

- ba6c296 patch: remove unused optional references from the state (ealush)

## vest: [3.2.3] - 2021-05-27

### Fixed and improved
- c02f22f patch: use context only as dev dep (ealush)

- c02f22f patch: use context only as dev dep (ealush)

## vest: [3.2.2] - 2021-05-17

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

If you want to try it out, you can already install it with the `next` tag.

```npm install vest@next```
`npm install vest@next`

This version brings many enhancements to Vest. This version is well tested, and can be used safely. If you do, however encounter any issues or have any thoughts on the next version, please report them on the issues page.

Expand Down
7 changes: 5 additions & 2 deletions packages/context/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Simple utility for context propagation within Javascript applications and librar
It allows you to keep reference for shared variables, and access them down in your function call even if not declared in the same scope.

## How Context Works?

The way context works is quite simple. Creating a context initializes a closure with a context storage object. When you run your context call, it takes your values, and places them in the context storage object. When your function finishes running, the context is cleared.

![image](https://user-images.githubusercontent.com/11255103/137119151-6912d3f1-ab48-4c91-a426-76af8abc8c55.png)
Expand Down Expand Up @@ -125,13 +126,15 @@ Working with context inside within async code may lead to unexpected results whe
This is known and expected behavior. Context is a synchronous context propagation tool that completely relies on the synchronous nature of function calls in JS - this is exactly what allows context to run.

#### But my function is still running. Why did the context clear?

The async parts of your function are actually not executed along with your sync code, and even though you "await" it, the browser carries on and allows other code to run in between instead of blocking execution until your async code is complete.

#### Ok, so what do I do?

There are multiple strategies of handling async functions with context.

1. Pulling your values from context right before your async call
This is the most obvious and easiest to achieve, though not always what you need. The basic idea is that you take whatever you need from the context when it is still available to you.
This is the most obvious and easiest to achieve, though not always what you need. The basic idea is that you take whatever you need from the context when it is still available to you.

2. context.bind or context.run your async function to the context you extracted
This is the next logical step - you have a function that you know should run later with your context. You can bind your context to it for delayed execution. When your function runs later down the line within your asynchronous code, internally it will still have access to whatever you bound to it.
This is the next logical step - you have a function that you know should run later with your context. You can bind your context to it for delayed execution. When your function runs later down the line within your asynchronous code, internally it will still have access to whatever you bound to it.
6 changes: 3 additions & 3 deletions packages/n4s/src/plugins/schema/optional.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import isNullish from 'isNullish';

import type { TLazy } from 'genEnforceLazy';
import { isNull } from 'isNull';
import { isUndefined } from 'isUndefined';
import type { TRuleDetailedResult } from 'ruleReturn';
import * as ruleReturn from 'ruleReturn';
import runLazyRule from 'runLazyRule';

export function optional(value: any, ruleChain: TLazy): TRuleDetailedResult {
if (isUndefined(value) || isNull(value)) {
if (isNullish(value)) {
return ruleReturn.passing();
}
return runLazyRule(ruleChain, value);
Expand Down
4 changes: 2 additions & 2 deletions packages/n4s/src/runtime/enforceEager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ export default function enforceEager(value: TRuleValue): IRules {
return target;
}

const proxy = new Proxy(target, {
const proxy: TEnforceEager = new Proxy(target, {
get: (_, ruleName: string) => {
const rule = getRule(ruleName);
if (rule) {
return genRuleCall(proxy, rule, ruleName);
}
},
}) as IRules;
});

return proxy;

Expand Down
6 changes: 6 additions & 0 deletions packages/shared/src/isNullish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { isNull } from 'isNull';
import { isUndefined } from 'isUndefined';

export default function isNullish(value: any): value is null | undefined {
return isNull(value) || isUndefined(value);
}
2 changes: 1 addition & 1 deletion packages/vest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

If you want to try it out, you can already install it with the `next` tag.

```npm install vest@next```
`npm install vest@next`

This version brings many enhancements to Vest. This version is well tested, and can be used safely. If you do, however encounter any issues or have any thoughts on the next version, please report them on the issues page.

Expand Down
15 changes: 6 additions & 9 deletions packages/vest/src/__tests__/isolate.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import mockThrowError from '../../testUtils/mockThrowError';

import { IsolateTypes } from 'IsolateTypes';

describe('isolate', () => {
let firstRun = true;
let vest, isolate, skipWhen, dummyTest;
let throwError, throwErrorDeferred;
let throwErrorDeferred;

beforeEach(() => {
firstRun = true;
jest.resetModules();
throwError = jest.fn();
throwErrorDeferred = jest.fn();
jest.mock('throwError', () => ({
throwErrorDeferred,
default: throwError,
}));
vest = require('vest');
const mock = mockThrowError();
throwErrorDeferred = mock.throwErrorDeferred;
vest = mock.vest;
skipWhen = vest.skipWhen;
isolate = require('isolate').isolate;
dummyTest = require('../../testUtils/testDummy').dummyTest;
Expand Down
17 changes: 13 additions & 4 deletions packages/vest/src/core/ctx/ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assign from 'assign';
import { createContext } from 'context';
import { createCursor } from 'cursor';

import { IsolateTypes } from 'IsolateTypes';
import { IsolateKeys, IsolateTypes } from 'IsolateTypes';
import VestTest from 'VestTest';
import type { TStateRef } from 'createStateRef';

Expand All @@ -12,19 +12,28 @@ export default createContext<CTXType>((ctxRef, parentContext) =>
: assign(
{},
{
isolate: { type: IsolateTypes.DEFAULT },
testCursor: createCursor(),
exclusion: {
tests: {},
groups: {},
},
isolate: {
type: IsolateTypes.DEFAULT,
keys: {
current: {},
prev: {},
},
},
testCursor: createCursor(),
},
ctxRef
)
);

type CTXType = {
isolate: { type: IsolateTypes };
isolate: {
type: IsolateTypes;
keys: IsolateKeys;
};
testCursor: ReturnType<typeof createCursor>;
stateRef?: TStateRef;
exclusion: {
Expand Down
7 changes: 7 additions & 0 deletions packages/vest/src/core/isolate/IsolateTypes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import VestTest from 'VestTest';

export enum IsolateTypes {
DEFAULT,
SUITE,
EACH,
SKIP_WHEN,
GROUP,
}

export type IsolateKeys = {
current: Record<string, VestTest>;
prev: Record<string, VestTest>;
};
11 changes: 9 additions & 2 deletions packages/vest/src/core/isolate/isolate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import isFunction from 'isFunction';
import * as nestedArray from 'nestedArray';

import { IsolateTypes } from 'IsolateTypes';
import { IsolateKeys, IsolateTypes } from 'IsolateTypes';
import VestTest from 'VestTest';
import ctx from 'ctx';
import { usePrevKeys } from 'key';
import { useSetTests } from 'stateHooks';
import * as testCursor from 'testCursor';

Expand All @@ -14,11 +15,17 @@ export function isolate(
if (!isFunction(callback)) {
return;
}
const keys: IsolateKeys = {
current: {},
prev: {},
};

const path = testCursor.usePath();
return ctx.run({ isolate: { type } }, () => {
return ctx.run({ isolate: { type, keys } }, () => {
testCursor.addLevel();

keys.prev = usePrevKeys();

useSetTests(tests => nestedArray.setValueAtPath(tests, path, []));

const res = callback();
Expand Down
2 changes: 1 addition & 1 deletion packages/vest/src/core/state/createStateRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function createStateRef(
current: NestedArray<VestTest>;
}>(prev => {
return {
prev: (prev ? prev.current : []) as NestedArray<VestTest>,
prev: prev ? prev.current : [],
current: [] as NestedArray<VestTest>,
};
}),
Expand Down
11 changes: 10 additions & 1 deletion packages/vest/src/core/test/VestTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default class VestTest {
asyncTest?: TAsyncTest;
groupName?: string;
message?: string;
key?: null | string = null;

id = genId();
severity = TestSeverity.Error;
Expand All @@ -22,7 +23,11 @@ export default class VestTest {
constructor(
fieldName: string,
testFn: TTestFn,
{ message, groupName }: { message?: string; groupName?: string } = {}
{
message,
groupName,
key,
}: { message?: string; groupName?: string; key?: string } = {}
) {
this.fieldName = fieldName;
this.testFn = testFn;
Expand All @@ -34,6 +39,10 @@ export default class VestTest {
if (message) {
this.message = message;
}

if (key) {
this.key = key;
}
}

run(): TTestResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exports[`VestTest TestObject constructor 1`] = `
VestTest {
"fieldName": "unicycle",
"id": "0",
"key": null,
"message": "I am Root.",
"severity": "error",
"status": "UNTESTED",
Expand All @@ -15,6 +16,7 @@ exports[`VestTest testObject.warn Should mark the test as warning 1`] = `
VestTest {
"fieldName": "unicycle",
"id": "102",
"key": null,
"message": "I am Root.",
"severity": "warning",
"status": "UNTESTED",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Test Vest's \`test\` function test params creates a test with a message and with a key 1`] = `
VestTest {
"fieldName": "field_name",
"id": "21",
"key": "keyboardcat",
"message": "failure message",
"severity": "error",
"status": "PASSING",
"testFn": [Function],
}
`;

exports[`Test Vest's \`test\` function test params creates a test without a key 1`] = `
VestTest {
"fieldName": "field_name",
"id": "17",
"key": null,
"message": "failure message",
"severity": "error",
"status": "PASSING",
"testFn": [Function],
}
`;

exports[`Test Vest's \`test\` function test params creates a test without a message and with a key 1`] = `
VestTest {
"fieldName": "field_name",
"id": "19",
"key": "keyboardcat",
"severity": "error",
"status": "PASSING",
"testFn": [Function],
}
`;

exports[`Test Vest's \`test\` function test params creates a test without a message and without a key 1`] = `
VestTest {
"fieldName": "field_name",
"id": "15",
"key": null,
"severity": "error",
"status": "PASSING",
"testFn": [Function],
}
`;
Loading

0 comments on commit b0a9a14

Please sign in to comment.