-
-
Notifications
You must be signed in to change notification settings - Fork 241
Testing Components with TestBed #1061
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { NgModule } from "@angular/core"; | ||
import { TestComponentRenderer } from "@angular/core/testing"; | ||
import { NativeScriptTestComponentRenderer } from "./src/nativescript_test_component_renderer"; | ||
import { COMMON_PROVIDERS } from "../platform-common"; | ||
import { APP_ROOT_VIEW } from "../platform-providers"; | ||
import { testingRootView } from "./src/util"; | ||
export * from "./src/util"; | ||
|
||
/** | ||
* Providers array is exported for cases where a custom module has to be constructed | ||
* to test a particular piece of code. This can happen, for example, if you are trying | ||
* to test dynamic component loading and need to specify an entryComponent for the testing | ||
* module. | ||
*/ | ||
export const NATIVESCRIPT_TESTING_PROVIDERS: any[] = [ | ||
COMMON_PROVIDERS, | ||
{provide: APP_ROOT_VIEW, useFactory: testingRootView}, | ||
{provide: TestComponentRenderer, useClass: NativeScriptTestComponentRenderer}, | ||
]; | ||
|
||
/** | ||
* NativeScript testing support module. Enables use of TestBed for angular components, directives, | ||
* pipes, and services. | ||
*/ | ||
@NgModule({ | ||
providers: NATIVESCRIPT_TESTING_PROVIDERS | ||
}) | ||
export class NativeScriptTestingModule { | ||
} |
24 changes: 24 additions & 0 deletions
24
nativescript-angular/testing/src/nativescript_test_component_renderer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Injectable } from "@angular/core"; | ||
import { TestComponentRenderer } from "@angular/core/testing"; | ||
import { topmost } from "tns-core-modules/ui/frame"; | ||
import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; | ||
import { ProxyViewContainer } from "tns-core-modules/ui/proxy-view-container"; | ||
|
||
/** | ||
* A NativeScript based implementation of the TestComponentRenderer. | ||
*/ | ||
@Injectable() | ||
export class NativeScriptTestComponentRenderer extends TestComponentRenderer { | ||
|
||
insertRootElement(rootElId: string) { | ||
const page = topmost().currentPage; | ||
|
||
const layout = new ProxyViewContainer(); | ||
layout.id = rootElId; | ||
|
||
const rootLayout = page.layoutView as LayoutBase; | ||
rootLayout.addChild(layout); | ||
} | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
|
||
import { View } from "tns-core-modules/ui/core/view"; | ||
import { topmost } from "tns-core-modules/ui/frame"; | ||
import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; | ||
import { ComponentFixture, TestBed } from "@angular/core/testing"; | ||
import { NgModule, Type } from "@angular/core"; | ||
import { NativeScriptModule } from "../../nativescript.module"; | ||
import { platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing"; | ||
import { NS_COMPILER_PROVIDERS } from "../../platform"; | ||
import { NATIVESCRIPT_TESTING_PROVIDERS, NativeScriptTestingModule } from "../index"; | ||
import { CommonModule } from "@angular/common"; | ||
/** | ||
* Get a reference to the root application view. | ||
*/ | ||
export function testingRootView(): View { | ||
return topmost().currentPage.content; | ||
} | ||
|
||
/** | ||
* Declared test contexts. When the suite is done this map should be empty if all lifecycle | ||
* calls have happened as expected. | ||
* @private | ||
*/ | ||
const activeTestFixtures: ComponentFixture<any>[][] = []; | ||
|
||
/** | ||
* Return a promise that resolves after (durationMs) milliseconds | ||
*/ | ||
export function promiseWait(durationMs: number) { | ||
return () => new Promise((resolve) => setTimeout(() => resolve(), durationMs)); | ||
} | ||
|
||
/** | ||
* Perform basic TestBed environment initialization. Call this once in the main entry point to your tests. | ||
*/ | ||
export function nTestBedInit() { | ||
TestBed.initTestEnvironment( | ||
NativeScriptTestingModule, | ||
platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS) | ||
); | ||
} | ||
|
||
/** | ||
* Helper for configuring a TestBed instance for rendering components for test. Ideally this | ||
* would not be needed, and in truth it's just a wrapper to eliminate some boilerplate. It | ||
* exists because when you need to specify `entryComponents` for a test the setup becomes quite | ||
* a bit more complex than if you're just doing a basic component test. | ||
* | ||
* More about entryComponents complexity: https://github.com/angular/angular/issues/12079 | ||
* | ||
* Use: | ||
* ``` | ||
* beforeEach(nTestBedBeforeEach([MyComponent,MyFailComponent])); | ||
* ``` | ||
* | ||
* **NOTE*** Remember to pair with {@see nTestBedAfterEach} | ||
* | ||
* @param components Any components that you will create during the test | ||
* @param providers Any services your tests depend on | ||
* @param imports Any module imports your tests depend on | ||
* @param entryComponents Any entry components that your tests depend on | ||
*/ | ||
export function nTestBedBeforeEach( | ||
components: any[], | ||
providers: any[] = [], | ||
imports: any[] = [], | ||
entryComponents: any[] = []) { | ||
return (done) => { | ||
activeTestFixtures.push([]); | ||
// If there are no entry components we can take the simple path. | ||
if (entryComponents.length === 0) { | ||
TestBed.configureTestingModule({ | ||
declarations: [...components], | ||
providers: [...providers], | ||
imports: [NativeScriptModule, ...imports] | ||
}); | ||
} else { | ||
// If there are entry components, we have to reset the testing platform. | ||
// | ||
// There's got to be a better way... (o_O) | ||
TestBed.resetTestEnvironment(); | ||
@NgModule({ | ||
declarations: entryComponents, | ||
exports: entryComponents, | ||
entryComponents: entryComponents | ||
}) | ||
class EntryComponentsTestModule { | ||
} | ||
TestBed.initTestEnvironment( | ||
EntryComponentsTestModule, | ||
platformBrowserDynamicTesting(NS_COMPILER_PROVIDERS) | ||
); | ||
TestBed.configureTestingModule({ | ||
declarations: components, | ||
imports: [ | ||
NativeScriptModule, NativeScriptTestingModule, CommonModule, | ||
...imports | ||
], | ||
providers: [...providers, ...NATIVESCRIPT_TESTING_PROVIDERS], | ||
}); | ||
} | ||
TestBed.compileComponents() | ||
.then(() => done()) | ||
.catch((e) => { | ||
console.log(`Failed to instantiate test component with error: ${e}`); | ||
console.log(e.stack); | ||
done(); | ||
}); | ||
}; | ||
} | ||
|
||
/** | ||
* Helper for a basic component TestBed clean up. | ||
* @param resetEnv When true the testing environment will be reset | ||
* @param resetFn When resetting the environment, use this init function | ||
*/ | ||
export function nTestBedAfterEach(resetEnv = true, resetFn = nTestBedInit) { | ||
return () => { | ||
if (activeTestFixtures.length === 0) { | ||
throw new Error( | ||
`There are no more declared fixtures.` + | ||
`Did you call "nTestBedBeforeEach" and "nTestBedAfterEach" an equal number of times?` | ||
); | ||
} | ||
const root = testingRootView() as LayoutBase; | ||
const fixtures = activeTestFixtures.pop(); | ||
fixtures.forEach((fixture) => { | ||
root.removeChild(fixture.nativeElement); | ||
fixture.destroy(); | ||
}); | ||
TestBed.resetTestingModule(); | ||
if (resetEnv) { | ||
TestBed.resetTestEnvironment(); | ||
resetFn(); | ||
} | ||
}; | ||
} | ||
|
||
/** | ||
* Render a component using the TestBed helper, and return a promise that resolves when the | ||
* ComponentFixture is fully initialized. | ||
*/ | ||
export function nTestBedRender<T>(componentType: Type<T>): Promise<ComponentFixture<T>> { | ||
const fixture = TestBed.createComponent(componentType); | ||
fixture.detectChanges(); | ||
return fixture.whenRenderingDone() | ||
// TODO(jd): it seems that the whenStable and whenRenderingDone utilities of ComponentFixture | ||
// do not work as expected. I looked at how to fix it and it's not clear how to provide | ||
// a {N} specific subclass, because ComponentFixture is newed directly rather than injected | ||
// What to do about it? Maybe fakeAsync can help? For now just setTimeout for 100ms (x_X) | ||
.then(promiseWait(100)) | ||
.then(() => { | ||
const list = activeTestFixtures[activeTestFixtures.length - 1]; | ||
if (!list) { | ||
console.warn( | ||
"nTestBedRender called without nTestBedBeforeEach/nTestBedAfter each. " + | ||
"You are responsible for calling 'fixture.destroy()' when your test is done " + | ||
"in order to clean up the components that are created." | ||
); | ||
} else { | ||
list.push(fixture); | ||
} | ||
return fixture; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
Zone.js for NativeScript | ||
--- | ||
|
||
Zone.js is a library that aims to intercept all asynchronous API calls made in an environment, in order | ||
to wrap them into coherent execution contexts over time. | ||
|
||
NativeScript executes inside an environment that Zone.js is not designed to work in, so a custom Zone.js output | ||
must be created. | ||
|
||
Find out more about this in the [Upgrading Zone.js document](../../doc/upgrading-zonejs.md) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you prefix the all test method with
ns
instead ofn
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, what about: