Skip to content
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

fix: test.each should be in ProxyZone #340

Merged
merged 5 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions example/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -20,7 +15,7 @@ describe('AppComponent', () => {
testBed.configureTestingModule({
declarations: [AppComponent],
imports: [NoopAnimationsModule],
schemas: [NO_ERRORS_SCHEMA],
schemas: [NO_ERRORS_SCHEMA]
});
};

Expand Down Expand Up @@ -50,8 +45,9 @@ describe('AppComponent', () => {
expect(compiled.querySelector('h1').textContent).toContain('app works!');
}));

it('looks async but is synchronous', <any>fakeAsync(
(): void => {
it(
'looks async but is synchronous',
<any>fakeAsync((): void => {
let flag = false;
setTimeout(() => {
flag = true;
Expand All @@ -61,8 +57,28 @@ describe('AppComponent', () => {
expect(flag).toBe(false);
tick(50);
expect(flag).toBe(true);
}
));
})
);

it(
'async should work',
<any>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');
147 changes: 76 additions & 71 deletions example/src/app/service/hero.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,109 @@
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<any>) => {
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<any>) => {
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<any>) => {
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<any>) => {
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) => {
debugger;
console.log('id, testData', id, testData, (window as any).Zone.current.name);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove debug code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, forget to remove

let actualData = {};

expect(actualData).toEqual(expectedData)
})
service.getHero(1).subscribe(data => (actualData = data));

backend
.expectOne((req: HttpRequest<any>) => {
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<any>) => {
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());
});
});
25 changes: 14 additions & 11 deletions src/zone-patch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ function wrapTestInZone(testBody) {
if (testBody === undefined) {
return;
}
return testBody.length === 0
? () => testProxyZone.run(testBody, null)
: done => testProxyZone.run(testBody, null, [done]);
return function() {
return testProxyZone.run(testBody, null, arguments);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about return (...args) => testProxyZone.run(testBody, null, args)? Not that it matters too much, it's just my preference of not using arguments when possible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I also think that is a good idea, will update it.

};
}

const bindDescribe = originalJestFn =>
Expand All @@ -62,6 +62,15 @@ const bindDescribe = originalJestFn =>
};
};

const bindTest = originalJestFn =>
function() {
const eachArguments = arguments;
return function(description, specDefinitions, timeout) {
arguments[1] = wrapTestInZone(specDefinitions);
return originalJestFn.apply(this, eachArguments).apply(this, arguments);
};
};

['xdescribe', 'fdescribe', 'describe'].forEach(methodName => {
const originaljestFn = env[methodName];
env[methodName] = function(description, specDefinitions, timeout) {
Expand All @@ -72,8 +81,6 @@ const bindDescribe = originalJestFn =>
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);
wtho marked this conversation as resolved.
Show resolved Hide resolved
}
});

Expand All @@ -83,17 +90,13 @@ const bindDescribe = originalJestFn =>
arguments[1] = wrapTestInZone(specDefinitions);
return originaljestFn.apply(this, arguments);
};
// 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) {
env[methodName].todo = function() {
return originaljestFn.todo.apply(this, arguments);
};
}
Expand Down