From 3d1d0e424a057d9b59f7f1aec8bfa9e1a16c1057 Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Thu, 16 Apr 2026 21:01:35 +0200 Subject: [PATCH 1/3] test(entity): refactor to Vitest type tests --- .../spec/types/entity_selectors.types.spec.ts | 41 ++++------ .../spec/types/entity_state.types.spec.ts | 75 ++++++++----------- modules/entity/spec/types/utils.ts | 13 ---- 3 files changed, 46 insertions(+), 83 deletions(-) delete mode 100644 modules/entity/spec/types/utils.ts diff --git a/modules/entity/spec/types/entity_selectors.types.spec.ts b/modules/entity/spec/types/entity_selectors.types.spec.ts index 166f37d0e8..0f1bb02082 100644 --- a/modules/entity/spec/types/entity_selectors.types.spec.ts +++ b/modules/entity/spec/types/entity_selectors.types.spec.ts @@ -1,31 +1,20 @@ -import { expecter } from 'ts-snippet'; -import { compilerOptions } from './utils'; +import { Selector } from '@ngrx/store'; +import { expectTypeOf, describe, it } from 'vitest'; +import { EntitySelectors } from '../..'; describe('EntitySelectors', () => { - const expectSnippet = expecter( - (code) => ` - import { Selector } from '@ngrx/store'; - import { EntitySelectors } from './modules/entity/src/models'; - - ${code} - `, - compilerOptions() - ); - it('is compatible with a dictionary of selectors', () => { - expectSnippet(` - type SelectorsDictionary = Record< - string, - | Selector, unknown> - | ((...args: any[]) => Selector, unknown>) - >; - type ExtendsSelectorsDictionary = T extends SelectorsDictionary - ? true - : false; + type SelectorsDictionary = Record< + string, + | Selector, unknown> + | ((...args: any[]) => Selector, unknown>) + >; + type ExtendsSelectorsDictionary = T extends SelectorsDictionary + ? true + : false; - let result: ExtendsSelectorsDictionary< - EntitySelectors> - >; - `).toInfer('result', 'true'); + expectTypeOf< + ExtendsSelectorsDictionary>> + >().toEqualTypeOf(); }); -}, 8_000); +}); diff --git a/modules/entity/spec/types/entity_state.types.spec.ts b/modules/entity/spec/types/entity_state.types.spec.ts index 24d831bbf5..1129bf119d 100644 --- a/modules/entity/spec/types/entity_state.types.spec.ts +++ b/modules/entity/spec/types/entity_state.types.spec.ts @@ -1,63 +1,50 @@ -import { expecter } from 'ts-snippet'; -import { compilerOptions } from './utils'; +import { expectTypeOf, describe, it } from 'vitest'; +import { createEntityAdapter, EntityAdapter, EntityState } from '../..'; -describe('EntityState Types', () => { - const expectSnippet = expecter( - (code) => ` - import { EntityState, createEntityAdapter, EntityAdapter } from '@ngrx/entity'; - - interface Book { - id: string; - title: string; - } +interface Book { + id: string; + title: string; +} - interface BookState extends EntityState { - selectedBookId: string | null; - } +interface BookState extends EntityState { + selectedBookId: string | null; +} - export const adapter: EntityAdapter = createEntityAdapter(); - ${code} - `, - compilerOptions() - ); +const adapter: EntityAdapter = createEntityAdapter(); +describe('EntityState Types', () => { describe('getInitialState', () => { it('can set the initial state', () => { - expectSnippet(` - export const initialState: BookState = adapter.getInitialState({ - selectedBookId: '1', - }); + const initialState: BookState = adapter.getInitialState({ + selectedBookId: '1', + }); - `).toSucceed(); + expectTypeOf(initialState).toEqualTypeOf(); }); it('can set the initial state with additional properties', () => { - expectSnippet(` - export const initialState: BookState = adapter.getInitialState({ - selectedBookId: '1', - }); + const initialState: BookState = adapter.getInitialState({ + selectedBookId: '1', + }); - `).toSucceed(); + expectTypeOf(initialState).toEqualTypeOf(); }); it('throws when setting the initial state with unknown properties', () => { - expectSnippet(` - export const initialState: BookState = adapter.getInitialState({ - selectedBookId: '1', - otherProperty: 'value', - }); - `).toFail( - /Object literal may only specify known properties, and 'otherProperty' does not exist in type 'Omit>'/i - ); + adapter.getInitialState({ + selectedBookId: '1', + // @ts-expect-error Object literal may only specify known properties, and 'otherProperty' does not exist in type 'Omit>' + otherProperty: 'value', + }); }); it('can set the initial state with unknown properties when the state is untyped', () => { - expectSnippet(` - export const initialState = adapter.getInitialState({ - selectedBookId: '1', - otherProperty: 'value', - }); - `).toSucceed(); + const initialState = adapter.getInitialState({ + selectedBookId: '1', + otherProperty: 'value', + }); + + expectTypeOf(initialState).toExtend>(); }); }); -}, 8_000); +}); diff --git a/modules/entity/spec/types/utils.ts b/modules/entity/spec/types/utils.ts deleted file mode 100644 index a1ddc21fa9..0000000000 --- a/modules/entity/spec/types/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const compilerOptions = () => ({ - module: 'preserve', - moduleResolution: 'bundler', - target: 'ES2022', - baseUrl: '.', - ignoreDeprecations: '6.0', - experimentalDecorators: true, - strict: true, - paths: { - '@ngrx/entity': ['./modules/entity'], - '@ngrx/store': ['./modules/store'], - }, -}); From 3f977f1523843e62d500a2927d66f28bb8aa7987 Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Sun, 14 Jun 2026 19:45:05 +0200 Subject: [PATCH 2/3] test: update to vitest --- .../entity/spec/types/entity_adapter.spec.ts | 68 ------------------- .../spec/types/entity_adapter.types.spec.ts | 67 ++++++++++++++++++ 2 files changed, 67 insertions(+), 68 deletions(-) delete mode 100644 modules/entity/spec/types/entity_adapter.spec.ts create mode 100644 modules/entity/spec/types/entity_adapter.types.spec.ts diff --git a/modules/entity/spec/types/entity_adapter.spec.ts b/modules/entity/spec/types/entity_adapter.spec.ts deleted file mode 100644 index 317b86c99b..0000000000 --- a/modules/entity/spec/types/entity_adapter.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { expecter } from 'ts-snippet'; -import { compilerOptions } from './utils'; - -describe('EntityAdapter Types', () => { - const expectSnippet = expecter( - (code) => ` - import { EntityState, createEntityAdapter, EntityAdapter } from '@ngrx/entity'; - - interface EntityWithStringId { - id: string; - } - - interface EntityWithNumberId { - id: number; - } - - interface EntityWithoutId { - key: number; - } - - - ${code} - `, - compilerOptions() - ); - - it('sets the id type to string when the entity has a string id', () => { - expectSnippet(` - export const adapter: EntityAdapter = createEntityAdapter(); - `).toSucceed(); - }); - - it('sets the id type to number when the entity has a number id', () => { - expectSnippet(` - export const adapter: EntityAdapter = createEntityAdapter(); - `).toSucceed(); - }); - - it('sets the id type to string when selectId returns a string', () => { - expectSnippet(` - export const adapter: EntityAdapter = createEntityAdapter({ - selectId: (entity) => entity.id.toString(), - }); - `).toSucceed(); - }); - - it('sets the id type to string | number when the entity has no id and no selectId is provided', () => { - expectSnippet(` - export const adapter: EntityAdapter = createEntityAdapter(); - `).toSucceed(); - }); - - it('sets the id type to correct type if selectId is provided', () => { - expectSnippet(` - export const adapter: EntityAdapter = createEntityAdapter({ - selectId: (entity) => entity.key.toString(), - }); - `).toSucceed(); - }); - - it('sets the id type to string when selectId returns a string', () => { - expectSnippet(` - export const adapter: EntityAdapter = createEntityAdapter({ - selectId: (entity) => entity.id.toString(), - }); - `).toSucceed(); - }); -}, 8_000); diff --git a/modules/entity/spec/types/entity_adapter.types.spec.ts b/modules/entity/spec/types/entity_adapter.types.spec.ts new file mode 100644 index 0000000000..f0bf2241f9 --- /dev/null +++ b/modules/entity/spec/types/entity_adapter.types.spec.ts @@ -0,0 +1,67 @@ +import { expectTypeOf, describe, it } from 'vitest'; +import { createEntityAdapter, EntityAdapter } from '../..'; + +interface EntityWithStringId { + id: string; +} + +interface EntityWithNumberId { + id: number; +} + +interface EntityWithoutId { + key: number; +} + +describe('EntityAdapter Types', () => { + it('sets the id type to string when the entity has a string id', () => { + const adapter = createEntityAdapter(); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to number when the entity has a number id', () => { + const adapter = createEntityAdapter(); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to string when selectId returns a string', () => { + const adapter = createEntityAdapter({ + selectId: (entity) => entity.id.toString(), + }); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to string | number when the entity has no id and no selectId is provided', () => { + const adapter = createEntityAdapter(); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to correct type if selectId is provided', () => { + const adapter = createEntityAdapter({ + selectId: (entity) => entity.key.toString(), + }); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('throws when selectId returns an invalid id type', () => { + createEntityAdapter({ + // @ts-expect-error Type 'boolean' is not assignable to type 'string | number' + selectId: (entity) => entity.key > 0, + }); + }); +}); From ac64e1bfe5e16e79a83c4bd86451429a4803dcfa Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:43:41 +0200 Subject: [PATCH 3/3] test(entity): split type tests into .test-d.ts and hybrid .spec.ts files Move pure type-only tests (using expectTypeOf) to .test-d.ts files so they are only compiled and never executed. Keep hybrid tests that use @ts-expect-error in .types.spec.ts files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../spec/types/entity_adapter.test-d.ts | 60 +++++++++++++++++++ .../spec/types/entity_adapter.types.spec.ts | 56 +---------------- ...pes.spec.ts => entity_selectors.test-d.ts} | 0 .../entity/spec/types/entity_state.test-d.ts | 42 +++++++++++++ .../spec/types/entity_state.types.spec.ts | 27 +-------- 5 files changed, 105 insertions(+), 80 deletions(-) create mode 100644 modules/entity/spec/types/entity_adapter.test-d.ts rename modules/entity/spec/types/{entity_selectors.types.spec.ts => entity_selectors.test-d.ts} (100%) create mode 100644 modules/entity/spec/types/entity_state.test-d.ts diff --git a/modules/entity/spec/types/entity_adapter.test-d.ts b/modules/entity/spec/types/entity_adapter.test-d.ts new file mode 100644 index 0000000000..0d6a51fc05 --- /dev/null +++ b/modules/entity/spec/types/entity_adapter.test-d.ts @@ -0,0 +1,60 @@ +import { expectTypeOf, describe, it } from 'vitest'; +import { createEntityAdapter, EntityAdapter } from '../..'; + +interface EntityWithStringId { + id: string; +} + +interface EntityWithNumberId { + id: number; +} + +interface EntityWithoutId { + key: number; +} + +describe('EntityAdapter Types', () => { + it('sets the id type to string when the entity has a string id', () => { + const adapter = createEntityAdapter(); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to number when the entity has a number id', () => { + const adapter = createEntityAdapter(); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to string when selectId returns a string', () => { + const adapter = createEntityAdapter({ + selectId: (entity) => entity.id.toString(), + }); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to string | number when the entity has no id and no selectId is provided', () => { + const adapter = createEntityAdapter(); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); + + it('sets the id type to correct type if selectId is provided', () => { + const adapter = createEntityAdapter({ + selectId: (entity) => entity.key.toString(), + }); + + expectTypeOf(adapter).toEqualTypeOf< + EntityAdapter + >(); + }); +}); diff --git a/modules/entity/spec/types/entity_adapter.types.spec.ts b/modules/entity/spec/types/entity_adapter.types.spec.ts index f0bf2241f9..c7ccb70543 100644 --- a/modules/entity/spec/types/entity_adapter.types.spec.ts +++ b/modules/entity/spec/types/entity_adapter.types.spec.ts @@ -1,63 +1,11 @@ -import { expectTypeOf, describe, it } from 'vitest'; -import { createEntityAdapter, EntityAdapter } from '../..'; - -interface EntityWithStringId { - id: string; -} - -interface EntityWithNumberId { - id: number; -} +import { it, describe } from 'vitest'; +import { createEntityAdapter } from '../..'; interface EntityWithoutId { key: number; } describe('EntityAdapter Types', () => { - it('sets the id type to string when the entity has a string id', () => { - const adapter = createEntityAdapter(); - - expectTypeOf(adapter).toEqualTypeOf< - EntityAdapter - >(); - }); - - it('sets the id type to number when the entity has a number id', () => { - const adapter = createEntityAdapter(); - - expectTypeOf(adapter).toEqualTypeOf< - EntityAdapter - >(); - }); - - it('sets the id type to string when selectId returns a string', () => { - const adapter = createEntityAdapter({ - selectId: (entity) => entity.id.toString(), - }); - - expectTypeOf(adapter).toEqualTypeOf< - EntityAdapter - >(); - }); - - it('sets the id type to string | number when the entity has no id and no selectId is provided', () => { - const adapter = createEntityAdapter(); - - expectTypeOf(adapter).toEqualTypeOf< - EntityAdapter - >(); - }); - - it('sets the id type to correct type if selectId is provided', () => { - const adapter = createEntityAdapter({ - selectId: (entity) => entity.key.toString(), - }); - - expectTypeOf(adapter).toEqualTypeOf< - EntityAdapter - >(); - }); - it('throws when selectId returns an invalid id type', () => { createEntityAdapter({ // @ts-expect-error Type 'boolean' is not assignable to type 'string | number' diff --git a/modules/entity/spec/types/entity_selectors.types.spec.ts b/modules/entity/spec/types/entity_selectors.test-d.ts similarity index 100% rename from modules/entity/spec/types/entity_selectors.types.spec.ts rename to modules/entity/spec/types/entity_selectors.test-d.ts diff --git a/modules/entity/spec/types/entity_state.test-d.ts b/modules/entity/spec/types/entity_state.test-d.ts new file mode 100644 index 0000000000..08dd13a3d9 --- /dev/null +++ b/modules/entity/spec/types/entity_state.test-d.ts @@ -0,0 +1,42 @@ +import { expectTypeOf, describe, it } from 'vitest'; +import { createEntityAdapter, EntityAdapter, EntityState } from '../..'; + +interface Book { + id: string; + title: string; +} + +interface BookState extends EntityState { + selectedBookId: string | null; +} + +const adapter: EntityAdapter = createEntityAdapter(); + +describe('EntityState Types', () => { + describe('getInitialState', () => { + it('can set the initial state', () => { + const initialState: BookState = adapter.getInitialState({ + selectedBookId: '1', + }); + + expectTypeOf(initialState).toEqualTypeOf(); + }); + + it('can set the initial state with additional properties', () => { + const initialState: BookState = adapter.getInitialState({ + selectedBookId: '1', + }); + + expectTypeOf(initialState).toEqualTypeOf(); + }); + + it('can set the initial state with unknown properties when the state is untyped', () => { + const initialState = adapter.getInitialState({ + selectedBookId: '1', + otherProperty: 'value', + }); + + expectTypeOf(initialState).toExtend>(); + }); + }); +}); diff --git a/modules/entity/spec/types/entity_state.types.spec.ts b/modules/entity/spec/types/entity_state.types.spec.ts index 1129bf119d..7ec8e016b9 100644 --- a/modules/entity/spec/types/entity_state.types.spec.ts +++ b/modules/entity/spec/types/entity_state.types.spec.ts @@ -1,4 +1,4 @@ -import { expectTypeOf, describe, it } from 'vitest'; +import { it, describe } from 'vitest'; import { createEntityAdapter, EntityAdapter, EntityState } from '../..'; interface Book { @@ -14,22 +14,6 @@ const adapter: EntityAdapter = createEntityAdapter(); describe('EntityState Types', () => { describe('getInitialState', () => { - it('can set the initial state', () => { - const initialState: BookState = adapter.getInitialState({ - selectedBookId: '1', - }); - - expectTypeOf(initialState).toEqualTypeOf(); - }); - - it('can set the initial state with additional properties', () => { - const initialState: BookState = adapter.getInitialState({ - selectedBookId: '1', - }); - - expectTypeOf(initialState).toEqualTypeOf(); - }); - it('throws when setting the initial state with unknown properties', () => { adapter.getInitialState({ selectedBookId: '1', @@ -37,14 +21,5 @@ describe('EntityState Types', () => { otherProperty: 'value', }); }); - - it('can set the initial state with unknown properties when the state is untyped', () => { - const initialState = adapter.getInitialState({ - selectedBookId: '1', - otherProperty: 'value', - }); - - expectTypeOf(initialState).toExtend>(); - }); }); });