From b6400afd6d3bc4f124a1d5f623f8d82dcd06d387 Mon Sep 17 00:00:00 2001 From: Evyatar Date: Wed, 17 Jan 2024 01:09:00 +0200 Subject: [PATCH] fix(vest): eager mode works inside of sub-isolates (#1134) --- .../schema/__tests__/shape&loose.test.ts | 8 +++--- .../core/StateMachines/CommonStateMachine.ts | 5 ++-- packages/vest/src/core/VestBus/VestBus.ts | 2 +- .../vest/src/core/__tests__/runtime.test.ts | 1 + packages/vest/src/core/isolate/VestIsolate.ts | 6 ++--- .../vest/src/hooks/__tests__/mode.test.ts | 23 ++++++++++++++++ .../vest/src/hooks/focused/useIsExcluded.ts | 3 +-- .../__tests__/hasFailuresByTestObject.test.ts | 26 +++++++++++-------- .../__tests__/summaryFailures.test.ts | 2 ++ .../selectors/shouldAddValidProperty.ts | 2 +- .../selectors/useProduceSuiteSummary.ts | 22 ++++++++-------- packages/vest/src/vest.ts | 3 +-- .../vestjs-runtime/src/Isolate/Isolate.ts | 18 +++++++------ packages/vestjs-runtime/src/IsolateWalker.ts | 26 +++++++++---------- 14 files changed, 89 insertions(+), 58 deletions(-) diff --git a/packages/n4s/src/plugins/schema/__tests__/shape&loose.test.ts b/packages/n4s/src/plugins/schema/__tests__/shape&loose.test.ts index c9ebbd58e..caff29e3e 100644 --- a/packages/n4s/src/plugins/schema/__tests__/shape&loose.test.ts +++ b/packages/n4s/src/plugins/schema/__tests__/shape&loose.test.ts @@ -14,7 +14,7 @@ describe.each(['loose', 'shape'])('enforce.%s', (methodName: string) => { enforce[methodName]({ username: enforce.isString(), age: enforce.isNumber().gt(18), - }).run({ username: 'ealush', age: 31 }) + }).run({ username: 'ealush', age: 31 }), ).toEqual(ruleReturn.passing()); }); @@ -23,7 +23,7 @@ describe.each(['loose', 'shape'])('enforce.%s', (methodName: string) => { enforce[methodName]({ username: enforce.isString(), age: enforce.isNumber().gt(18), - }).run({ username: null, age: 0 }) + }).run({ username: null, age: 0 }), ).toEqual(ruleReturn.failing()); }); @@ -48,7 +48,7 @@ describe.each(['loose', 'shape'])('enforce.%s', (methodName: string) => { state: 'NY', zip: 12345, }, - }) + }), ).toEqual(ruleReturn.passing()); }); it('Should return a failing return when tests are invalid', () => { @@ -69,7 +69,7 @@ describe.each(['loose', 'shape'])('enforce.%s', (methodName: string) => { street: '123 Main St', city: null, }, - }) + }), ).toEqual(ruleReturn.failing()); }); }); diff --git a/packages/vest/src/core/StateMachines/CommonStateMachine.ts b/packages/vest/src/core/StateMachines/CommonStateMachine.ts index 90e286380..fdfb1adaa 100644 --- a/packages/vest/src/core/StateMachines/CommonStateMachine.ts +++ b/packages/vest/src/core/StateMachines/CommonStateMachine.ts @@ -3,11 +3,12 @@ import { TIsolate } from 'vestjs-runtime'; export const CommonStates = { PENDING: 'PENDING', + INITIAL: 'INITIAL', }; -export const State = { +const State = { [CommonStates.PENDING]: CommonStates.PENDING, - INITIAL: 'INITIAL', + [CommonStates.INITIAL]: CommonStates.INITIAL, DONE: 'DONE', }; diff --git a/packages/vest/src/core/VestBus/VestBus.ts b/packages/vest/src/core/VestBus/VestBus.ts index 3546baef2..311a19ea0 100644 --- a/packages/vest/src/core/VestBus/VestBus.ts +++ b/packages/vest/src/core/VestBus/VestBus.ts @@ -1,4 +1,3 @@ -import { SuiteWalker } from 'SuiteWalker'; import { CB, ValueOf } from 'vest-utils'; import { Bus, RuntimeEvents, TIsolate } from 'vestjs-runtime'; @@ -11,6 +10,7 @@ import { useResetSuite, } from 'Runtime'; import { TFieldName } from 'SuiteResultTypes'; +import { SuiteWalker } from 'SuiteWalker'; import { TestWalker } from 'TestWalker'; import { VestTest } from 'VestTest'; import { useOmitOptionalFields } from 'omitOptionalFields'; diff --git a/packages/vest/src/core/__tests__/runtime.test.ts b/packages/vest/src/core/__tests__/runtime.test.ts index 6f12f4f3c..3738151be 100644 --- a/packages/vest/src/core/__tests__/runtime.test.ts +++ b/packages/vest/src/core/__tests__/runtime.test.ts @@ -65,6 +65,7 @@ describe('useLoadSuite', () => { function genDump() { const suite = vest.create(() => { + vest.mode(vest.Modes.ALL); vest.skip('t5'); vest.test('t1', () => false); diff --git a/packages/vest/src/core/isolate/VestIsolate.ts b/packages/vest/src/core/isolate/VestIsolate.ts index 7364b9123..99678422a 100644 --- a/packages/vest/src/core/isolate/VestIsolate.ts +++ b/packages/vest/src/core/isolate/VestIsolate.ts @@ -1,4 +1,4 @@ -import { nonnullish, TStateMachineApi } from 'vest-utils'; +import { TStateMachineApi } from 'vest-utils'; import { TIsolate } from 'vestjs-runtime'; import { CommonStateMachine, CommonStates } from 'CommonStateMachine'; @@ -7,14 +7,14 @@ export class VestIsolate { static stateMachine: TStateMachineApi = CommonStateMachine; static getStatus(isolate: TIsolate): string { - return nonnullish(isolate.status); + return isolate.status ?? CommonStates.INITIAL; } static setStatus(isolate: TIsolate, status: string, payload?: any): void { isolate.status = this.stateMachine.staticTransition( VestIsolate.getStatus(isolate), status, - payload + payload, ); } diff --git a/packages/vest/src/hooks/__tests__/mode.test.ts b/packages/vest/src/hooks/__tests__/mode.test.ts index d37515400..6dd6a1472 100644 --- a/packages/vest/src/hooks/__tests__/mode.test.ts +++ b/packages/vest/src/hooks/__tests__/mode.test.ts @@ -178,6 +178,29 @@ describe('mode', () => { expect(suite.get().getErrors('field_1')).toEqual([]); }); }); + + describe('When in a nested block', () => { + it('Should follow the same behavior as if it was not nested', () => { + const suite = create(() => { + group('group_1', () => { + dummyTest.failing('field_1', 'first-of-field_1'); + dummyTest.failing('field_1', 'second-of-field_1'); + dummyTest.failing('field_2', 'first-of-field_2'); + dummyTest.failing('field_2', 'second-of-field_2'); + dummyTest.failing('field_3', 'first-of-field_3'); + dummyTest.failing('field_3', 'second-of-field_3'); + }); + }); + expect(suite.get().testCount).toBe(0); // sanity + suite(); + + expect(suite.get().testCount).toBe(3); + expect(suite.get().errorCount).toBe(3); + expect(suite.get().getErrors('field_1')).toEqual(['first-of-field_1']); + expect(suite.get().getErrors('field_2')).toEqual(['first-of-field_2']); + expect(suite.get().getErrors('field_3')).toEqual(['first-of-field_3']); + }); + }); }); describe('All', () => { diff --git a/packages/vest/src/hooks/focused/useIsExcluded.ts b/packages/vest/src/hooks/focused/useIsExcluded.ts index 81ab2a581..702f0d697 100644 --- a/packages/vest/src/hooks/focused/useIsExcluded.ts +++ b/packages/vest/src/hooks/focused/useIsExcluded.ts @@ -10,7 +10,7 @@ import { useHasOnliedTests } from 'useHasOnliedTests'; //Checks whether a certain test profile excluded by any of the exclusion groups. function useClosestMatchingFocus( - testObject: TIsolateTest + testObject: TIsolateTest, ): Nullable { return Walker.findClosest(testObject, (child: TIsolate) => { if (!FocusSelectors.isIsolateFocused(child)) return false; @@ -21,7 +21,6 @@ function useClosestMatchingFocus( }); } -// eslint-disable-next-line complexity, max-statements export function useIsExcluded(testObject: TIsolateTest): boolean { const { fieldName } = VestTest.getData(testObject); diff --git a/packages/vest/src/suiteResult/selectors/__tests__/hasFailuresByTestObject.test.ts b/packages/vest/src/suiteResult/selectors/__tests__/hasFailuresByTestObject.test.ts index 8681915f4..7d261210c 100644 --- a/packages/vest/src/suiteResult/selectors/__tests__/hasFailuresByTestObject.test.ts +++ b/packages/vest/src/suiteResult/selectors/__tests__/hasFailuresByTestObject.test.ts @@ -1,8 +1,8 @@ import { faker } from '@faker-js/faker'; -import { VestTest } from 'VestTest'; import { TIsolateTest } from 'IsolateTest'; import { Severity } from 'Severity'; +import { VestTest } from 'VestTest'; import { hasFailuresByTestObject } from 'hasFailuresByTestObjects'; import { mockIsolateTest } from 'vestMocks'; @@ -22,10 +22,10 @@ describe('hasFailuresByTestObject', () => { it('Should return false', () => { expect(hasFailuresByTestObject(testObject, Severity.ERRORS)).toBe(false); expect(hasFailuresByTestObject(testObject, Severity.WARNINGS)).toBe( - false + false, ); expect( - hasFailuresByTestObject(testObject, Severity.ERRORS, fieldName) + hasFailuresByTestObject(testObject, Severity.ERRORS, fieldName), ).toBe(false); }); }); @@ -38,11 +38,11 @@ describe('hasFailuresByTestObject', () => { describe('When non matching severity profile', () => { it('should return false', () => { expect(hasFailuresByTestObject(testObject, Severity.WARNINGS)).toBe( - false + false, ); VestTest.warn(testObject); expect(hasFailuresByTestObject(testObject, Severity.ERRORS)).toBe( - false + false, ); }); }); @@ -50,11 +50,11 @@ describe('hasFailuresByTestObject', () => { describe('When matching severity profile', () => { it('Should return true', () => { expect(hasFailuresByTestObject(testObject, Severity.ERRORS)).toBe( - true + true, ); VestTest.warn(testObject); expect(hasFailuresByTestObject(testObject, Severity.WARNINGS)).toBe( - true + true, ); }); }); @@ -63,7 +63,11 @@ describe('hasFailuresByTestObject', () => { describe('When field name matches', () => { it('should return false', () => { expect( - hasFailuresByTestObject(testObject, Severity.ERRORS, 'non_matching') + hasFailuresByTestObject( + testObject, + Severity.ERRORS, + 'non_matching', + ), ).toBe(false); }); }); @@ -71,15 +75,15 @@ describe('hasFailuresByTestObject', () => { describe('When field name matches', () => { it('Should continue with normal flow', () => { expect(hasFailuresByTestObject(testObject, Severity.WARNINGS)).toBe( - false + false, ); VestTest.warn(testObject); expect(hasFailuresByTestObject(testObject, Severity.ERRORS)).toBe( - false + false, ); VestTest.fail(testObject); expect(hasFailuresByTestObject(testObject, Severity.WARNINGS)).toBe( - true + true, ); }); }); diff --git a/packages/vest/src/suiteResult/selectors/__tests__/summaryFailures.test.ts b/packages/vest/src/suiteResult/selectors/__tests__/summaryFailures.test.ts index e59565112..1d2e1199d 100644 --- a/packages/vest/src/suiteResult/selectors/__tests__/summaryFailures.test.ts +++ b/packages/vest/src/suiteResult/selectors/__tests__/summaryFailures.test.ts @@ -1,4 +1,5 @@ import { TTestSuite } from 'testUtils/TVestMock'; + import * as vest from 'vest'; describe('summaryFailures', () => { @@ -68,6 +69,7 @@ describe('summaryFailures', () => { test('Should add the test group into the error object', () => { suite = vest.create(() => { + vest.mode(vest.Modes.ALL); vest.group('user', () => { vest.test('username', 'uxsername is required', () => false); vest.test('username', 'username is too short', () => false); diff --git a/packages/vest/src/suiteResult/selectors/shouldAddValidProperty.ts b/packages/vest/src/suiteResult/selectors/shouldAddValidProperty.ts index 4966a29e6..aadd394bb 100644 --- a/packages/vest/src/suiteResult/selectors/shouldAddValidProperty.ts +++ b/packages/vest/src/suiteResult/selectors/shouldAddValidProperty.ts @@ -1,4 +1,3 @@ -import { SuiteWalker } from 'SuiteWalker'; import { useIsOptionalFieldApplied } from 'optional'; import { Predicates } from 'vest-utils'; import { VestRuntime } from 'vestjs-runtime'; @@ -8,6 +7,7 @@ import { TIsolateTest } from 'IsolateTest'; import { OptionalFieldTypes } from 'OptionalTypes'; import { Severity } from 'Severity'; import { TFieldName, TGroupName } from 'SuiteResultTypes'; +import { SuiteWalker } from 'SuiteWalker'; import { TestWalker } from 'TestWalker'; import { VestTest } from 'VestTest'; import { diff --git a/packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts b/packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts index a0af0c781..c5ae202d2 100644 --- a/packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts +++ b/packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts @@ -21,7 +21,7 @@ import { export function useProduceSuiteSummary< F extends TFieldName, - G extends TGroupName + G extends TGroupName, >(): SuiteSummary { const summary: SuiteSummary = new SuiteSummary(); @@ -31,12 +31,12 @@ export function useProduceSuiteSummary< summary.errors = appendFailures( Severity.ERRORS, summary.errors, - testObject + testObject, ); summary.warnings = appendFailures( Severity.WARNINGS, summary.warnings, - testObject + testObject, ); }); @@ -48,7 +48,7 @@ export function useProduceSuiteSummary< function appendFailures( key: Severity, failures: SummaryFailure[], - testObject: TIsolateTest + testObject: TIsolateTest, ): SummaryFailure[] { if (VestTest.isOmitted(testObject)) { return failures; @@ -67,7 +67,7 @@ function appendFailures( function useAppendToTest( tests: Tests, - testObject: TIsolateTest + testObject: TIsolateTest, ): Tests { const fieldName = VestTest.getData(testObject).fieldName; @@ -90,7 +90,7 @@ function useAppendToTest( */ function useAppendToGroup( groups: Groups, - testObject: TIsolateTest + testObject: TIsolateTest, ): Groups { const { groupName, fieldName } = VestTest.getData(testObject); @@ -105,7 +105,7 @@ function useAppendToGroup( newGroups[groupName] = newGroups[groupName] || {}; newGroups[groupName][fieldName] = appendTestObject( newGroups[groupName][fieldName], - testObject + testObject, ); newGroups[groupName][fieldName].valid = @@ -120,7 +120,7 @@ function useAppendToGroup( * Counts the failed tests and adds global counters */ function countOverallStates( - summary: SuiteSummary + summary: SuiteSummary, ): SuiteSummary { for (const test in summary.tests) { summary.errorCount += summary.tests[test].errorCount; @@ -137,14 +137,14 @@ function countOverallStates( // eslint-disable-next-line max-statements, complexity function appendTestObject( summaryKey: Maybe, - testObject: TIsolateTest + testObject: TIsolateTest, ): SingleTestSummary { const { message } = VestTest.getData(testObject); // Let's first create a new object, so we don't mutate the original. const nextSummaryKey = defaultTo( summaryKey ? { ...summaryKey } : null, - baseTestStats + baseTestStats, ); // If the test is not actionable, we don't need to append it to the summary. @@ -174,7 +174,7 @@ function appendTestObject( nextSummaryKey[countKey]++; if (message) { nextSummaryKey[severity] = (nextSummaryKey[severity] || []).concat( - message + message, ); } } diff --git a/packages/vest/src/vest.ts b/packages/vest/src/vest.ts index d8f2a3957..b6c05e954 100644 --- a/packages/vest/src/vest.ts +++ b/packages/vest/src/vest.ts @@ -1,7 +1,6 @@ import { enforce } from 'n4s'; - -// eslint-disable-next-line import/order -- will handle this circular dep issue later. import { optional } from 'optional'; + import { Modes } from 'Modes'; import type { SuiteResult, diff --git a/packages/vestjs-runtime/src/Isolate/Isolate.ts b/packages/vestjs-runtime/src/Isolate/Isolate.ts index 3bd3cbd17..b3c6bf669 100644 --- a/packages/vestjs-runtime/src/Isolate/Isolate.ts +++ b/packages/vestjs-runtime/src/Isolate/Isolate.ts @@ -33,26 +33,28 @@ export class Isolate { type: string, callback: CB, payload: Maybe = undefined, - key?: IsolateKey + key?: IsolateKey, ): TIsolate { const parent = VestRuntime.useIsolate(); const newCreatedNode = IsolateMutator.setParent( baseIsolate(type, payload, key), - parent + parent, ); const nextIsolateChild = Reconciler.reconcile(newCreatedNode); const localHistoryNode = VestRuntime.useHistoryIsolateAtCurrentPosition(); - const output = Object.is(nextIsolateChild, newCreatedNode) + const shouldRunNew = Object.is(nextIsolateChild, newCreatedNode); + + VestRuntime.addNodeToHistory(nextIsolateChild); + + const output = shouldRunNew ? useRunAsNew(localHistoryNode, newCreatedNode, callback) : nextIsolateChild.output; - IsolateMutator.setParent(nextIsolateChild, parent); IsolateMutator.saveOutput(nextIsolateChild, output); - VestRuntime.addNodeToHistory(nextIsolateChild); return nextIsolateChild as TIsolate; } @@ -73,7 +75,7 @@ export class Isolate { function useRunAsNew( localHistoryNode: Nullable, current: TIsolate, - callback: CB + callback: CB, ): ReturnType { const runtimeRoot = VestRuntime.useRuntimeRoot(); const emit = useEmit(); @@ -104,7 +106,7 @@ function useRunAsNew( } return output; - } + }, ); current.output = output; @@ -114,7 +116,7 @@ function useRunAsNew( function baseIsolate( type: string, payload: Maybe = undefined, - key: IsolateKey = null + key: IsolateKey = null, ): TIsolate { const { allowReorder, status, ...data } = payload ?? {}; return { diff --git a/packages/vestjs-runtime/src/IsolateWalker.ts b/packages/vestjs-runtime/src/IsolateWalker.ts index 9c2ba1476..42c11c1d0 100644 --- a/packages/vestjs-runtime/src/IsolateWalker.ts +++ b/packages/vestjs-runtime/src/IsolateWalker.ts @@ -9,7 +9,7 @@ type VisitOnlyPredicate = (isolate: TIsolate) => boolean; export function walk( startNode: TIsolate, callback: (isolate: TIsolate, breakout: CB) => void, - visitOnly?: VisitOnlyPredicate + visitOnly?: VisitOnlyPredicate, ): void { // If the startNode has no children, there is nothing to walk. if (isNullish(startNode.children)) { @@ -43,7 +43,7 @@ export function walk( breakout(); }); }, - visitOnly + visitOnly, ); } @@ -57,7 +57,7 @@ export function walk( export function some( startNode: TIsolate, predicate: (node: TIsolate) => boolean, - visitOnly?: VisitOnlyPredicate + visitOnly?: VisitOnlyPredicate, ): boolean { let hasMatch = false; @@ -70,7 +70,7 @@ export function some( hasMatch = true; } }, - visitOnly + visitOnly, ); return hasMatch; @@ -86,7 +86,7 @@ export function has(startNode: TIsolate, match: VisitOnlyPredicate): boolean { // and returns the first direct descendant that satisfies the predicate export function findClosest( startNode: TIsolate, - predicate: (node: TIsolate) => boolean + predicate: (node: TIsolate) => boolean, ): Nullable { let found: Nullable = null; let current: Nullable = startNode; @@ -109,7 +109,7 @@ export function findClosest( export function find( startNode: TIsolate, predicate: (node: TIsolate) => boolean, - visitOnly?: VisitOnlyPredicate + visitOnly?: VisitOnlyPredicate, ): Nullable { let found = null; @@ -122,7 +122,7 @@ export function find( found = node; } }, - visitOnly + visitOnly, ); return found; @@ -133,7 +133,7 @@ export function find( export function every( startNode: TIsolate, predicate: (node: TIsolate) => boolean, - visitOnly?: VisitOnlyPredicate + visitOnly?: VisitOnlyPredicate, ): boolean { let hasMatch = true; walk( @@ -144,7 +144,7 @@ export function every( hasMatch = false; } }, - visitOnly + visitOnly, ); return hasMatch; @@ -156,7 +156,7 @@ export function every( export function pluck( startNode: TIsolate, predicate: (node: TIsolate) => boolean, - visitOnly?: VisitOnlyPredicate + visitOnly?: VisitOnlyPredicate, ): void { walk( startNode, @@ -165,7 +165,7 @@ export function pluck( IsolateMutator.removeChild(node.parent, node); } }, - visitOnly + visitOnly, ); } @@ -173,7 +173,7 @@ export function pluck( //startNode that satisfies the given predicate function. export function closest( startNode: TIsolate, - predicate: (node: TIsolate) => boolean + predicate: (node: TIsolate) => boolean, ): Nullable { let current: Nullable = startNode; do { @@ -189,7 +189,7 @@ export function closest( // given startNode that satisfies the given predicate function exists. export function closestExists( startNode: TIsolate, - predicate: (node: TIsolate) => boolean + predicate: (node: TIsolate) => boolean, ): boolean { return !!closest(startNode, predicate); }