Skip to content

Commit 1a29f13

Browse files
chore: make nTestBed helpers automatically clean up test components
- before the components were destroyed by TestBed, but not removed from the rootView. - maintain a list of active fixtures for a set of tests, and remove them all when the tests complete. - reorder to the testing utils to flow better when reading (start with test init, then before/after then render components) - clean up some lint.
1 parent dde24c1 commit 1a29f13

File tree

4 files changed

+77
-44
lines changed

4 files changed

+77
-44
lines changed

Diff for: nativescript-angular/testing/index.ts

+1-9
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,9 @@ import { TestComponentRenderer } from "@angular/core/testing";
33
import { NativeScriptTestComponentRenderer } from "./src/nativescript_test_component_renderer";
44
import { COMMON_PROVIDERS } from "../platform-common";
55
import { APP_ROOT_VIEW } from "../platform-providers";
6-
import { View } from "tns-core-modules/ui/core/view";
7-
import { topmost } from "tns-core-modules/ui/frame";
6+
import { testingRootView } from "./src/util";
87
export * from "./src/util";
98

10-
/**
11-
* Get a reference to the root application view.
12-
*/
13-
export function testingRootView(): View {
14-
return topmost().currentPage.content;
15-
}
16-
179
/**
1810
* Providers array is exported for cases where a custom module has to be constructed
1911
* to test a particular piece of code. This can happen, for example, if you are trying

Diff for: nativescript-angular/testing/src/util.ts

+65-24
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
1+
2+
import { View } from "tns-core-modules/ui/core/view";
3+
import { topmost } from "tns-core-modules/ui/frame";
4+
import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
15
import { ComponentFixture, TestBed } from "@angular/core/testing";
26
import { NgModule, Type } from "@angular/core";
37
import { NativeScriptModule } from "../../nativescript.module";
48
import { platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing";
59
import { NS_COMPILER_PROVIDERS } from "../../platform";
610
import { NATIVESCRIPT_TESTING_PROVIDERS, NativeScriptTestingModule } from "../index";
711
import { CommonModule } from "@angular/common";
12+
/**
13+
* Get a reference to the root application view.
14+
*/
15+
export function testingRootView(): View {
16+
return topmost().currentPage.content;
17+
}
18+
19+
/**
20+
* Declared test contexts. When the suite is done this map should be empty if all lifecycle
21+
* calls have happened as expected.
22+
* @private
23+
*/
24+
const activeTestFixtures: ComponentFixture<any>[][] = [];
825

926
/**
1027
* Return a promise that resolves after (durationMs) milliseconds
@@ -14,19 +31,13 @@ export function promiseWait(durationMs: number) {
1431
}
1532

1633
/**
17-
* Render a component using the TestBed helper, and return a promise that resolves when the
18-
* ComponentFixture is fully initialized.
34+
* Perform basic TestBed environment initialization. Call this once in the main entry point to your tests.
1935
*/
20-
export function nTestBedRender<T>(componentType: Type<T>): Promise<ComponentFixture<T>> {
21-
const fixture = TestBed.createComponent(componentType);
22-
fixture.detectChanges();
23-
return fixture.whenRenderingDone()
24-
// TODO(jd): it seems that the whenStable and whenRenderingDone utilities of ComponentFixture
25-
// do not work as expected. I looked at how to fix it and it's not clear how to provide
26-
// a {N} specific subclass, because ComponentFixture is newed directly rather than injected
27-
// What to do about it? Maybe fakeAsync can help? For now just setTimeout for 100ms (x_X)
28-
.then(promiseWait(100))
29-
.then(() => fixture);
36+
export function nTestBedInit() {
37+
TestBed.initTestEnvironment(
38+
NativeScriptTestingModule,
39+
platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS)
40+
);
3041
}
3142

3243
/**
@@ -55,6 +66,7 @@ export function nTestBedBeforeEach(
5566
imports: any[] = [],
5667
entryComponents: any[] = []) {
5768
return (done) => {
69+
activeTestFixtures.push([]);
5870
// If there are no entry components we can take the simple path.
5971
if (entryComponents.length === 0) {
6072
TestBed.configureTestingModule({
@@ -96,30 +108,59 @@ export function nTestBedBeforeEach(
96108
done();
97109
});
98110
};
99-
100-
}
101-
102-
/**
103-
* Perform basic TestBed environment initialization. Call this once in the main entry point to your tests.
104-
*/
105-
export function nBasicTestBedInit() {
106-
TestBed.initTestEnvironment(
107-
NativeScriptTestingModule,
108-
platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS)
109-
);
110111
}
111112

