Skip to content

Commit a26ef96

Browse files
committed
feat: support assertion extensions in harnesses
1 parent c7571a6 commit a26ef96

File tree

5 files changed

+40
-8
lines changed

5 files changed

+40
-8
lines changed

docs/scenario-testing/vision.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414

1515
## Vision
1616

17-
TODO: explain feature: scenario based test generation
17+
TODO: docs: explain feature: scenario based test generation

library/scenario-testing/scenario-harnesses.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { TypedDebugElement, UnwrapSignals } from '../types';
55
import { isNgtxQuerySelector } from '../utility';
66
import { NgtxScenarioTestEnvironment } from './scenario-testing';
77
import { NgtxScenarioTestIsAssertionNegated } from './symbols';
8-
import { ComponentFixtureRef } from './types';
8+
import { ComponentFixtureRef, NgtxScenarioTestAssertionFn } from './types';
99

1010
export class ScenarioTestingHarnessBase<Html extends HTMLElement, Component> {
1111
private [NgtxScenarioTestIsAssertionNegated] = false;
@@ -16,7 +16,7 @@ export class ScenarioTestingHarnessBase<Html extends HTMLElement, Component> {
1616

1717
public readonly not: Omit<typeof this, 'not'> = new Proxy(this, {
1818
get: (_, property) => {
19-
// TODO: document that constructors of Harnesses are not allowed to be overridden!
19+
// TODO: docs: document that constructors of Harnesses are not allowed to be overridden!
2020
const harnessConstructor = this.constructor as any;
2121
const target = new harnessConstructor(
2222
this.selector,
@@ -243,4 +243,8 @@ export class ScenarioTestingHarness<
243243
);
244244
});
245245
}
246+
public to(...assertions: NgtxScenarioTestAssertionFn<Html, Component>[]) {
247+
const addTests: typeof this.addTests = this.addTests.bind(this);
248+
return assertions.map((assertion) => assertion(addTests, this));
249+
}
246250
}

library/scenario-testing/scenario-testing.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ export class NgtxTestScenario<T = any> {
130130
return scenario;
131131
}
132132

133-
expect(...tests: ScenarioTestDefinition<T>[]): void {
133+
expect(
134+
...tests: (ScenarioTestDefinition<T> | ScenarioTestDefinition<T>[])[]
135+
): void {
134136
const scenario = NgtxTestScenario.from(
135137
{
136138
componentType: this._componentType,
@@ -140,7 +142,7 @@ export class NgtxTestScenario<T = any> {
140142
this._modificationsBeforeComponentCreation,
141143
modificationsAfterComponentCreation:
142144
this._modificationsAfterComponentCreation,
143-
tests: [...this.tests, ...tests],
145+
tests: [...this.tests, ...tests].flat(1),
144146
},
145147
this._testEnvironment,
146148
);

library/scenario-testing/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Type } from '@angular/core';
22
import { ComponentFixture, TestModuleMetadata } from '@angular/core/testing';
3+
import { ScenarioTestingHarness } from './scenario-harnesses';
34
import {
45
NgtxScenarioSetupFnMarker,
56
NgtxScenarioViewSetupFnMarker,
@@ -34,3 +35,8 @@ export type ScenarioViewSetupFn<T> = ((
3435
export type ScenarioSetupFn = (() => void) & {
3536
[NgtxScenarioSetupFnMarker]: boolean;
3637
};
38+
39+
export type NgtxScenarioTestAssertionFn<Html extends HTMLElement, Component> = (
40+
addTests: ScenarioTestingHarness<Html, Component>['addTests'],
41+
harness: ScenarioTestingHarness<Html, Component>,
42+
) => ScenarioTestDefinition<any>;

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

+23-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ActivatedRoute, RouterModule } from '@angular/router';
44
import { ngtx } from '../../ngtx';
55
import { ScenarioTestingHarness } from '../../scenario-testing/scenario-harnesses';
66
import { useScenarioTesting } from '../../scenario-testing/scenario-testing';
7+
import { NgtxScenarioTestAssertionFn } from '../../scenario-testing/types';
78

89
@Injectable()
910
class MyService {
@@ -57,7 +58,11 @@ const withServiceState = <T>(token: Type<T>, state: Partial<T>) =>
5758
// Usage Example
5859
// ----------------------------
5960

60-
const { scenario, expect, tests } = useScenarioTesting({
61+
const {
62+
scenario,
63+
expect: expectThat,
64+
tests,
65+
} = useScenarioTesting({
6166
componentType: ScenarioTestComponent,
6267
moduleConfig: {
6368
imports: [RouterModule.forRoot([])],
@@ -110,16 +115,31 @@ scenario(`The param id is 42`)
110115
the.ParamIdDiv.toHaveAttributes({ title: 42 }),
111116
);
112117

113-
expect(
118+
expectThat(
114119
the.Div.toBeFound(),
115120
the.ParamIdDiv.toBeMissing(),
116121
the.Text.toHaveState({ text: 'Hello, World!' }),
117122
);
118123

119-
expect(
124+
const beComponentType =
125+
(type: any): NgtxScenarioTestAssertionFn<HTMLElement, any> =>
126+
// TODO: docs: document that debugElement must only be accessed within it case:
127+
(addTests, harness) =>
128+
addTests(() => {
129+
it(`should be the component "${type.name}"`, () => {
130+
expect(harness.debugElement.componentInstance).toBeInstanceOf(type);
131+
});
132+
});
133+
134+
expectThat(
120135
the.Div.toBeFound(),
121136
the.ParamIdDiv.toBeMissing(),
122137
the.Text.toHaveState({ text: 'Hello, World!' }),
138+
the.Text.to(
139+
beComponentType(TextComponent),
140+
beComponentType(TextComponent),
141+
beComponentType(TextComponent),
142+
),
123143
);
124144

125145
tests.run();

0 commit comments

Comments
 (0)