Skip to content

Commit 73823e8

Browse files
committed
feat: improve scenario api
1 parent 1ee8b15 commit 73823e8

File tree

6 files changed

+102
-35
lines changed

6 files changed

+102
-35
lines changed

library/ngtx.ts

+41-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import { ComponentFixture } from '@angular/core/testing';
22
import { NgtxElement, NgtxFixture } from './core';
33
import { createDeclarativeTestingApi } from './declarative-testing/declarative-testing';
4+
import {
5+
NgtxScenarioSetupFnMarker,
6+
NgtxScenarioViewSetupFnMarker,
7+
} from './scenario-testing/symbols';
8+
import {
9+
ComponentFixtureRef,
10+
ScenarioSetupFn,
11+
ScenarioViewSetupFn,
12+
} from './scenario-testing/types';
413
import { NgtxSuite, UseFixtureOptions } from './types';
514

615
/**
@@ -24,7 +33,7 @@ import { NgtxSuite, UseFixtureOptions } from './types';
2433
* ---
2534
* @param suite The test suite to be enriched with ngtx helper features.
2635
*/
27-
export function ngtx<T = any>(suite: (ngtx: NgtxSuite<T>) => void) {
36+
function _ngtx<T = any>(suite: (ngtx: NgtxSuite<T>) => void) {
2837
const ngtxFixture = new NgtxFixture();
2938
const When = createDeclarativeTestingApi(ngtxFixture);
3039

@@ -55,3 +64,34 @@ export function ngtx<T = any>(suite: (ngtx: NgtxSuite<T>) => void) {
5564

5665
return () => suite(ngtxImpl);
5766
}
67+
68+
export const ngtx = Object.assign(_ngtx, {
69+
scenario: {
70+
envSetupFn,
71+
viewSetupFn,
72+
},
73+
is,
74+
});
75+
76+
function envSetupFn(fn: () => unknown) {
77+
return Object.assign(fn, { [NgtxScenarioSetupFnMarker]: true });
78+
}
79+
function viewSetupFn(fn: (fixtureRef: ComponentFixtureRef) => unknown) {
80+
return Object.assign(fn, { [NgtxScenarioViewSetupFnMarker]: true });
81+
}
82+
83+
function is(obj: any, type: 'scenarioSetupFn'): obj is ScenarioSetupFn;
84+
function is(
85+
obj: any,
86+
type: 'scenarioViewSetupFn',
87+
): obj is ScenarioViewSetupFn<any>;
88+
function is(obj: any, type: 'scenarioSetupFn' | 'scenarioViewSetupFn') {
89+
switch (type) {
90+
case 'scenarioSetupFn':
91+
return obj?.[NgtxScenarioSetupFnMarker] === true;
92+
case 'scenarioViewSetupFn':
93+
return obj?.[NgtxScenarioViewSetupFnMarker] === true;
94+
default:
95+
return false;
96+
}
97+
}

library/scenario-testing/scenario-harnesses.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { Type } from 'ng-mocks';
22
import { TypedDebugElement } from '../types';
33
import { NgtxScenarioTestEnvironment } from './scenario-testing';
4+
import { NgtxScenarioTestIsAssertionNegated } from './symbols';
45
import { ComponentFixtureRef } from './types';
56

6-
const NgtxIsAssertionNegatedFlag = Symbol('IsNegated');
7-
87
export class ScenarioTestingHarnessBase<Html extends HTMLElement, Component> {
9-
[NgtxIsAssertionNegatedFlag] = false;
8+
private [NgtxScenarioTestIsAssertionNegated] = false;
109

1110
public get isAssertionNegated() {
12-
return this[NgtxIsAssertionNegatedFlag];
11+
return this[NgtxScenarioTestIsAssertionNegated];
1312
}
13+
1414
public readonly not: Omit<typeof this, 'not'> = new Proxy(this, {
1515
get: (_, property) => {
1616
// TODO: document that constructors of Harnesses are not allowed to be overridden!
@@ -21,8 +21,8 @@ export class ScenarioTestingHarnessBase<Html extends HTMLElement, Component> {
2121
this.name,
2222
);
2323

24-
// hint: toggling isNegated
25-
target[NgtxIsAssertionNegatedFlag] = !this[NgtxIsAssertionNegatedFlag];
24+
target[NgtxScenarioTestIsAssertionNegated] =
25+
!this[NgtxScenarioTestIsAssertionNegated];
2626

2727
return (target as any)[property];
2828
},

library/scenario-testing/scenario-testing.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import {
66
import { By } from '@angular/platform-browser';
77
import { Type } from 'ng-mocks';
88
import { NGTX_GLOBAL_CONFIG } from '../global-config';
9+
import { ngtx } from '../ngtx';
910
import { TypedDebugElement } from '../types';
1011
import {
1112
ComponentFixtureRef,
1213
NgtxScenarioInitProps,
1314
NgtxScenarioProps,
1415
NgtxTestingFrameworkAdapter,
16+
ScenarioSetupFn,
1517
ScenarioTestDefinition,
18+
ScenarioViewSetupFn,
1619
} from './types';
1720

1821
export class NgtxScenarioTestEnvironment<T> {
@@ -97,25 +100,30 @@ export class NgtxTestScenario<T = any> {
97100
private readonly _testEnvironment: NgtxScenarioTestEnvironment<T>,
98101
private readonly _moduleConfig: TestModuleMetadata,
99102
private readonly _componentType: Type<T>,
100-
private readonly _modificationsBeforeComponentCreation: (() => void)[],
101-
private readonly _modificationsAfterComponentCreation: ((
102-
fxRef: ComponentFixtureRef<T>,
103-
) => void)[],
103+
private readonly _modificationsBeforeComponentCreation: ScenarioSetupFn[],
104+
private readonly _modificationsAfterComponentCreation: ScenarioViewSetupFn<T>[],
104105
private readonly tests: ScenarioTestDefinition<T>[],
105106
) {}
106107

107-
configure(...mods: (() => void)[]): NgtxTestScenario<T> {
108+
setup(
109+
...mods: (ScenarioSetupFn | ScenarioViewSetupFn<T>)[]
110+
): NgtxTestScenario<T> {
111+
const scenarioMods = mods.filter((mod) => ngtx.is(mod, 'scenarioSetupFn'));
112+
const viewMods = mods.filter((mod) => ngtx.is(mod, 'scenarioViewSetupFn'));
113+
108114
const scenario = NgtxTestScenario.from(
109115
{
110116
componentType: this._componentType,
111117
description: this._description,
112118
moduleConfig: this._moduleConfig,
113119
modificationsBeforeComponentCreation: [
114120
...this._modificationsBeforeComponentCreation,
115-
...mods,
121+
...scenarioMods,
122+
],
123+
modificationsAfterComponentCreation: [
124+
...this._modificationsAfterComponentCreation,
125+
...viewMods,
116126
],
117-
modificationsAfterComponentCreation:
118-
this._modificationsAfterComponentCreation,
119127
tests: this.tests,
120128
},
121129
this._testEnvironment,
@@ -124,9 +132,7 @@ export class NgtxTestScenario<T = any> {
124132
return scenario;
125133
}
126134

127-
whenComponentReady(
128-
...mods: ((fxRef: ComponentFixtureRef<T>) => void)[]
129-
): NgtxTestScenario<T> {
135+
whenComponentReady(...mods: ScenarioViewSetupFn<T>[]): NgtxTestScenario<T> {
130136
const scenario = NgtxTestScenario.from(
131137
{
132138
componentType: this._componentType,

library/scenario-testing/symbols.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const NgtxScenarioTestIsAssertionNegated = Symbol('IsNegated');
2+
export const NgtxScenarioSetupFnMarker = Symbol('NgtxSetupFnMarker');
3+
export const NgtxScenarioViewSetupFnMarker = Symbol('NgtxSetupFnMarker');

library/scenario-testing/types.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { Type } from '@angular/core';
22
import { ComponentFixture, TestModuleMetadata } from '@angular/core/testing';
3+
import {
4+
NgtxScenarioSetupFnMarker,
5+
NgtxScenarioViewSetupFnMarker,
6+
} from './symbols';
37

48
export type ComponentFixtureRef<T = any> = () => ComponentFixture<T>;
59
export type ScenarioTestDefinition<T> = (
@@ -14,11 +18,19 @@ export type NgtxScenarioInitProps<T> = {
1418
componentType: Type<T>;
1519
moduleConfig: TestModuleMetadata;
1620
};
21+
1722
export type NgtxScenarioProps<T> = NgtxScenarioInitProps<T> & {
1823
description: string;
1924
tests?: ScenarioTestDefinition<T>[];
20-
modificationsBeforeComponentCreation?: (() => void)[];
21-
modificationsAfterComponentCreation?: ((
22-
fxRef: ComponentFixtureRef<T>,
23-
) => void)[];
25+
modificationsBeforeComponentCreation?: ScenarioSetupFn[];
26+
modificationsAfterComponentCreation?: ScenarioViewSetupFn<T>[];
27+
};
28+
29+
export type ScenarioViewSetupFn<T> = ((
30+
fxRef: ComponentFixtureRef<T>,
31+
) => void) & {
32+
[NgtxScenarioViewSetupFnMarker]: boolean;
33+
};
34+
export type ScenarioSetupFn = (() => void) & {
35+
[NgtxScenarioSetupFnMarker]: boolean;
2436
};

library/specs/scenario/scenario-testing.spec.ts

+19-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Component, Injectable, Type, inject } from '@angular/core';
22
import { TestBed } from '@angular/core/testing';
33
import { ActivatedRoute } from '@angular/router';
4+
import { ngtx } from '../../ngtx';
45
import { ScenarioTestingHarness } from '../../scenario-testing/scenario-harnesses';
56
import { useScenarioTesting } from '../../scenario-testing/scenario-testing';
67
import { ComponentFixtureRef } from '../../scenario-testing/types';
@@ -30,22 +31,23 @@ class ScenarioTestComponent {
3031
// ----------------------------
3132
// Test Setup Utilities
3233
// ----------------------------
33-
const withRouterParams = (params: Record<string, unknown>) => () => {
34-
TestBed.overrideProvider(ActivatedRoute, {
35-
useValue: { snapshot: { params: params } },
34+
35+
const withRouterParams = (params: Record<string, unknown>) =>
36+
ngtx.scenario.envSetupFn(() => {
37+
TestBed.overrideProvider(ActivatedRoute, {
38+
useValue: { snapshot: { params: params } },
39+
});
3640
});
37-
};
3841

39-
const withServiceState =
40-
<T>(token: Type<T>, state: Partial<T>) =>
41-
() => {
42+
const withServiceState = <T>(token: Type<T>, state: Partial<T>) =>
43+
ngtx.scenario.envSetupFn(() => {
4244
TestBed.overrideProvider(token, { useValue: state });
43-
};
45+
});
4446

4547
const withInitialChangeDetection = () => {
46-
return (fxRef: ComponentFixtureRef) => {
48+
return ngtx.scenario.viewSetupFn((fxRef: ComponentFixtureRef) => {
4749
fxRef().changeDetectorRef.detectChanges();
48-
};
50+
});
4951
};
5052

5153
// ----------------------------
@@ -66,11 +68,11 @@ class the {
6668
}
6769

6870
scenario(`MyService value is displayed`)
69-
.configure(
71+
.setup(
7072
withRouterParams({ id: undefined }),
7173
withServiceState(MyService, { value: 'Jane' }),
74+
withInitialChangeDetection(),
7275
)
73-
.whenComponentReady(withInitialChangeDetection())
7476
.expect(
7577
the.Div.toContainText('Jane'),
7678
the.Div.not.toContainText('Madam'),
@@ -80,10 +82,14 @@ scenario(`MyService value is displayed`)
8082
color: 'red',
8183
fontSize: '12px',
8284
}),
85+
the.Div.not.toHaveStyles({
86+
color: 'blue',
87+
fontSize: '24px',
88+
}),
8389
);
8490

8591
scenario(`The param id is 42`)
86-
.configure(
92+
.setup(
8793
withRouterParams({ id: 42 }),
8894
withServiceState(MyService, { value: 'Henry' }),
8995
)

0 commit comments

Comments
 (0)