-
-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #124 from satanTime/issues/121
fix: better coverage for tokens
- Loading branch information
Showing
6 changed files
with
327 additions
and
34 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// tslint:disable:max-classes-per-file no-parameter-properties | ||
|
||
import { CommonModule } from '@angular/common'; | ||
import { Component, Inject, InjectionToken, NgModule } from '@angular/core'; | ||
|
||
export const MY_TOKEN_SINGLE = new (InjectionToken as any) /* A5 */('MY_TOKEN_SINGLE', { | ||
factory: () => 'MY_TOKEN_SINGLE', | ||
}); | ||
|
||
export const MY_TOKEN_MULTI = new (InjectionToken as any) /* A5 */('MY_TOKEN_MULTI', { | ||
factory: () => 'MY_TOKEN_MULTI', | ||
}); | ||
|
||
@Component({ | ||
selector: 'internal-component', | ||
template: '{{ tokenSingle | json }} {{ tokenMulti | json }}', | ||
}) | ||
export class TargetComponent { | ||
constructor( | ||
@Inject(MY_TOKEN_SINGLE) public readonly tokenSingle: string, | ||
@Inject(MY_TOKEN_MULTI) public readonly tokenMulti: string[] | ||
) {} | ||
} | ||
|
||
@NgModule({ | ||
declarations: [TargetComponent], | ||
exports: [TargetComponent], | ||
imports: [CommonModule], | ||
}) | ||
export class TargetModule {} |
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,108 @@ | ||
import { VERSION } from '@angular/core'; | ||
|
||
import { MockBuilder } from '../../lib/mock-builder'; | ||
import { MockRender } from '../../lib/mock-render'; | ||
|
||
import { MY_TOKEN_MULTI, MY_TOKEN_SINGLE, TargetComponent, TargetModule } from './fixtures'; | ||
|
||
// Because all tokens have factories the test should render them correctly. | ||
// There's no way to specify multi in a factory, so we don't get an array. | ||
describe('module-with-factory-tokens:real', () => { | ||
beforeEach(() => MockBuilder().keep(TargetModule)); | ||
|
||
it('renders all tokens', () => { | ||
// tslint:disable-next-line:no-magic-numbers | ||
if (parseInt(VERSION.major, 10) <= 5) { | ||
pending('Need Angular > 5'); | ||
return; | ||
} | ||
|
||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"MY_TOKEN_SINGLE" "MY_TOKEN_MULTI"'); | ||
}); | ||
}); | ||
|
||
// Because all tokens are kept the test should render them correctly. | ||
// There's no way to specify multi in a factory, so we don't get an array. | ||
describe('module-with-factory-tokens:keep', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(MY_TOKEN_SINGLE).keep(MY_TOKEN_MULTI)); | ||
|
||
it('renders all tokens', () => { | ||
// tslint:disable-next-line:no-magic-numbers | ||
if (parseInt(VERSION.major, 10) <= 5) { | ||
pending('Need Angular > 5'); | ||
return; | ||
} | ||
|
||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"MY_TOKEN_SINGLE" "MY_TOKEN_MULTI"'); | ||
}); | ||
}); | ||
|
||
// Preferred way. | ||
// Because tokens are provided in the testbed module with custom values the test should render them. | ||
describe('module-with-factory-tokens:mock-0', () => { | ||
beforeEach(() => | ||
MockBuilder(TargetComponent, TargetModule) | ||
.provide({ | ||
provide: MY_TOKEN_SINGLE, | ||
useValue: 'V1', | ||
}) | ||
.provide({ | ||
multi: true, | ||
provide: MY_TOKEN_MULTI, | ||
useValue: 'V2', | ||
}) | ||
); | ||
|
||
it('fails to render all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"V1" [ "V2" ]'); | ||
}); | ||
}); | ||
|
||
// Because all tokens are mocked in the module the test should render empty values. | ||
// The tokens will be added to provides with undefined values. | ||
// Result of the render is an empty string because there's no way to pass multi. | ||
describe('module-with-factory-tokens:mock-1', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule).mock(MY_TOKEN_SINGLE).mock(MY_TOKEN_MULTI)); | ||
|
||
it('renders all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual(''); | ||
}); | ||
}); | ||
|
||
// Because all tokens are mocked with custom values the test should render them. | ||
// There's no way to specify multi in a factory, so we don't get an array. | ||
describe('module-with-factory-tokens:mock-2', () => { | ||
beforeEach(() => | ||
MockBuilder(TargetComponent, TargetModule) | ||
.mock(MY_TOKEN_SINGLE, 'MOCKED_MY_TOKEN_SINGLE') | ||
.mock(MY_TOKEN_MULTI, 'MOCKED_MY_TOKEN_MULTI') | ||
); | ||
|
||
it('renders all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"MOCKED_MY_TOKEN_SINGLE" "MOCKED_MY_TOKEN_MULTI"'); | ||
}); | ||
}); | ||
|
||
// And the most interesting case. Because we don't touch tokens at all and mock the module | ||
// the tokens will used as they are with their factories. | ||
// Unfortunately it's quite tough to guess which tokens we can keep, mocks or omit and now | ||
// a user is responsible to specify tokens for his mock. | ||
describe('module-with-factory-tokens:mock-3', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule)); | ||
|
||
it('renders all tokens', () => { | ||
// tslint:disable-next-line:no-magic-numbers | ||
if (parseInt(VERSION.major, 10) <= 5) { | ||
pending('Need Angular > 5'); | ||
return; | ||
} | ||
|
||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"MY_TOKEN_SINGLE" "MY_TOKEN_MULTI"'); | ||
}); | ||
}); |
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,42 @@ | ||
// tslint:disable:max-classes-per-file no-parameter-properties | ||
|
||
import { CommonModule } from '@angular/common'; | ||
import { Component, Inject, InjectionToken, NgModule } from '@angular/core'; | ||
|
||
export const MY_TOKEN_SINGLE = new InjectionToken('MY_TOKEN_SINGLE'); | ||
|
||
export const MY_TOKEN_MULTI = new InjectionToken('MY_TOKEN_MULTI'); | ||
|
||
@Component({ | ||
selector: 'internal-component', | ||
template: '{{ tokenSingle | json }} {{ tokenMulti | json }}', | ||
}) | ||
export class TargetComponent { | ||
constructor( | ||
@Inject(MY_TOKEN_SINGLE) public readonly tokenSingle: string, | ||
@Inject(MY_TOKEN_MULTI) public readonly tokenMulti: string[] | ||
) {} | ||
} | ||
|
||
@NgModule({ | ||
declarations: [TargetComponent], | ||
exports: [TargetComponent], | ||
imports: [CommonModule], | ||
providers: [ | ||
{ | ||
provide: MY_TOKEN_SINGLE, | ||
useValue: 'MY_TOKEN_SINGLE', | ||
}, | ||
{ | ||
multi: true, | ||
provide: MY_TOKEN_MULTI, | ||
useValue: 'MY_TOKEN_MULTI', | ||
}, | ||
{ | ||
multi: true, | ||
provide: MY_TOKEN_MULTI, | ||
useValue: 'MY_TOKEN_MULTI_2', | ||
}, | ||
], | ||
}) | ||
export class TargetModule {} |
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,90 @@ | ||
import { MockBuilder } from '../../lib/mock-builder'; | ||
import { MockRender } from '../../lib/mock-render'; | ||
|
||
import { MY_TOKEN_MULTI, MY_TOKEN_SINGLE, TargetComponent, TargetModule } from './fixtures'; | ||
|
||
// Because all tokens are provided in the module the test should render them correctly. | ||
describe('module-with-tokens:real', () => { | ||
beforeEach(() => MockBuilder().keep(TargetModule)); | ||
|
||
it('renders all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"MY_TOKEN_SINGLE" [ "MY_TOKEN_MULTI", "MY_TOKEN_MULTI_2" ]'); | ||
}); | ||
}); | ||
|
||
// Because all tokens are kept in the module the test should render them correctly. | ||
describe('module-with-tokens:keep', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(MY_TOKEN_SINGLE).keep(MY_TOKEN_MULTI)); | ||
|
||
it('renders all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"MY_TOKEN_SINGLE" [ "MY_TOKEN_MULTI", "MY_TOKEN_MULTI_2" ]'); | ||
}); | ||
}); | ||
|
||
// Preferred way. | ||
// Because tokens are provided in the testbed module with custom values the test should render them. | ||
describe('module-with-tokens:mock-0', () => { | ||
beforeEach(() => | ||
MockBuilder(TargetComponent, TargetModule) | ||
.provide({ | ||
provide: MY_TOKEN_SINGLE, | ||
useValue: 'V1', | ||
}) | ||
.provide({ | ||
multi: true, | ||
provide: MY_TOKEN_MULTI, | ||
useValue: 'V2', | ||
}) | ||
.provide({ | ||
multi: true, | ||
provide: MY_TOKEN_MULTI, | ||
useValue: 'V3', | ||
}) | ||
); | ||
|
||
it('fails to render all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('"V1" [ "V2", "V3" ]'); | ||
}); | ||
}); | ||
|
||
// Because all tokens are mocked in the module the test should render empty values. | ||
// interesting is that for multi it's null, not undefined. | ||
describe('module-with-tokens:mock-1', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule).mock(MY_TOKEN_SINGLE).mock(MY_TOKEN_MULTI)); | ||
|
||
it('renders all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual('[ null, null ]'); | ||
}); | ||
}); | ||
|
||
// Because all tokens are mocked in the module with custom values the test should render them. | ||
describe('module-with-tokens:mock-2', () => { | ||
beforeEach(() => | ||
MockBuilder(TargetComponent, TargetModule) | ||
.mock(MY_TOKEN_SINGLE, 'MOCKED_MY_TOKEN_SINGLE') | ||
.mock(MY_TOKEN_MULTI, 'MOCKED_MY_TOKEN_MULTI') | ||
); | ||
|
||
it('renders all tokens', () => { | ||
const fixture = MockRender(TargetComponent); | ||
expect(fixture.nativeElement.innerText).toEqual( | ||
'"MOCKED_MY_TOKEN_SINGLE" [ "MOCKED_MY_TOKEN_MULTI", "MOCKED_MY_TOKEN_MULTI" ]' | ||
); | ||
}); | ||
}); | ||
|
||
// And the most complicated case. Because we don't touch tokens at all and mock the module | ||
// the tokens will be omitted from the final mock and injection will fail. | ||
// Unfortunately it's quite tough to guess which tokens we can keep, mocks or omit and now | ||
// a user is responsible to specify tokens for his mock. | ||
describe('module-with-tokens:mock-3', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule)); | ||
|
||
it('fails to render all tokens', () => { | ||
expect(() => MockRender(TargetComponent)).toThrowError(/InjectionToken/); | ||
}); | ||
}); |
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
Oops, something went wrong.