diff --git a/example/src/app/app.component.spec.ts b/example/src/app/app.component.spec.ts index 678ace2256..910f8867ba 100644 --- a/example/src/app/app.component.spec.ts +++ b/example/src/app/app.component.spec.ts @@ -1,10 +1,5 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { - async, - fakeAsync, - tick, - ComponentFixture, -} from '@angular/core/testing'; +import { async, fakeAsync, tick, ComponentFixture } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ConfigureFn, configureTests } from '@lib/testing'; @@ -20,7 +15,7 @@ describe('AppComponent', () => { testBed.configureTestingModule({ declarations: [AppComponent], imports: [NoopAnimationsModule], - schemas: [NO_ERRORS_SCHEMA], + schemas: [NO_ERRORS_SCHEMA] }); }; @@ -50,8 +45,9 @@ describe('AppComponent', () => { expect(compiled.querySelector('h1').textContent).toContain('app works!'); })); - it('looks async but is synchronous', fakeAsync( - (): void => { + it( + 'looks async but is synchronous', + fakeAsync((): void => { let flag = false; setTimeout(() => { flag = true; @@ -61,8 +57,28 @@ describe('AppComponent', () => { expect(flag).toBe(false); tick(50); expect(flag).toBe(true); - } - )); + }) + ); + + it( + 'async should work', + async((): void => { + let flag = false; + setTimeout(() => { + flag = true; + expect(flag).toBe(true); + }, 100); + }) + ); + + it('test with done should work', (done): void => { + let flag = false; + setTimeout(() => { + flag = true; + expect(flag).toBe(true); + done(); + }, 100); + }); }); test.todo('a sample todo'); diff --git a/example/src/app/service/hero.service.spec.ts b/example/src/app/service/hero.service.spec.ts index 638596671d..0e22b15a65 100644 --- a/example/src/app/service/hero.service.spec.ts +++ b/example/src/app/service/hero.service.spec.ts @@ -1,104 +1,107 @@ -import { inject, TestBed } from '@angular/core/testing' -import { HttpClientModule, HttpErrorResponse, HttpRequest } from '@angular/common/http' -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing' +import { HttpClientModule, HttpErrorResponse, HttpRequest } from '@angular/common/http'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { inject, TestBed } from '@angular/core/testing'; -import { heroesUrl, HeroService } from './hero.service' +import { heroesUrl, HeroService } from './hero.service'; describe('Service: HeroService', () => { - let service: HeroService - let backend: HttpTestingController + let service: HeroService; + let backend: HttpTestingController; - const expectedData = { - id: 1, - name: 'Test hero', - } + const expectedData = { id: 1, name: 'Test hero' }; const expectedDataAll = [ - { - id: 1, - name: 'Test hero 1' - }, - { - id: 2, - name: 'Test hero 2' - } - ] + { id: 1, name: 'Test hero 1' }, + { id: 2, name: 'Test hero 2' } + ]; beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - HttpClientModule, - HttpClientTestingModule, - ], - providers: [ - HeroService, - ], - }) - - backend = TestBed.get(HttpTestingController) - service = TestBed.get(HeroService) + imports: [HttpClientModule, HttpClientTestingModule], + providers: [HeroService] + }); + + backend = TestBed.get(HttpTestingController); + service = TestBed.get(HeroService); // Mock implementation of console.error to // return undefined to stop printing out to console log during test - jest.spyOn(console, 'error').mockImplementation(() => undefined) - }) + jest.spyOn(console, 'error').mockImplementation(() => undefined); + }); - afterEach(inject([ HttpTestingController ], (_backend: HttpTestingController) => { - _backend.verify() - })) + afterEach(inject([HttpTestingController], (_backend: HttpTestingController) => { + _backend.verify(); + })); it('should create an instance successfully', () => { - expect(service).toBeDefined() - }) + expect(service).toBeDefined(); + }); it('should call the GET heroes api and return all results', () => { - let actualDataAll = {} + let actualDataAll = {}; + + service.getHeroes().subscribe(data => (actualDataAll = data)); + + backend + .expectOne((req: HttpRequest) => { + return req.url === `${heroesUrl}` && req.method === 'GET'; + }, `GET all hero data from ${heroesUrl}`) + .flush(expectedDataAll); - service.getHeroes().subscribe(data => actualDataAll = data) + expect(actualDataAll).toEqual(expectedDataAll); + }); - backend.expectOne((req: HttpRequest) => { - return req.url === `${heroesUrl}` - && req.method === 'GET' - }, `GET all hero data from ${heroesUrl}`) - .flush(expectedDataAll) + it('should call the GET hero api with id and return the result', () => { + let actualData = {}; - expect(actualDataAll).toEqual(expectedDataAll) - }) + service.getHero(1).subscribe(data => (actualData = data)); - it('should call the GET hero api and return the result', () => { - let actualData = {} + backend + .expectOne((req: HttpRequest) => { + return req.url === `${heroesUrl}` && req.method === 'GET' && req.params.get('id') === '1'; + }, `GET hero data from ${heroesUrl}?id=1`) + .flush(expectedData); - service.getHero(1).subscribe(data => actualData = data) + expect(actualData).toEqual(expectedData); + }); - backend.expectOne((req: HttpRequest) => { - return req.url === `${heroesUrl}` - && req.method === 'GET' - && req.params.get('id') === '1' - }, `GET hero data from ${heroesUrl}?id=1`) - .flush(expectedData) + test.each([ + [1, { id: 1, name: 'Test Hero 1' }], + [2, { id: 2, name: 'Test Hero 2' }] + ])('should call the GET hero api and return the result', (id: number, testData: any) => { + let actualData = {}; - expect(actualData).toEqual(expectedData) - }) + service.getHero(1).subscribe(data => (actualData = data)); + + backend + .expectOne((req: HttpRequest) => { + return req.url === `${heroesUrl}` && req.method === 'GET'; + }, `GET hero data from ${heroesUrl}?id=${id}`) + .flush(testData); + + expect(actualData).toEqual(testData); + }); it('should send an expected GET request and throw error to console when an error occurs', () => { - service.getHero(1).subscribe() + service.getHero(1).subscribe(); const getHeroRequest = backend.expectOne((req: HttpRequest) => { - return req.url === `${heroesUrl}` - && req.method === 'GET' - && req.params.get('id') === '1' - }, `GET hero data from ${heroesUrl}?id=1`) + return req.url === `${heroesUrl}` && req.method === 'GET' && req.params.get('id') === '1'; + }, `GET hero data from ${heroesUrl}?id=1`); // Stimulate an error happens from the backend - getHeroRequest.error(new ErrorEvent('ERROR_GET_HERO_DATA')) + getHeroRequest.error(new ErrorEvent('ERROR_GET_HERO_DATA')); - expect(console.error).toHaveBeenCalled() - }) + expect(console.error).toHaveBeenCalled(); + }); it('should return an observable of undefined and print error to console', () => { - const result = service.handleError(new HttpErrorResponse({ error: 'Error occurs' }), 'test method') - - expect(console.error).toHaveBeenCalled() - result.subscribe(value => expect(value).toBeUndefined()) - }) -}) + const result = service.handleError( + new HttpErrorResponse({ error: 'Error occurs' }), + 'test method' + ); + + expect(console.error).toHaveBeenCalled(); + result.subscribe(value => expect(value).toBeUndefined()); + }); +}); diff --git a/example/src/app/simple/another.component.ts b/example/src/app/simple/another.component.ts new file mode 100644 index 0000000000..bc801744f0 --- /dev/null +++ b/example/src/app/simple/another.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'another-comp', + template: '' +}) +export class AnotherComponent {} diff --git a/example/src/app/simple/simple.component.spec.ts b/example/src/app/simple/simple.component.spec.ts index e2f450d546..5798890017 100644 --- a/example/src/app/simple/simple.component.spec.ts +++ b/example/src/app/simple/simple.component.spec.ts @@ -1,28 +1,27 @@ import { async, ComponentFixture } from '@angular/core/testing'; -import {ConfigureFn, configureTests} from '@lib/testing'; +import { ConfigureFn, configureTests } from '@lib/testing'; import { SimpleComponent } from './simple.component'; +import { AnotherComponent } from './another.component'; describe('SimpleComponent', () => { let component: SimpleComponent; let fixture: ComponentFixture; - beforeEach( - async(() => { - const configure: ConfigureFn = testBed => { - testBed.configureTestingModule({ - declarations: [ SimpleComponent ] - }); - }; - - configureTests(configure).then(testBed => { - fixture = testBed.createComponent(SimpleComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + beforeEach(async(() => { + const configure: ConfigureFn = testBed => { + testBed.configureTestingModule({ + declarations: [SimpleComponent] }); - }) - ); + }; + + configureTests(configure).then(testBed => { + fixture = testBed.createComponent(SimpleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + })); it('should create', () => { expect(component).toBeTruthy(); @@ -36,3 +35,26 @@ describe('SimpleComponent', () => { expect(fixture.nativeElement).toMatchSnapshot(); }); }); + +describe.each([[SimpleComponent, AnotherComponent]])('Test components', ComponentType => { + let component: any; + let fixture: ComponentFixture; + + beforeEach(async(() => { + const configure: ConfigureFn = testBed => { + testBed.configureTestingModule({ + declarations: [ComponentType] + }); + }; + + configureTests(configure).then(testBed => { + fixture = testBed.createComponent(ComponentType); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/zone-patch/index.js b/src/zone-patch/index.js index 26178e5ab8..8c2631e1dc 100644 --- a/src/zone-patch/index.js +++ b/src/zone-patch/index.js @@ -36,8 +36,8 @@ const ambientZone = Zone.current; // inside of a `describe` but outside of a `beforeEach` or `it`. const syncZone = ambientZone.fork(new SyncTestZoneSpec('jest.describe')); function wrapDescribeInZone(describeBody) { - return function() { - return syncZone.run(describeBody, null, arguments); + return function(...args) { + return syncZone.run(describeBody, null, args); }; } @@ -48,61 +48,68 @@ function wrapTestInZone(testBody) { if (testBody === undefined) { return; } - return testBody.length === 0 - ? () => testProxyZone.run(testBody, null) - : done => testProxyZone.run(testBody, null, [done]); + return function(...args) { + return testProxyZone.run(testBody, null, args); + }; } +/** + * bind describe method to wrap describe.each function + */ const bindDescribe = originalJestFn => - function() { - const eachArguments = arguments; - return function(description, specDefinitions, timeout) { - arguments[1] = wrapDescribeInZone(specDefinitions); - return originalJestFn.apply(this, eachArguments).apply(this, arguments); + function(...eachArgs) { + return function(...args) { + args[1] = wrapDescribeInZone(args[1]); + return originalJestFn.apply(this, eachArgs).apply(this, args); + }; + }; + +/** + * bind test method to wrap test.each function + */ +const bindTest = originalJestFn => + function(...eachArgs) { + return function(...args) { + args[1] = wrapTestInZone(args[1]); + return originalJestFn.apply(this, eachArgs).apply(this, args); }; }; ['xdescribe', 'fdescribe', 'describe'].forEach(methodName => { const originaljestFn = env[methodName]; - env[methodName] = function(description, specDefinitions, timeout) { - arguments[1] = wrapDescribeInZone(specDefinitions); - return originaljestFn.apply(this, arguments); + env[methodName] = function(...args) { + args[1] = wrapDescribeInZone(args[1]); + return originaljestFn.apply(this, args); }; env[methodName].each = bindDescribe(originaljestFn.each); if (methodName === 'describe') { env[methodName].only = env['fdescribe']; env[methodName].skip = env['xdescribe']; - env[methodName].only.each = bindDescribe(originaljestFn.only.each); - env[methodName].skip.each = bindDescribe(originaljestFn.skip.each); } }); ['xit', 'fit', 'xtest', 'test', 'it'].forEach(methodName => { const originaljestFn = env[methodName]; - env[methodName] = function(description, specDefinitions, timeout) { - arguments[1] = wrapTestInZone(specDefinitions); - return originaljestFn.apply(this, arguments); + env[methodName] = function(...args) { + args[1] = wrapTestInZone(args[1]); + return originaljestFn.apply(this, args); }; - // The revised method will be populated to the final each method, so we only declare the method that in the new globals - env[methodName].each = originaljestFn.each; + env[methodName].each = bindTest(originaljestFn.each); if (methodName === 'test' || methodName === 'it') { env[methodName].only = env['fit']; - env[methodName].only.each = originaljestFn.only.each; - env[methodName].skip = env['xit']; - env[methodName].skip.each = originaljestFn.skip.each; - env[methodName].todo = function(description) { - return originaljestFn.todo.apply(this, arguments); + env[methodName].todo = function(...args) { + return originaljestFn.todo.apply(this, args); }; } }); ['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach(methodName => { const originaljestFn = env[methodName]; - env[methodName] = function(specDefinitions, timeout) { - arguments[0] = wrapTestInZone(specDefinitions); - return originaljestFn.apply(this, arguments); + env[methodName] = function(...args) { + args[0] = wrapTestInZone(args[0]); + return originaljestFn.apply(this, args); }; });