-
Notifications
You must be signed in to change notification settings - Fork 27.2k
feat(tests): manage asynchronous tests using zones #7735
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 all commits
Commits
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 hidden or 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 hidden or 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 |
|---|---|---|
|
|
@@ -130,6 +130,7 @@ export class InjectSetupWrapper { | |
| return new FunctionWithParamTokens(tokens, fn, false, this._providers); | ||
| } | ||
|
|
||
| /** @Deprecated {use async(withProviders().inject())} */ | ||
| injectAsync(tokens: any[], fn: Function): FunctionWithParamTokens { | ||
| return new FunctionWithParamTokens(tokens, fn, true, this._providers); | ||
| } | ||
|
|
@@ -140,6 +141,8 @@ export function withProviders(providers: () => any) { | |
| } | ||
|
|
||
| /** | ||
| * @Deprecated {use async(inject())} | ||
| * | ||
| * Allows injecting dependencies in `beforeEach()` and `it()`. The test must return | ||
| * a promise which will resolve when all asynchronous activity is complete. | ||
| * | ||
|
|
@@ -161,6 +164,32 @@ export function injectAsync(tokens: any[], fn: Function): FunctionWithParamToken | |
| return new FunctionWithParamTokens(tokens, fn, true); | ||
| } | ||
|
|
||
|
||
| /** | ||
| * Wraps a test function in an asynchronous test zone. The test will automatically | ||
| * complete when all asynchronous calls within this zone are done. Can be used | ||
| * to wrap an {@link inject} call. | ||
| * | ||
| * Example: | ||
| * | ||
| * ``` | ||
| * it('...', async(inject([AClass], (object) => { | ||
| * object.doSomething.then(() => { | ||
| * expect(...); | ||
| * }) | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export function async(fn: Function | FunctionWithParamTokens): FunctionWithParamTokens { | ||
| if (fn instanceof FunctionWithParamTokens) { | ||
| fn.isAsync = true; | ||
| return fn; | ||
| } else if (fn instanceof Function) { | ||
| return new FunctionWithParamTokens([], fn, true); | ||
| } else { | ||
| throw new BaseException('argument to async must be a function or inject(<Function>)'); | ||
| } | ||
| } | ||
|
|
||
| function emptyArray(): Array<any> { | ||
| return []; | ||
| } | ||
|
|
||
This file contains hidden or 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
7 changes: 7 additions & 0 deletions
7
modules/angular2/test/testing/testing_public_browser_spec.dart
This file contains hidden or 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,7 @@ | ||
| library angular2.test.testing.testing_browser_pec; | ||
|
|
||
| /** | ||
| * This is intentionally left blank. The public test lib is only for TS/JS | ||
| * apps. | ||
| */ | ||
| main() {} |
166 changes: 166 additions & 0 deletions
166
modules/angular2/test/testing/testing_public_browser_spec.ts
This file contains hidden or 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,166 @@ | ||
| import { | ||
| it, | ||
| iit, | ||
| xit, | ||
| describe, | ||
| ddescribe, | ||
| xdescribe, | ||
| expect, | ||
| beforeEach, | ||
| beforeEachProviders, | ||
| inject, | ||
| async, | ||
| TestComponentBuilder, | ||
| fakeAsync, | ||
| tick | ||
| } from 'angular2/testing'; | ||
|
|
||
| import {Injectable, bind} from 'angular2/core'; | ||
| import {Directive, Component, ViewMetadata} from 'angular2/core'; | ||
| import {PromiseWrapper} from 'angular2/src/facade/promise'; | ||
| import {XHR} from 'angular2/src/compiler/xhr'; | ||
| import {XHRImpl} from 'angular2/src/platform/browser/xhr_impl'; | ||
|
|
||
| // Components for the tests. | ||
| class FancyService { | ||
| value: string = 'real value'; | ||
| getAsyncValue() { return Promise.resolve('async value'); } | ||
| getTimeoutValue() { | ||
| return new Promise((resolve, reject) => { setTimeout(() => {resolve('timeout value')}, 10); }) | ||
| } | ||
| } | ||
|
|
||
| @Component({ | ||
| selector: 'external-template-comp', | ||
| templateUrl: '/base/modules/angular2/test/testing/static_assets/test.html' | ||
| }) | ||
| class ExternalTemplateComp { | ||
| } | ||
|
|
||
| @Component({selector: 'bad-template-comp', templateUrl: 'non-existant.html'}) | ||
| class BadTemplateUrl { | ||
| } | ||
|
|
||
| // Tests for angular2/testing bundle specific to the browser environment. | ||
| // For general tests, see test/testing/testing_public_spec.ts. | ||
| export function main() { | ||
| describe('test APIs for the browser', () => { | ||
| describe('angular2 jasmine matchers', () => { | ||
| describe('toHaveCssClass', () => { | ||
| it('should assert that the CSS class is present', () => { | ||
| var el = document.createElement('div'); | ||
| el.classList.add('matias'); | ||
| expect(el).toHaveCssClass('matias'); | ||
| }); | ||
|
|
||
| it('should assert that the CSS class is not present', () => { | ||
| var el = document.createElement('div'); | ||
| el.classList.add('matias'); | ||
| expect(el).not.toHaveCssClass('fatias'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('toHaveCssStyle', () => { | ||
| it('should assert that the CSS style is present', () => { | ||
| var el = document.createElement('div'); | ||
| expect(el).not.toHaveCssStyle('width'); | ||
|
|
||
| el.style.setProperty('width', '100px'); | ||
| expect(el).toHaveCssStyle('width'); | ||
| }); | ||
|
|
||
| it('should assert that the styles are matched against the element', () => { | ||
| var el = document.createElement('div'); | ||
| expect(el).not.toHaveCssStyle({width: '100px', height: '555px'}); | ||
|
|
||
| el.style.setProperty('width', '100px'); | ||
| expect(el).toHaveCssStyle({width: '100px'}); | ||
| expect(el).not.toHaveCssStyle({width: '100px', height: '555px'}); | ||
|
|
||
| el.style.setProperty('height', '555px'); | ||
| expect(el).toHaveCssStyle({height: '555px'}); | ||
| expect(el).toHaveCssStyle({width: '100px', height: '555px'}); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| describe('using the async helper', () => { | ||
| var actuallyDone: boolean; | ||
|
|
||
| beforeEach(() => { actuallyDone = false; }); | ||
|
|
||
| afterEach(() => { expect(actuallyDone).toEqual(true); }); | ||
|
|
||
| it('should run async tests with XHRs', async(() => { | ||
| var xhr = new XHRImpl(); | ||
| xhr.get('/base/modules/angular2/test/testing/static_assets/test.html') | ||
| .then(() => { actuallyDone = true; }); | ||
| }), | ||
| 10000); // Long timeout here because this test makes an actual XHR. | ||
| }); | ||
|
|
||
| describe('using the test injector with the inject helper', () => { | ||
| describe('setting up Providers', () => { | ||
| beforeEachProviders(() => [bind(FancyService).toValue(new FancyService())]); | ||
|
|
||
| it('provides a real XHR instance', | ||
| inject([XHR], (xhr) => { expect(xhr).toBeAnInstanceOf(XHRImpl); })); | ||
|
|
||
| it('should allow the use of fakeAsync', | ||
| inject([FancyService], fakeAsync((service) => { | ||
| var value; | ||
| service.getAsyncValue().then(function(val) { value = val; }); | ||
| tick(); | ||
| expect(value).toEqual('async value'); | ||
| }))); | ||
| }); | ||
| }); | ||
|
|
||
| describe('errors', () => { | ||
| var originalJasmineIt: any; | ||
|
|
||
| var patchJasmineIt = () => { | ||
| var deferred = PromiseWrapper.completer(); | ||
| originalJasmineIt = jasmine.getEnv().it; | ||
| jasmine.getEnv().it = (description: string, fn) => { | ||
| var done = () => { deferred.resolve() }; | ||
| (<any>done).fail = (err) => { deferred.reject(err) }; | ||
| fn(done); | ||
| return null; | ||
| }; | ||
| return deferred.promise; | ||
| }; | ||
|
|
||
| var restoreJasmineIt = () => { jasmine.getEnv().it = originalJasmineIt; }; | ||
|
|
||
| it('should fail when an XHR fails', (done) => { | ||
| var itPromise = patchJasmineIt(); | ||
|
|
||
| it('should fail with an error from a promise', | ||
| async(inject([TestComponentBuilder], | ||
| (tcb) => { return tcb.createAsync(BadTemplateUrl); }))); | ||
|
|
||
| itPromise.then(() => { done.fail('Expected test to fail, but it did not'); }, (err) => { | ||
| expect(err).toEqual('Uncaught (in promise): Failed to load non-existant.html'); | ||
| done(); | ||
| }); | ||
| restoreJasmineIt(); | ||
| }, 10000); | ||
| }); | ||
|
|
||
| describe('test component builder', function() { | ||
| it('should allow an external templateUrl', | ||
| async(inject([TestComponentBuilder], | ||
| (tcb: TestComponentBuilder) => { | ||
|
|
||
| tcb.createAsync(ExternalTemplateComp) | ||
| .then((componentFixture) => { | ||
| componentFixture.detectChanges(); | ||
| expect(componentFixture.debugElement.nativeElement) | ||
| .toHaveText('from external template\n'); | ||
| }); | ||
| })), | ||
| 10000); // Long timeout here because this test makes an actual XHR, and is slow on Edge. | ||
| }); | ||
| }); | ||
| } |
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 we use this opportunity to remove FunctionWithParamTokens from regular paths(and handle it temporarily in till injectAsync is completely removed) since we no longer have a sync and async variations of it.
So the new inject() would just be something like
This way inject can be a generic mechanism to execute a function with injected parameters and not return the special FunctionWithParams type.
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.
I don't think that we necessarily need to get rid of
FunctionWithParamTokens- it's useful to store information about what we want to test and it's not tied to Jasmine.In particular, we need to store information about additional providers somewhere so that we can do:
We could pass the providers around everywhere, but I think it's clearer to store them on an object.
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.
Understood. Agreed.