112113
/**
113114
* Helper for a basic component TestBed clean up.
114115
* @param resetEnv When true the testing environment will be reset
115116
* @param resetFn When resetting the environment, use this init function
116117
*/
117-
export function nTestBedAfterEach(resetEnv = true, resetFn = nBasicTestBedInit) {
118+
export function nTestBedAfterEach(resetEnv = true, resetFn = nTestBedInit) {
118119
return () => {
120+
if (activeTestFixtures.length === 0) {
121+
throw new Error(
122+
`There are no more declared fixtures.` +
123+
`Did you call "nTestBedBeforeEach" and "nTestBedAfterEach" an equal number of times?`
124+
);
125+
}
126+
const root = testingRootView() as LayoutBase;
127+
const fixtures = activeTestFixtures.pop();
128+
fixtures.forEach((fixture) => {
129+
root.removeChild(fixture.nativeElement);
130+
fixture.destroy();
131+
});
119132
TestBed.resetTestingModule();
120133
if (resetEnv) {
121134
TestBed.resetTestEnvironment();
122135
resetFn();
123136
}
124137
};
125138
}
139+
140+
/**
141+
* Render a component using the TestBed helper, and return a promise that resolves when the
142+
* ComponentFixture is fully initialized.
143+
*/
144+
export function nTestBedRender<T>(componentType: Type<T>): Promise<ComponentFixture<T>> {
145+
const fixture = TestBed.createComponent(componentType);
146+
fixture.detectChanges();
147+
return fixture.whenRenderingDone()
148+
// TODO(jd): it seems that the whenStable and whenRenderingDone utilities of ComponentFixture
149+
// do not work as expected. I looked at how to fix it and it's not clear how to provide
150+
// a {N} specific subclass, because ComponentFixture is newed directly rather than injected
151+
// What to do about it? Maybe fakeAsync can help? For now just setTimeout for 100ms (x_X)
152+
.then(promiseWait(100))
153+
.then(() => {
154+
const list = activeTestFixtures[activeTestFixtures.length - 1];
155+
if (!list) {
156+
console.warn(
157+
"nTestBedRender called without nTestBedBeforeEach/nTestBedAfter each. " +
158+
"You are responsible for calling 'fixture.destroy()' when your test is done " +
159+
"in order to clean up the components that are created."
160+
);
161+
} else {
162+
list.push(fixture);
163+
}
164+
return fixture;
165+
});
166+
}

Diff for: tests/app/tests/modal-dialog.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// make sure you import mocha-config before @angular/core
2-
import {assert} from './test-config';
3-
import {Component, ViewContainerRef} from '@angular/core';
4-
import {Page} from 'ui/page';
5-
import {topmost} from 'ui/frame';
6-
import {ModalDialogParams, ModalDialogService} from 'nativescript-angular/directives/dialogs';
2+
import {assert} from "./test-config";
3+
import {Component, ViewContainerRef} from "@angular/core";
4+
import {Page} from "ui/page";
5+
import {topmost} from "ui/frame";
6+
import {ModalDialogParams, ModalDialogService} from "nativescript-angular/directives/dialogs";
77

8-
import {device, platformNames} from 'platform';
8+
import {device, platformNames} from "platform";
99

10-
import {ComponentFixture} from '@angular/core/testing';
11-
import {nTestBedRender, nTestBedAfterEach, nTestBedBeforeEach} from 'nativescript-angular/testing';
10+
import {ComponentFixture} from "@angular/core/testing";
11+
import {nTestBedRender, nTestBedAfterEach, nTestBedBeforeEach} from "nativescript-angular/testing";
1212
const CLOSE_WAIT = (device.os === platformNames.ios) ? 1000 : 0;
1313

1414
@Component({

Diff for: tests/app/tests/test-main.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import 'nativescript-angular/zone-js/testing.mocha';
2-
import {nBasicTestBedInit} from 'nativescript-angular/testing';
3-
nBasicTestBedInit();
1+
import "nativescript-angular/zone-js/testing.mocha";
2+
import {nTestBedInit} from "nativescript-angular/testing";
3+
nTestBedInit();

0 commit comments

Comments
 (0)