fix(zone.js): add issue numbers of `@types/jasmine` to the test cases (#34625)
Some cases will still need to use `spy as any` cast, because `@types/jasmine` have some issues, 1. The issue jasmine doesn't handle optional method properties, https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486 2. The issue jasmine doesn't handle overload method correctly, https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42455 PR Close #34625
This commit is contained in:
parent
ef4736d052
commit
41667de778
|
@ -29,8 +29,8 @@ const XSSI_PREFIX = ')]}\'\n';
|
|||
|
||||
{
|
||||
describe('XhrBackend', () => {
|
||||
let factory: MockXhrFactory = null !;
|
||||
let backend: HttpXhrBackend = null !;
|
||||
let factory: MockXhrFactory = null!;
|
||||
let backend: HttpXhrBackend = null!;
|
||||
beforeEach(() => {
|
||||
factory = new MockXhrFactory();
|
||||
backend = new HttpXhrBackend(factory);
|
||||
|
@ -92,7 +92,7 @@ const XSSI_PREFIX = ')]}\'\n';
|
|||
factory.mock.mockFlush(200, 'OK', JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as HttpResponse<{data: string}>;
|
||||
expect(res.body !.data).toBe('some data');
|
||||
expect(res.body!.data).toBe('some data');
|
||||
});
|
||||
it('handles a blank json response', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
|
@ -106,14 +106,14 @@ const XSSI_PREFIX = ')]}\'\n';
|
|||
factory.mock.mockFlush(500, 'Error', JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as any as HttpErrorResponse;
|
||||
expect(res.error !.data).toBe('some data');
|
||||
expect(res.error!.data).toBe('some data');
|
||||
});
|
||||
it('handles a json error response with XSSI prefix', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
factory.mock.mockFlush(500, 'Error', XSSI_PREFIX + JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as any as HttpErrorResponse;
|
||||
expect(res.error !.data).toBe('some data');
|
||||
expect(res.error!.data).toBe('some data');
|
||||
});
|
||||
it('handles a json string response', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
|
@ -128,7 +128,7 @@ const XSSI_PREFIX = ')]}\'\n';
|
|||
factory.mock.mockFlush(200, 'OK', XSSI_PREFIX + JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as HttpResponse<{data: string}>;
|
||||
expect(res.body !.data).toBe('some data');
|
||||
expect(res.body!.data).toBe('some data');
|
||||
});
|
||||
it('emits unsuccessful responses via the error path', done => {
|
||||
backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => {
|
||||
|
|
|
@ -38,7 +38,7 @@ runInEachFileSystem(() => {
|
|||
initMockFileSystem(fs, testFiles);
|
||||
|
||||
// Force single-process execution in unit tests by mocking available CPUs to 1.
|
||||
spyOn(os, 'cpus').and.returnValue([{ model: 'Mock CPU' } as any]);
|
||||
spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'} as any]);
|
||||
});
|
||||
|
||||
it('should run ngcc without errors for esm2015', () => {
|
||||
|
|
|
@ -13,6 +13,7 @@ describe('unlocker', () => {
|
|||
spyOn(process, 'on');
|
||||
require('../../../src/locking/lock_file_with_child_process/unlocker');
|
||||
// TODO: @JiaLiPassion, need to wait for @types/jasmine to handle the override case
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42455
|
||||
expect(process.on).toHaveBeenCalledWith('disconnect' as any, jasmine.any(Function));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -47,7 +47,7 @@ describe('CachedFileSystem', () => {
|
|||
let lstatSpy: jasmine.Spy;
|
||||
beforeEach(() => {
|
||||
// For most of the tests the files are not symbolic links.
|
||||
lstatSpy = spyOn(delegate, 'lstat').and.returnValue({ isSymbolicLink: () => false } as any);
|
||||
lstatSpy = spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false} as any);
|
||||
});
|
||||
|
||||
it('should call delegate if not in cache', () => {
|
||||
|
@ -93,7 +93,7 @@ describe('CachedFileSystem', () => {
|
|||
describe('invalidateCaches()', () => {
|
||||
it('should call the delegate `readFile()` if the path for the cached file has been invalidated',
|
||||
() => {
|
||||
spyOn(delegate, 'lstat').and.returnValue({ isSymbolicLink: () => false } as any);
|
||||
spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false} as any);
|
||||
const spy = spyOn(delegate, 'readFile').and.returnValue('Some contents');
|
||||
fs.readFile(abcPath); // Call once to fill the cache
|
||||
spy.calls.reset();
|
||||
|
@ -230,7 +230,7 @@ describe('CachedFileSystem', () => {
|
|||
describe('moveFile()', () => {
|
||||
beforeEach(() => {
|
||||
// `moveFile()` relies upon `readFile` which calls through to `lstat()`, so stub it out.
|
||||
spyOn(delegate, 'lstat').and.returnValue({ isSymbolicLink: () => false } as any);
|
||||
spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false} as any);
|
||||
});
|
||||
|
||||
it('should call delegate', () => {
|
||||
|
|
|
@ -69,6 +69,7 @@ describe('NodeJSFileSystem', () => {
|
|||
const result = fs.readdir(abcPath);
|
||||
expect(result).toEqual([relativeFrom('x'), relativeFrom('y/z')]);
|
||||
// TODO: @JiaLiPassion need to wait for @types/jasmine update to handle optional parameters.
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
expect(spy as any).toHaveBeenCalledWith(abcPath);
|
||||
});
|
||||
});
|
||||
|
@ -90,6 +91,7 @@ describe('NodeJSFileSystem', () => {
|
|||
const result = fs.stat(abcPath);
|
||||
expect(result).toBe(stats);
|
||||
// TODO: @JiaLiPassion need to wait for @types/jasmine update to handle optional parameters.
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
expect(spy as any).toHaveBeenCalledWith(abcPath);
|
||||
});
|
||||
});
|
||||
|
@ -173,10 +175,12 @@ describe('NodeJSFileSystem', () => {
|
|||
}
|
||||
return false;
|
||||
});
|
||||
spyOn(fs, 'stat').and.returnValue({ isDirectory: () => true } as any);
|
||||
const mkdirSyncSpy = spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => {
|
||||
spyOn(fs, 'stat').and.returnValue({isDirectory: () => true} as any);
|
||||
const mkdirSyncSpy =
|
||||
spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('It exists already. Supposedly.');
|
||||
throw new Error(
|
||||
'It exists already. Supposedly.');
|
||||
}
|
||||
}) as any);
|
||||
|
||||
|
@ -188,7 +192,8 @@ describe('NodeJSFileSystem', () => {
|
|||
|
||||
it('should fail if creating the directory throws and the directory does not exist', () => {
|
||||
spyOn(fs, 'exists').and.returnValue(false);
|
||||
spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => {
|
||||
spyOn(realFs, 'mkdirSync')
|
||||
.and.callFake(((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('Unable to create directory (for whatever reason).');
|
||||
}
|
||||
|
@ -212,7 +217,7 @@ describe('NodeJSFileSystem', () => {
|
|||
}
|
||||
return false;
|
||||
});
|
||||
spyOn(fs, 'stat').and.returnValue({ isDirectory: isDirectorySpy } as any);
|
||||
spyOn(fs, 'stat').and.returnValue({isDirectory: isDirectorySpy} as any);
|
||||
spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('It exists already. Supposedly.');
|
||||
|
|
|
@ -41,7 +41,9 @@ describe('ngc transformer command-line', () => {
|
|||
basePath = support.basePath;
|
||||
outDir = path.join(basePath, 'built');
|
||||
process.chdir(basePath);
|
||||
write = (fileName: string, content: string) => { support.write(fileName, content); };
|
||||
write = (fileName: string, content: string) => {
|
||||
support.write(fileName, content);
|
||||
};
|
||||
|
||||
write('tsconfig-base.json', `{
|
||||
"compilerOptions": {
|
||||
|
@ -96,8 +98,9 @@ describe('ngc transformer command-line', () => {
|
|||
});
|
||||
|
||||
describe('errors', () => {
|
||||
|
||||
beforeEach(() => { errorSpy.and.stub(); });
|
||||
beforeEach(() => {
|
||||
errorSpy.and.stub();
|
||||
});
|
||||
|
||||
it('should not print the stack trace if user input file does not exist', () => {
|
||||
writeConfig(`{
|
||||
|
@ -231,7 +234,6 @@ describe('ngc transformer command-line', () => {
|
|||
});
|
||||
|
||||
describe('compile ngfactory files', () => {
|
||||
|
||||
it('should compile ngfactory files that are not referenced by root files', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
|
@ -1122,7 +1124,6 @@ describe('ngc transformer command-line', () => {
|
|||
});
|
||||
|
||||
describe('with external symbol re-exports enabled', () => {
|
||||
|
||||
it('should be able to compile multiple libraries with summaries', () => {
|
||||
// Note: we need to emit the generated code for the libraries
|
||||
// into the node_modules, as that is the only way that we
|
||||
|
@ -1560,11 +1561,13 @@ describe('ngc transformer command-line', () => {
|
|||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
|
||||
const timerToken = 100;
|
||||
// TODO: @JiaLiPassion, need to wait @types/jasmine to handle optional method case
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
spyOn(ts.sys as any, 'setTimeout').and.callFake((callback: () => void) => {
|
||||
timer = callback;
|
||||
return timerToken;
|
||||
});
|
||||
// TODO: @JiaLiPassion, need to wait @types/jasmine to handle optional method case
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
spyOn(ts.sys as any, 'clearTimeout').and.callFake((token: number) => {
|
||||
if (token == timerToken) {
|
||||
timer = undefined;
|
||||
|
@ -1617,7 +1620,9 @@ describe('ngc transformer command-line', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
afterEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; });
|
||||
afterEach(() => {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
});
|
||||
|
||||
function writeAppConfig(location: string) {
|
||||
writeConfig(`{
|
||||
|
@ -1672,11 +1677,13 @@ describe('ngc transformer command-line', () => {
|
|||
`);
|
||||
}));
|
||||
|
||||
it('should recompile when the html file changes',
|
||||
expectRecompile(() => { write('greet.html', '<p> Hello {{name}} again!</p>'); }));
|
||||
it('should recompile when the html file changes', expectRecompile(() => {
|
||||
write('greet.html', '<p> Hello {{name}} again!</p>');
|
||||
}));
|
||||
|
||||
it('should recompile when the css file changes',
|
||||
expectRecompile(() => { write('greet.css', `p.greeting { color: blue }`); }));
|
||||
it('should recompile when the css file changes', expectRecompile(() => {
|
||||
write('greet.css', `p.greeting { color: blue }`);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('regressions', () => {
|
||||
|
@ -2041,8 +2048,8 @@ describe('ngc transformer command-line', () => {
|
|||
expect(exitCode).toBe(1, 'Compile was expected to fail');
|
||||
const srcPathWithSep = `lib/`;
|
||||
expect(messages[0])
|
||||
.toEqual(
|
||||
`${srcPathWithSep}test.component.ts(6,21): Error during template compile of 'TestComponent'
|
||||
.toEqual(`${
|
||||
srcPathWithSep}test.component.ts(6,21): Error during template compile of 'TestComponent'
|
||||
Tagged template expressions are not supported in metadata in 't1'
|
||||
't1' references 't2' at ${srcPathWithSep}indirect1.ts(3,27)
|
||||
't2' contains the error at ${srcPathWithSep}indirect2.ts(4,27).
|
||||
|
@ -2051,7 +2058,6 @@ describe('ngc transformer command-line', () => {
|
|||
});
|
||||
|
||||
describe('tree shakeable services', () => {
|
||||
|
||||
function compileService(source: string): string {
|
||||
write('service.ts', source);
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ describe('convertValueToOutputAst', () => {
|
|||
it('should convert all array elements, including undefined', () => {
|
||||
const ctx = null;
|
||||
const value = new Array(3).concat('foo');
|
||||
const expr = convertValueToOutputAst(ctx !, value) as o.LiteralArrayExpr;
|
||||
expect(expr instanceof o.LiteralArrayExpr);
|
||||
const expr = convertValueToOutputAst(ctx!, value) as o.LiteralArrayExpr;
|
||||
expect(expr instanceof o.LiteralArrayExpr).toBe(true);
|
||||
expect(expr.entries.length).toBe(4);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
expect(expr.entries[i] instanceof o.Expression).toBe(true);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {AUTO_STYLE, AnimationPlayer, animate, animateChild, group, query, sequence, stagger, state, style, transition, trigger, ɵAnimationGroupPlayer as AnimationGroupPlayer} from '@angular/animations';
|
||||
import {animate, animateChild, AnimationPlayer, AUTO_STYLE, group, query, sequence, stagger, state, style, transition, trigger, ɵAnimationGroupPlayer as AnimationGroupPlayer} from '@angular/animations';
|
||||
import {AnimationDriver, ɵAnimationEngine} from '@angular/animations/browser';
|
||||
import {matchesElement} from '@angular/animations/browser/src/render/shared';
|
||||
import {TransitionAnimationPlayer} from '@angular/animations/browser/src/render/transition_animation_engine';
|
||||
|
@ -13,21 +13,23 @@ import {ENTER_CLASSNAME, LEAVE_CLASSNAME} from '@angular/animations/browser/src/
|
|||
import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, HostBinding, ViewChild} from '@angular/core';
|
||||
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||
import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
|
||||
import {HostListener} from '../../src/metadata/directives';
|
||||
|
||||
(function() {
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
if (isNode) return;
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
if (isNode) return;
|
||||
|
||||
describe('animation query tests', function() {
|
||||
describe('animation query tests', function() {
|
||||
function getLog(): MockAnimationPlayer[] {
|
||||
return MockAnimationDriver.log as MockAnimationPlayer[];
|
||||
}
|
||||
|
||||
function resetLog() { MockAnimationDriver.log = []; }
|
||||
function resetLog() {
|
||||
MockAnimationDriver.log = [];
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetLog();
|
||||
|
@ -60,7 +62,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
query(
|
||||
'@*',
|
||||
[
|
||||
style({ backgroundColor: 'blue' }),
|
||||
style({backgroundColor: 'blue'}),
|
||||
animate(1000, style({backgroundColor: 'red'})),
|
||||
]),
|
||||
]),
|
||||
|
@ -68,26 +70,22 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
trigger(
|
||||
'a',
|
||||
[
|
||||
transition('* => 1', [
|
||||
animate(1000, style({ opacity: 0 }))
|
||||
]),
|
||||
transition('* => 1', [animate(1000, style({opacity: 0}))]),
|
||||
]),
|
||||
trigger(
|
||||
'b',
|
||||
[
|
||||
transition('* => 1', [
|
||||
animate(1000, style({ opacity: 0 })),
|
||||
query('.b-inner', [
|
||||
animate(1000, style({ opacity: 0 }))
|
||||
]),
|
||||
transition(
|
||||
'* => 1',
|
||||
[
|
||||
animate(1000, style({opacity: 0})),
|
||||
query('.b-inner', [animate(1000, style({opacity: 0}))]),
|
||||
]),
|
||||
]),
|
||||
trigger(
|
||||
'c',
|
||||
[
|
||||
transition('* => 1', [
|
||||
animate(1000, style({ opacity: 0 }))
|
||||
]),
|
||||
transition('* => 1', [animate(1000, style({opacity: 0}))]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
|
@ -137,7 +135,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
query(
|
||||
':animating',
|
||||
[
|
||||
style({ backgroundColor: 'blue' }),
|
||||
style({backgroundColor: 'blue'}),
|
||||
animate(1000, style({backgroundColor: 'red'})),
|
||||
]),
|
||||
]),
|
||||
|
@ -145,26 +143,22 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
trigger(
|
||||
'a',
|
||||
[
|
||||
transition('* => 1', [
|
||||
animate(1000, style({ opacity: 0 }))
|
||||
]),
|
||||
transition('* => 1', [animate(1000, style({opacity: 0}))]),
|
||||
]),
|
||||
trigger(
|
||||
'b',
|
||||
[
|
||||
transition('* => 1', [
|
||||
animate(1000, style({ opacity: 0 })),
|
||||
query('.b-inner', [
|
||||
animate(1000, style({ opacity: 0 }))
|
||||
]),
|
||||
transition(
|
||||
'* => 1',
|
||||
[
|
||||
animate(1000, style({opacity: 0})),
|
||||
query('.b-inner', [animate(1000, style({opacity: 0}))]),
|
||||
]),
|
||||
]),
|
||||
trigger(
|
||||
'c',
|
||||
[
|
||||
transition('* => 1', [
|
||||
animate(1000, style({ opacity: 0 }))
|
||||
]),
|
||||
transition('* => 1', [animate(1000, style({opacity: 0}))]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
|
@ -361,8 +355,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players[2].element.classList.contains('e-4')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be able to query all actively queued animation triggers via `@*:animating`',
|
||||
() => {
|
||||
it('should be able to query all actively queued animation triggers via `@*:animating`', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
|
@ -454,7 +447,8 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should collect styles for the same elements between queries', () => {
|
||||
it(
|
||||
'should collect styles for the same elements between queries', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
|
@ -546,12 +540,11 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
template: `
|
||||
<div [@myAnimation]="exp"></div>
|
||||
`,
|
||||
animations: [trigger('myAnimation', [transition(
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[transition(
|
||||
'* => go',
|
||||
[
|
||||
query(':self', style({opacity: '0.5'})),
|
||||
animate(1000, style({opacity: '1'}))
|
||||
])])]
|
||||
[query(':self', style({opacity: '0.5'})), animate(1000, style({opacity: '1'}))])])]
|
||||
})
|
||||
class Cmp {
|
||||
public exp: any;
|
||||
|
@ -793,9 +786,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(1);
|
||||
const player = players[0];
|
||||
|
||||
expect(player.keyframes).toEqual([
|
||||
{height: '0px', offset: 0}, {height: '444px', offset: 1}
|
||||
]);
|
||||
expect(player.keyframes).toEqual([{height: '0px', offset: 0}, {height: '444px', offset: 1}]);
|
||||
player.finish();
|
||||
|
||||
expect(player.element.style.height).toEqual('444px');
|
||||
|
@ -903,7 +894,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
|
||||
cmp.items = [0, 1, 2, 3, 4];
|
||||
|
||||
expect(() => { fixture.detectChanges(); }).toThrow();
|
||||
expect(() => {
|
||||
fixture.detectChanges();
|
||||
}).toThrow();
|
||||
|
||||
const children = cmp.container.nativeElement.querySelectorAll('.child');
|
||||
expect(children.length).toEqual(5);
|
||||
|
@ -1049,7 +1042,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public items !: any[];
|
||||
public items!: any[];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -1071,7 +1064,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(5);
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
let player = players[i] !;
|
||||
let player = players[i]!;
|
||||
expect(player.keyframes).toEqual([
|
||||
{opacity: '0', offset: 0},
|
||||
{opacity: '1', offset: 1},
|
||||
|
@ -1091,7 +1084,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(5);
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
let player = players[i] !;
|
||||
let player = players[i]!;
|
||||
expect(player.keyframes).toEqual([
|
||||
{opacity: '1', offset: 0},
|
||||
{opacity: '0', offset: 1},
|
||||
|
@ -1130,7 +1123,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
class Cmp {
|
||||
public exp: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public items !: any[];
|
||||
public items!: any[];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -1351,7 +1344,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
class Cmp {
|
||||
public exp: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public items !: any[];
|
||||
public items!: any[];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -1369,7 +1362,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(5);
|
||||
|
||||
let count = 0;
|
||||
players.forEach(p => { p.onDone(() => count++); });
|
||||
players.forEach(p => {
|
||||
p.onDone(() => count++);
|
||||
});
|
||||
|
||||
expect(count).toEqual(0);
|
||||
|
||||
|
@ -1404,7 +1399,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
class Cmp {
|
||||
public exp: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public items !: any[];
|
||||
public items!: any[];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -1422,7 +1417,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(5);
|
||||
|
||||
let count = 0;
|
||||
players.forEach(p => { p.onDone(() => count++); });
|
||||
players.forEach(p => {
|
||||
p.onDone(() => count++);
|
||||
});
|
||||
|
||||
expect(count).toEqual(0);
|
||||
|
||||
|
@ -1467,7 +1464,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
public exp1: any;
|
||||
public exp2: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public items !: any[];
|
||||
public items!: any[];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -1485,7 +1482,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(5);
|
||||
|
||||
let count = 0;
|
||||
players.forEach(p => { p.onDone(() => count++); });
|
||||
players.forEach(p => {
|
||||
p.onDone(() => count++);
|
||||
});
|
||||
|
||||
resetLog();
|
||||
|
||||
|
@ -1500,7 +1499,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
players = getLog();
|
||||
expect(players.length).toEqual(3);
|
||||
|
||||
players.forEach(p => { p.onDone(() => count++); });
|
||||
players.forEach(p => {
|
||||
p.onDone(() => count++);
|
||||
});
|
||||
|
||||
cmp.exp1 = 'off';
|
||||
fixture.detectChanges();
|
||||
|
@ -1537,7 +1538,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
class Cmp {
|
||||
public exp: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public items !: any[];
|
||||
public items!: any[];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -1555,7 +1556,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
expect(players.length).toEqual(5);
|
||||
|
||||
let count = 0;
|
||||
players.forEach(p => { p.onDone(() => count++); });
|
||||
players.forEach(p => {
|
||||
p.onDone(() => count++);
|
||||
});
|
||||
|
||||
expect(count).toEqual(0);
|
||||
|
||||
|
@ -1592,7 +1595,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
class Cmp {
|
||||
public exp: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public items !: any[];
|
||||
public items!: any[];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -1631,8 +1634,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
'myAnimation',
|
||||
[transition(
|
||||
'* => on',
|
||||
[query(
|
||||
':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])])]
|
||||
[query(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])])]
|
||||
})
|
||||
class ParentCmp {
|
||||
public exp: any;
|
||||
|
@ -1680,8 +1682,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
`,
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[transition(
|
||||
'* => on', [query(':leave', [animate(1000, style({opacity: 0}))])])])]
|
||||
[transition('* => on', [query(':leave', [animate(1000, style({opacity: 0}))])])])]
|
||||
})
|
||||
class ParentCmp {
|
||||
public exp: any;
|
||||
|
@ -2018,9 +2019,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger('child', [transition(
|
||||
'a => z',
|
||||
[style({opacity: 0}), animate(1000, style({opacity: 1}))])]),
|
||||
trigger(
|
||||
'child',
|
||||
[transition('a => z', [style({opacity: 0}), animate(1000, style({opacity: 1}))])]),
|
||||
trigger('parent', [transition(
|
||||
'a => z',
|
||||
[
|
||||
|
@ -2076,28 +2077,19 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger('w', [
|
||||
transition('* => go', [
|
||||
style({ width: 0 }),
|
||||
animate(1800, style({ width: '100px' }))
|
||||
])
|
||||
]),
|
||||
trigger('h', [
|
||||
transition('* => go', [
|
||||
style({ height: 0 }),
|
||||
animate(1500, style({ height: '100px' }))
|
||||
])
|
||||
]),
|
||||
trigger('parent', [
|
||||
transition('* => go', [
|
||||
style({ opacity: 0 }),
|
||||
animate(1000, style({ opacity: 1 })),
|
||||
query('.child', [
|
||||
animateChild()
|
||||
]),
|
||||
animate(1000, style({ opacity: 0 }))
|
||||
])
|
||||
])
|
||||
trigger(
|
||||
'w',
|
||||
[transition('* => go', [style({width: 0}), animate(1800, style({width: '100px'}))])]),
|
||||
trigger(
|
||||
'h', [transition(
|
||||
'* => go', [style({height: 0}), animate(1500, style({height: '100px'}))])]),
|
||||
trigger(
|
||||
'parent', [transition(
|
||||
'* => go',
|
||||
[
|
||||
style({opacity: 0}), animate(1000, style({opacity: 1})),
|
||||
query('.child', [animateChild()]), animate(1000, style({opacity: 0}))
|
||||
])])
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
|
@ -2141,9 +2133,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger('child', [transition(
|
||||
'* => go',
|
||||
[style({width: 0}), animate(1800, style({width: '100px'}))])]),
|
||||
trigger(
|
||||
'child',
|
||||
[transition('* => go', [style({width: 0}), animate(1800, style({width: '100px'}))])]),
|
||||
trigger('parent', [transition(
|
||||
'* => go',
|
||||
[
|
||||
|
@ -2200,9 +2192,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
style({opacity: 0}), animate(1000, style({opacity: 1})),
|
||||
query('.child', animateChild())
|
||||
])]),
|
||||
trigger('child', [transition(
|
||||
'* => go',
|
||||
[style({opacity: 0}), animate(1800, style({opacity: 1}))])])
|
||||
trigger(
|
||||
'child',
|
||||
[transition('* => go', [style({opacity: 0}), animate(1800, style({opacity: 1}))])])
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
|
@ -2434,7 +2426,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
const players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
|
||||
const element = players[0] !.element;
|
||||
const element = players[0]!.element;
|
||||
expect(element.innerText.trim()).toMatch(/this\s+child/mg);
|
||||
}));
|
||||
|
||||
|
@ -2477,7 +2469,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp !: boolean;
|
||||
public exp!: boolean;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -2515,19 +2507,17 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
it('should collect multiple root levels of :enter and :leave nodes', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
animations: [
|
||||
trigger('pageAnimation', [
|
||||
animations: [trigger(
|
||||
'pageAnimation',
|
||||
[
|
||||
transition(':enter', []),
|
||||
transition('* => *', [
|
||||
query(':leave', [
|
||||
animate('1s', style({ opacity: 0 }))
|
||||
], { optional: true }),
|
||||
query(':enter', [
|
||||
animate('1s', style({ opacity: 1 }))
|
||||
], { optional: true })
|
||||
transition(
|
||||
'* => *',
|
||||
[
|
||||
query(':leave', [animate('1s', style({opacity: 0}))], {optional: true}),
|
||||
query(':enter', [animate('1s', style({opacity: 1}))], {optional: true})
|
||||
])
|
||||
])
|
||||
],
|
||||
])],
|
||||
template: `
|
||||
<div [@pageAnimation]="status">
|
||||
<header>
|
||||
|
@ -2657,7 +2647,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp !: boolean;
|
||||
public exp!: boolean;
|
||||
public log: string[] = [];
|
||||
callback(event: any) {
|
||||
this.log.push(event.element.getAttribute('data-name') + '-' + event.phaseName);
|
||||
|
@ -2679,8 +2669,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
expect(cmp.log).toEqual([
|
||||
'c1-start', 'c1-done', 'c2-start', 'c2-done', 'p-start', 'c3-start', 'c3-done',
|
||||
'p-done'
|
||||
'c1-start', 'c1-done', 'c2-start', 'c2-done', 'p-start', 'c3-start', 'c3-done', 'p-done'
|
||||
]);
|
||||
}));
|
||||
|
||||
|
@ -2764,7 +2753,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
public log: string[] = [];
|
||||
public remove = false;
|
||||
|
||||
track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); }
|
||||
track(event: any) {
|
||||
this.log.push(`${event.triggerName}-${event.phaseName}`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -2790,7 +2781,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
class ChildCmp {
|
||||
public exp: any;
|
||||
public log: string[] = [];
|
||||
track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); }
|
||||
track(event: any) {
|
||||
this.log.push(`${event.triggerName}-${event.phaseName}`);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]});
|
||||
|
@ -2893,7 +2886,9 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
public child2Exp = '';
|
||||
public log: string[] = [];
|
||||
|
||||
track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); }
|
||||
track(event: any) {
|
||||
this.log.push(`${event.triggerName}-${event.phaseName}`);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
@ -2968,8 +2963,8 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
engine.flush();
|
||||
|
||||
expect(engine.players.length).toEqual(1); // child player, parent cover, parent player
|
||||
const groupPlayer = (engine.players[0] as TransitionAnimationPlayer)
|
||||
.getRealPlayer() as AnimationGroupPlayer;
|
||||
const groupPlayer = (engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as
|
||||
AnimationGroupPlayer;
|
||||
const childPlayer = groupPlayer.players.find(player => {
|
||||
if (player instanceof MockAnimationPlayer) {
|
||||
return matchesElement(player.element, '.child');
|
||||
|
@ -3309,7 +3304,7 @@ import {HostListener} from '../../src/metadata/directives';
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
function cancelAllPlayers(players: AnimationPlayer[]) {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {ResourceLoader, UrlResolver} from '@angular/compiler';
|
||||
import {MockResourceLoader} from '@angular/compiler/testing';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, Provider, RendererFactory2, RendererType2, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
|
||||
import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {isTextNode} from '@angular/platform-browser/testing/src/browser_util';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
@ -26,55 +26,53 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
|
||||
|
||||
(function() {
|
||||
let renderLog: RenderLog;
|
||||
let directiveLog: DirectiveLog;
|
||||
let renderLog: RenderLog;
|
||||
let directiveLog: DirectiveLog;
|
||||
|
||||
function createCompFixture<T>(template: string): ComponentFixture<TestComponent>;
|
||||
function createCompFixture<T>(template: string, compType: Type<T>): ComponentFixture<T>;
|
||||
function createCompFixture<T>(
|
||||
function createCompFixture<T>(template: string): ComponentFixture<TestComponent>;
|
||||
function createCompFixture<T>(template: string, compType: Type<T>): ComponentFixture<T>;
|
||||
function createCompFixture<T>(
|
||||
template: string, compType: Type<T> = <any>TestComponent): ComponentFixture<T> {
|
||||
TestBed.overrideComponent(compType, {set: new Component({template})});
|
||||
|
||||
initHelpers();
|
||||
|
||||
return TestBed.createComponent(compType);
|
||||
}
|
||||
}
|
||||
|
||||
function initHelpers(): void {
|
||||
function initHelpers(): void {
|
||||
renderLog = TestBed.inject(RenderLog);
|
||||
directiveLog = TestBed.inject(DirectiveLog);
|
||||
patchLoggingRenderer2(TestBed.inject(RendererFactory2), renderLog);
|
||||
}
|
||||
}
|
||||
|
||||
function queryDirs(el: DebugElement, dirType: Type<any>): any {
|
||||
function queryDirs(el: DebugElement, dirType: Type<any>): any {
|
||||
const nodes = el.queryAllNodes(By.directive(dirType));
|
||||
return nodes.map(node => node.injector.get(dirType));
|
||||
}
|
||||
}
|
||||
|
||||
function _bindSimpleProp<T>(bindAttr: string): ComponentFixture<TestComponent>;
|
||||
function _bindSimpleProp<T>(bindAttr: string, compType: Type<T>): ComponentFixture<T>;
|
||||
function _bindSimpleProp<T>(
|
||||
function _bindSimpleProp<T>(bindAttr: string): ComponentFixture<TestComponent>;
|
||||
function _bindSimpleProp<T>(bindAttr: string, compType: Type<T>): ComponentFixture<T>;
|
||||
function _bindSimpleProp<T>(
|
||||
bindAttr: string, compType: Type<T> = <any>TestComponent): ComponentFixture<T> {
|
||||
const template = `<div ${bindAttr}></div>`;
|
||||
return createCompFixture(template, compType);
|
||||
}
|
||||
}
|
||||
|
||||
function _bindSimpleValue(expression: any): ComponentFixture<TestComponent>;
|
||||
function _bindSimpleValue<T>(expression: any, compType: Type<T>): ComponentFixture<T>;
|
||||
function _bindSimpleValue<T>(
|
||||
function _bindSimpleValue(expression: any): ComponentFixture<TestComponent>;
|
||||
function _bindSimpleValue<T>(expression: any, compType: Type<T>): ComponentFixture<T>;
|
||||
function _bindSimpleValue<T>(
|
||||
expression: any, compType: Type<T> = <any>TestComponent): ComponentFixture<T> {
|
||||
return _bindSimpleProp(`[id]='${expression}'`, compType);
|
||||
}
|
||||
}
|
||||
|
||||
function _bindAndCheckSimpleValue(
|
||||
expression: any, compType: Type<any> = TestComponent): string[] {
|
||||
function _bindAndCheckSimpleValue(expression: any, compType: Type<any> = TestComponent): string[] {
|
||||
const ctx = _bindSimpleValue(expression, compType);
|
||||
ctx.detectChanges(false);
|
||||
return renderLog.log;
|
||||
}
|
||||
|
||||
describe(`ChangeDetection`, () => {
|
||||
}
|
||||
|
||||
describe(`ChangeDetection`, () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS});
|
||||
TestBed.configureTestingModule({
|
||||
|
@ -112,79 +110,101 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
});
|
||||
|
||||
describe('expressions', () => {
|
||||
it('should support literals',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue(10)).toEqual(['id=10']); }));
|
||||
it('should support literals', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue(10)).toEqual(['id=10']);
|
||||
}));
|
||||
|
||||
it('should strip quotes from literals',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('"str"')).toEqual(['id=str']); }));
|
||||
it('should strip quotes from literals', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('"str"')).toEqual(['id=str']);
|
||||
}));
|
||||
|
||||
it('should support newlines in literals',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('"a\n\nb"')).toEqual(['id=a\n\nb']); }));
|
||||
it('should support newlines in literals', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('"a\n\nb"')).toEqual(['id=a\n\nb']);
|
||||
}));
|
||||
|
||||
it('should support + operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('10 + 2')).toEqual(['id=12']); }));
|
||||
it('should support + operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('10 + 2')).toEqual(['id=12']);
|
||||
}));
|
||||
|
||||
it('should support - operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('10 - 2')).toEqual(['id=8']); }));
|
||||
it('should support - operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('10 - 2')).toEqual(['id=8']);
|
||||
}));
|
||||
|
||||
it('should support * operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('10 * 2')).toEqual(['id=20']); }));
|
||||
it('should support * operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('10 * 2')).toEqual(['id=20']);
|
||||
}));
|
||||
|
||||
it('should support / operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('10 / 2')).toEqual([`id=${5.0}`]);
|
||||
})); // dart exp=5.0, js exp=5
|
||||
|
||||
it('should support % operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('11 % 2')).toEqual(['id=1']); }));
|
||||
it('should support % operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('11 % 2')).toEqual(['id=1']);
|
||||
}));
|
||||
|
||||
it('should support == operations on identical',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 == 1')).toEqual(['id=true']); }));
|
||||
it('should support == operations on identical', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 == 1')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support != operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 != 1')).toEqual(['id=false']); }));
|
||||
it('should support != operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 != 1')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support == operations on coerceible',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 == true')).toEqual([`id=true`]); }));
|
||||
it('should support == operations on coerceible', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 == true')).toEqual([`id=true`]);
|
||||
}));
|
||||
|
||||
it('should support === operations on identical',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 === 1')).toEqual(['id=true']); }));
|
||||
it('should support === operations on identical', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 === 1')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support !== operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 !== 1')).toEqual(['id=false']); }));
|
||||
it('should support !== operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 !== 1')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support === operations on coerceible', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 === true')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support true < operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 < 2')).toEqual(['id=true']); }));
|
||||
it('should support true < operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 < 2')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support false < operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 < 1')).toEqual(['id=false']); }));
|
||||
it('should support false < operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('2 < 1')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support false > operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 > 2')).toEqual(['id=false']); }));
|
||||
it('should support false > operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 > 2')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support true > operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 > 1')).toEqual(['id=true']); }));
|
||||
it('should support true > operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('2 > 1')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support true <= operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 <= 2')).toEqual(['id=true']); }));
|
||||
it('should support true <= operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 <= 2')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support equal <= operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 <= 2')).toEqual(['id=true']); }));
|
||||
it('should support equal <= operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('2 <= 2')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support false <= operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 <= 1')).toEqual(['id=false']); }));
|
||||
it('should support false <= operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('2 <= 1')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support true >= operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 >= 1')).toEqual(['id=true']); }));
|
||||
it('should support true >= operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('2 >= 1')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support equal >= operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('2 >= 2')).toEqual(['id=true']); }));
|
||||
it('should support equal >= operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('2 >= 2')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support false >= operations',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 >= 2')).toEqual(['id=false']); }));
|
||||
it('should support false >= operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 >= 2')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support true && operations', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('true && true')).toEqual(['id=true']);
|
||||
|
@ -202,17 +222,21 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
expect(_bindAndCheckSimpleValue('false || false')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support negate',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('!true')).toEqual(['id=false']); }));
|
||||
it('should support negate', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('!true')).toEqual(['id=false']);
|
||||
}));
|
||||
|
||||
it('should support double negate',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('!!true')).toEqual(['id=true']); }));
|
||||
it('should support double negate', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('!!true')).toEqual(['id=true']);
|
||||
}));
|
||||
|
||||
it('should support true conditionals',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 < 2 ? 1 : 2')).toEqual(['id=1']); }));
|
||||
it('should support true conditionals', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 < 2 ? 1 : 2')).toEqual(['id=1']);
|
||||
}));
|
||||
|
||||
it('should support false conditionals',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('1 > 2 ? 1 : 2')).toEqual(['id=2']); }));
|
||||
it('should support false conditionals', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('1 > 2 ? 1 : 2')).toEqual(['id=2']);
|
||||
}));
|
||||
|
||||
it('should support keyed access to a list item', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('["foo", "bar"][0]')).toEqual(['id=foo']);
|
||||
|
@ -245,14 +269,14 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
describe('safe navigation operator', () => {
|
||||
it('should support reading properties of nulls', fakeAsync(() => {
|
||||
const ctx = _bindSimpleValue('address?.city', Person);
|
||||
ctx.componentInstance.address = null !;
|
||||
ctx.componentInstance.address = null!;
|
||||
ctx.detectChanges(false);
|
||||
expect(renderLog.log).toEqual(['id=null']);
|
||||
}));
|
||||
|
||||
it('should support calling methods on nulls', fakeAsync(() => {
|
||||
const ctx = _bindSimpleValue('address?.toString()', Person);
|
||||
ctx.componentInstance.address = null !;
|
||||
ctx.componentInstance.address = null!;
|
||||
ctx.detectChanges(false);
|
||||
expect(renderLog.log).toEqual(['id=null']);
|
||||
}));
|
||||
|
@ -273,7 +297,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
|
||||
it('should support short-circuting safe navigation', fakeAsync(() => {
|
||||
const ctx = _bindSimpleValue('value?.address.city', PersonHolder);
|
||||
ctx.componentInstance.value = null !;
|
||||
ctx.componentInstance.value = null!;
|
||||
ctx.detectChanges(false);
|
||||
expect(renderLog.log).toEqual(['id=null']);
|
||||
}));
|
||||
|
@ -301,7 +325,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
expect(() => {
|
||||
const ctx = _bindSimpleValue('value?.address.city', PersonHolder);
|
||||
const person = new Person();
|
||||
person.address = null !;
|
||||
person.address = null!;
|
||||
ctx.componentInstance.value = person;
|
||||
ctx.detectChanges(false);
|
||||
}).toThrow();
|
||||
|
@ -444,8 +468,9 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
expect(renderLog.log).toEqual(['id=BA']);
|
||||
}));
|
||||
|
||||
it('should escape values in literals that indicate interpolation',
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('"$"')).toEqual(['id=$']); }));
|
||||
it('should escape values in literals that indicate interpolation', fakeAsync(() => {
|
||||
expect(_bindAndCheckSimpleValue('"$"')).toEqual(['id=$']);
|
||||
}));
|
||||
|
||||
it('should read locals', fakeAsync(() => {
|
||||
const ctx = createCompFixture(
|
||||
|
@ -479,8 +504,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
}));
|
||||
|
||||
it('should support calling pure pipes with different number of arguments', fakeAsync(() => {
|
||||
const ctx =
|
||||
_bindSimpleValue('name | multiArgPipe:"a":"b" | multiArgPipe:0:1:2', Person);
|
||||
const ctx = _bindSimpleValue('name | multiArgPipe:"a":"b" | multiArgPipe:0:1:2', Person);
|
||||
ctx.componentInstance.name = 'value';
|
||||
ctx.detectChanges(false);
|
||||
expect(renderLog.loggedValues).toEqual(['value a b default 0 1 2']);
|
||||
|
@ -537,7 +561,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
it('should call pure pipes only if the arguments change', fakeAsync(() => {
|
||||
const ctx = _bindSimpleValue('name | countingPipe', Person);
|
||||
// change from undefined -> null
|
||||
ctx.componentInstance.name = null !;
|
||||
ctx.componentInstance.name = null!;
|
||||
ctx.detectChanges(false);
|
||||
expect(renderLog.loggedValues).toEqual(['null state:0']);
|
||||
ctx.detectChanges(false);
|
||||
|
@ -553,14 +577,9 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
// change from some value -> some other value
|
||||
ctx.componentInstance.name = 'bart';
|
||||
ctx.detectChanges(false);
|
||||
expect(renderLog.loggedValues).toEqual([
|
||||
'null state:0', 'bob state:1', 'bart state:2'
|
||||
]);
|
||||
expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1', 'bart state:2']);
|
||||
ctx.detectChanges(false);
|
||||
expect(renderLog.loggedValues).toEqual([
|
||||
'null state:0', 'bob state:1', 'bart state:2'
|
||||
]);
|
||||
|
||||
expect(renderLog.loggedValues).toEqual(['null state:0', 'bob state:1', 'bart state:2']);
|
||||
}));
|
||||
|
||||
modifiedInIvy('Pure pipes are instantiated differently in view engine and ivy')
|
||||
|
@ -662,7 +681,9 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
}));
|
||||
|
||||
it('should throw when trying to assign to a local', fakeAsync(() => {
|
||||
expect(() => { _bindSimpleProp('(event)="$event=1"'); })
|
||||
expect(() => {
|
||||
_bindSimpleProp('(event)="$event=1"');
|
||||
})
|
||||
.toThrowError(new RegExp(
|
||||
'Cannot assign value (.*) to template variable (.*). Template variables are read-only.'));
|
||||
}));
|
||||
|
@ -675,7 +696,6 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
expect(ctx.componentInstance.a).toEqual(1);
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('RendererFactory', () => {
|
||||
|
@ -685,6 +705,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
const rf = TestBed.inject(RendererFactory2);
|
||||
// TODO: @JiaLiPassion, need to wait @types/jasmine to fix the
|
||||
// optional method infer issue.
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
spyOn(rf as any, 'begin');
|
||||
spyOn(rf as any, 'end');
|
||||
expect(rf.begin).not.toHaveBeenCalled();
|
||||
|
@ -824,8 +845,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
]);
|
||||
}));
|
||||
|
||||
it('should be called on every detectChanges run, except for checkNoChanges',
|
||||
fakeAsync(() => {
|
||||
it('should be called on every detectChanges run, except for checkNoChanges', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<div testDirective="dir"></div>');
|
||||
|
||||
ctx.detectChanges(false);
|
||||
|
@ -864,9 +884,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
|
||||
ctx.detectChanges(false);
|
||||
|
||||
expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([
|
||||
'dir.ngAfterContentInit'
|
||||
]);
|
||||
expect(directiveLog.filter(['ngAfterContentInit'])).toEqual(['dir.ngAfterContentInit']);
|
||||
|
||||
// reset directives
|
||||
directiveLog.clear();
|
||||
|
@ -895,9 +913,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
}
|
||||
expect(errored).toBe(true);
|
||||
|
||||
expect(directiveLog.filter(['ngAfterContentInit'])).toEqual([
|
||||
'dir.ngAfterContentInit'
|
||||
]);
|
||||
expect(directiveLog.filter(['ngAfterContentInit'])).toEqual(['dir.ngAfterContentInit']);
|
||||
directiveLog.clear();
|
||||
|
||||
// Second change detection also fails, but this time ngAfterContentInit should not be
|
||||
|
@ -925,8 +941,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
]);
|
||||
}));
|
||||
|
||||
it('should be called on every detectChanges run, except for checkNoChanges',
|
||||
fakeAsync(() => {
|
||||
it('should be called on every detectChanges run, except for checkNoChanges', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<div testDirective="dir"></div>');
|
||||
|
||||
ctx.detectChanges(false);
|
||||
|
@ -1038,15 +1053,12 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
]);
|
||||
}));
|
||||
|
||||
it('should be called on every detectChanges run, except for checkNoChanges',
|
||||
fakeAsync(() => {
|
||||
it('should be called on every detectChanges run, except for checkNoChanges', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<div testDirective="dir"></div>');
|
||||
|
||||
ctx.detectChanges(false);
|
||||
|
||||
expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([
|
||||
'dir.ngAfterViewChecked'
|
||||
]);
|
||||
expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual(['dir.ngAfterViewChecked']);
|
||||
|
||||
// reset directives
|
||||
directiveLog.clear();
|
||||
|
@ -1059,9 +1071,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
// re-verify that changes are still detected
|
||||
ctx.detectChanges(false);
|
||||
|
||||
expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual([
|
||||
'dir.ngAfterViewChecked'
|
||||
]);
|
||||
expect(directiveLog.filter(['ngAfterViewChecked'])).toEqual(['dir.ngAfterViewChecked']);
|
||||
}));
|
||||
|
||||
it('should be called in reverse order so the child is always notified before the parent',
|
||||
|
@ -1136,14 +1146,11 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
ctx.detectChanges(false);
|
||||
ctx.destroy();
|
||||
|
||||
expect(directiveLog.filter(['ngOnDestroy'])).toEqual([
|
||||
'pipeWithOnDestroy.ngOnDestroy'
|
||||
]);
|
||||
expect(directiveLog.filter(['ngOnDestroy'])).toEqual(['pipeWithOnDestroy.ngOnDestroy']);
|
||||
}));
|
||||
|
||||
it('should call ngOnDestroy on an injectable class', fakeAsync(() => {
|
||||
TestBed.overrideDirective(
|
||||
TestDirective, {set: {providers: [InjectableWithLifecycle]}});
|
||||
TestBed.overrideDirective(TestDirective, {set: {providers: [InjectableWithLifecycle]}});
|
||||
|
||||
const ctx = createCompFixture('<div testDirective="dir"></div>', TestComponent);
|
||||
|
||||
|
@ -1302,7 +1309,6 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
ctx.detectChanges();
|
||||
|
||||
expect(renderLog.log).toEqual(['{{hello}}']);
|
||||
|
||||
}));
|
||||
|
||||
it('Reattaches in the original cd mode', fakeAsync(() => {
|
||||
|
@ -1321,14 +1327,13 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
ctx.detectChanges();
|
||||
expect(cmp.renderCount).toBe(count);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('multi directive order', () => {
|
||||
modifiedInIvy('order of bindings to directive inputs is different in ivy')
|
||||
.it('should follow the DI order for the same element', fakeAsync(() => {
|
||||
const ctx = createCompFixture(
|
||||
'<div orderCheck2="2" orderCheck0="0" orderCheck1="1"></div>');
|
||||
const ctx =
|
||||
createCompFixture('<div orderCheck2="2" orderCheck0="0" orderCheck1="1"></div>');
|
||||
|
||||
ctx.detectChanges(false);
|
||||
ctx.destroy();
|
||||
|
@ -1358,7 +1363,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
class Comp {
|
||||
name = 'Tom';
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('vc', {read: ViewContainerRef, static: true}) vc !: ViewContainerRef;
|
||||
@ViewChild('vc', {read: ViewContainerRef, static: true}) vc!: ViewContainerRef;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild(TemplateRef, {static: true}) template !: TemplateRef<any>;
|
||||
}
|
||||
|
@ -1380,8 +1385,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
|
||||
@Directive({selector: '[i]'})
|
||||
class DummyDirective {
|
||||
@Input()
|
||||
i: any;
|
||||
@Input() i: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -1391,7 +1395,9 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
})
|
||||
class MainComp {
|
||||
constructor(public cdRef: ChangeDetectorRef) {}
|
||||
log(id: string) { log.push(`main-${id}`); }
|
||||
log(id: string) {
|
||||
log.push(`main-${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -1401,11 +1407,12 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
})
|
||||
class OuterComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TemplateRef, {static: true})
|
||||
tpl !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>;
|
||||
|
||||
constructor(public cdRef: ChangeDetectorRef) {}
|
||||
log(id: string) { log.push(`outer-${id}`); }
|
||||
log(id: string) {
|
||||
log.push(`outer-${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -1415,15 +1422,15 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
})
|
||||
class InnerComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TemplateRef, {static: true})
|
||||
tpl !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input()
|
||||
outerTpl !: TemplateRef<any>;
|
||||
@Input() outerTpl!: TemplateRef<any>;
|
||||
|
||||
constructor(public cdRef: ChangeDetectorRef) {}
|
||||
log(id: string) { log.push(`inner-${id}`); }
|
||||
log(id: string) {
|
||||
log.push(`inner-${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
let ctx: ComponentFixture<MainComp>;
|
||||
|
@ -1444,13 +1451,11 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
|
||||
it('should dirty check projected views in regular order', () => {
|
||||
ctx.detectChanges(false);
|
||||
expect(log).toEqual(
|
||||
['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
|
||||
expect(log).toEqual(['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
|
||||
|
||||
log = [];
|
||||
ctx.detectChanges(false);
|
||||
expect(log).toEqual(
|
||||
['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
|
||||
expect(log).toEqual(['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
|
||||
});
|
||||
|
||||
it('should not dirty check projected views if neither the declaration nor the insertion place is dirty checked',
|
||||
|
@ -1526,8 +1531,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
@HostBinding('class.foo')
|
||||
fooClass = true;
|
||||
@HostBinding('class.foo') fooClass = true;
|
||||
}
|
||||
|
||||
const ctx =
|
||||
|
@ -1544,8 +1548,12 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
describe('lifecycle asserts', () => {
|
||||
let logged: string[];
|
||||
|
||||
function log(value: string) { logged.push(value); }
|
||||
function clearLog() { logged = []; }
|
||||
function log(value: string) {
|
||||
logged.push(value);
|
||||
}
|
||||
function clearLog() {
|
||||
logged = [];
|
||||
}
|
||||
|
||||
function expectOnceAndOnlyOnce(log: string) {
|
||||
expect(logged.indexOf(log) >= 0)
|
||||
|
@ -1554,7 +1562,9 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
.toBeTruthy(`'${log}' logged more than once. Log was ${JSON.stringify(logged)}`);
|
||||
}
|
||||
|
||||
beforeEach(() => { clearLog(); });
|
||||
beforeEach(() => {
|
||||
clearLog();
|
||||
});
|
||||
|
||||
enum LifetimeMethods {
|
||||
None = 0,
|
||||
|
@ -1588,16 +1598,26 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
private thrown = LifetimeMethods.None;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() inp !: boolean;
|
||||
@Input() inp!: boolean;
|
||||
@Output() outp = new EventEmitter<any>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); }
|
||||
ngOnInit() { this.check(LifetimeMethods.ngOnInit); }
|
||||
ngOnChanges() { this.check(LifetimeMethods.ngOnChanges); }
|
||||
ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); }
|
||||
ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); }
|
||||
ngDoCheck() {
|
||||
this.check(LifetimeMethods.ngDoCheck);
|
||||
}
|
||||
ngOnInit() {
|
||||
this.check(LifetimeMethods.ngOnInit);
|
||||
}
|
||||
ngOnChanges() {
|
||||
this.check(LifetimeMethods.ngOnChanges);
|
||||
}
|
||||
ngAfterViewInit() {
|
||||
this.check(LifetimeMethods.ngAfterViewInit);
|
||||
}
|
||||
ngAfterContentInit() {
|
||||
this.check(LifetimeMethods.ngAfterContentInit);
|
||||
}
|
||||
|
||||
private check(method: LifetimeMethods) {
|
||||
log(`MyChild::${LifetimeMethods[method]}()`);
|
||||
|
@ -1625,10 +1645,18 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
})
|
||||
class MyComponent {
|
||||
constructor(private changeDetectionRef: ChangeDetectorRef) {}
|
||||
ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); }
|
||||
ngOnInit() { this.check(LifetimeMethods.ngOnInit); }
|
||||
ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); }
|
||||
ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); }
|
||||
ngDoCheck() {
|
||||
this.check(LifetimeMethods.ngDoCheck);
|
||||
}
|
||||
ngOnInit() {
|
||||
this.check(LifetimeMethods.ngOnInit);
|
||||
}
|
||||
ngAfterViewInit() {
|
||||
this.check(LifetimeMethods.ngAfterViewInit);
|
||||
}
|
||||
ngAfterContentInit() {
|
||||
this.check(LifetimeMethods.ngAfterContentInit);
|
||||
}
|
||||
onOutp() {
|
||||
log('<RECURSION START>');
|
||||
this.changeDetectionRef.detectChanges();
|
||||
|
@ -1670,14 +1698,16 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
}
|
||||
|
||||
forEachMethod(LifetimeMethods.InitMethodsAndChanges, method => {
|
||||
it(`should ensure that init hooks are called once an only once with recursion in ${LifetimeMethods[method]} `,
|
||||
it(`should ensure that init hooks are called once an only once with recursion in ${
|
||||
LifetimeMethods[method]} `,
|
||||
() => {
|
||||
// Ensure all the init methods are called once.
|
||||
ensureOneInit({childRecursion: method, childThrows: LifetimeMethods.None});
|
||||
});
|
||||
});
|
||||
forEachMethod(LifetimeMethods.All, method => {
|
||||
it(`should ensure that init hooks are called once an only once with a throw in ${LifetimeMethods[method]} `,
|
||||
it(`should ensure that init hooks are called once an only once with a throw in ${
|
||||
LifetimeMethods[method]} `,
|
||||
() => {
|
||||
// Ensure all the init methods are called once.
|
||||
// the first cycle throws but the next cycle should complete the inits.
|
||||
|
@ -1686,7 +1716,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
@Injectable()
|
||||
|
@ -1750,7 +1780,9 @@ class DirectiveLog {
|
|||
this.entries.push(new DirectiveLogEntry(directiveName, method));
|
||||
}
|
||||
|
||||
clear() { this.entries = []; }
|
||||
clear() {
|
||||
this.entries = [];
|
||||
}
|
||||
|
||||
filter(methods: string[]): string[] {
|
||||
return this.entries.filter((entry) => methods.indexOf(entry.method) !== -1)
|
||||
|
@ -1762,32 +1794,44 @@ class DirectiveLog {
|
|||
@Pipe({name: 'countingPipe'})
|
||||
class CountingPipe implements PipeTransform {
|
||||
state: number = 0;
|
||||
transform(value: any) { return `${value} state:${this.state++}`; }
|
||||
transform(value: any) {
|
||||
return `${value} state:${this.state++}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'countingImpurePipe', pure: false})
|
||||
class CountingImpurePipe implements PipeTransform {
|
||||
state: number = 0;
|
||||
transform(value: any) { return `${value} state:${this.state++}`; }
|
||||
transform(value: any) {
|
||||
return `${value} state:${this.state++}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'pipeWithOnDestroy'})
|
||||
class PipeWithOnDestroy implements PipeTransform, OnDestroy {
|
||||
constructor(private directiveLog: DirectiveLog) {}
|
||||
|
||||
ngOnDestroy() { this.directiveLog.add('pipeWithOnDestroy', 'ngOnDestroy'); }
|
||||
ngOnDestroy() {
|
||||
this.directiveLog.add('pipeWithOnDestroy', 'ngOnDestroy');
|
||||
}
|
||||
|
||||
transform(value: any): any { return null; }
|
||||
transform(value: any): any {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'identityPipe'})
|
||||
class IdentityPipe implements PipeTransform {
|
||||
transform(value: any) { return value; }
|
||||
transform(value: any) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'wrappedPipe'})
|
||||
class WrappedPipe implements PipeTransform {
|
||||
transform(value: any) { return WrappedValue.wrap(value); }
|
||||
transform(value: any) {
|
||||
return WrappedValue.wrap(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'multiArgPipe'})
|
||||
|
@ -1856,7 +1900,9 @@ class Gh9882 implements AfterContentInit {
|
|||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
|
||||
}
|
||||
|
||||
ngAfterContentInit(): any { this._viewContainer.createEmbeddedView(this._templateRef); }
|
||||
ngAfterContentInit(): any {
|
||||
this._viewContainer.createEmbeddedView(this._templateRef);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[testDirective]', exportAs: 'testDirective'})
|
||||
|
@ -1865,21 +1911,25 @@ class TestDirective implements OnInit, DoCheck, OnChanges, AfterContentInit, Aft
|
|||
@Input() a: any;
|
||||
@Input() b: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
changes !: SimpleChanges;
|
||||
changes!: SimpleChanges;
|
||||
event: any;
|
||||
eventEmitter: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('testDirective') name !: string;
|
||||
@Input('testDirective') name!: string;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() throwOn !: string;
|
||||
@Input() throwOn!: string;
|
||||
|
||||
constructor(public log: DirectiveLog) {}
|
||||
|
||||
onEvent(event: any) { this.event = event; }
|
||||
onEvent(event: any) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
ngDoCheck() { this.log.add(this.name, 'ngDoCheck'); }
|
||||
ngDoCheck() {
|
||||
this.log.add(this.name, 'ngDoCheck');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.log.add(this.name, 'ngOnInit');
|
||||
|
@ -1937,20 +1987,24 @@ class InjectableWithLifecycle {
|
|||
name = 'injectable';
|
||||
constructor(public log: DirectiveLog) {}
|
||||
|
||||
ngOnDestroy() { this.log.add(this.name, 'ngOnDestroy'); }
|
||||
ngOnDestroy() {
|
||||
this.log.add(this.name, 'ngOnDestroy');
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[onDestroyDirective]'})
|
||||
class OnDestroyDirective implements OnDestroy {
|
||||
@Output('destroy') emitter = new EventEmitter<string>(false);
|
||||
|
||||
ngOnDestroy() { this.emitter.emit('destroyed'); }
|
||||
ngOnDestroy() {
|
||||
this.emitter.emit('destroyed');
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[orderCheck0]'})
|
||||
class OrderCheckDirective0 {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private _name !: string;
|
||||
private _name!: string;
|
||||
|
||||
@Input('orderCheck0')
|
||||
set name(value: string) {
|
||||
|
@ -1964,7 +2018,7 @@ class OrderCheckDirective0 {
|
|||
@Directive({selector: '[orderCheck1]'})
|
||||
class OrderCheckDirective1 {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private _name !: string;
|
||||
private _name!: string;
|
||||
|
||||
@Input('orderCheck1')
|
||||
set name(value: string) {
|
||||
|
@ -1978,7 +2032,7 @@ class OrderCheckDirective1 {
|
|||
@Directive({selector: '[orderCheck2]'})
|
||||
class OrderCheckDirective2 {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private _name !: string;
|
||||
private _name!: string;
|
||||
|
||||
@Input('orderCheck2')
|
||||
set name(value: string) {
|
||||
|
@ -2003,21 +2057,25 @@ class TestLocals {
|
|||
@Component({selector: 'root', template: 'empty'})
|
||||
class Person {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
age !: number;
|
||||
age!: number;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
name !: string;
|
||||
name!: string;
|
||||
address: Address|null = null;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
phones !: number[];
|
||||
phones!: number[];
|
||||
|
||||
init(name: string, address: Address|null = null) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
sayHi(m: any): string { return `Hi, ${m}`; }
|
||||
sayHi(m: any): string {
|
||||
return `Hi, ${m}`;
|
||||
}
|
||||
|
||||
passThrough(val: any): any { return val; }
|
||||
passThrough(val: any): any {
|
||||
return val;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
const address = this.address == null ? '' : ' address=' + this.address.toString();
|
||||
|
@ -2042,11 +2100,17 @@ class Address {
|
|||
return this._zipcode;
|
||||
}
|
||||
|
||||
set city(v) { this._city = v; }
|
||||
set city(v) {
|
||||
this._city = v;
|
||||
}
|
||||
|
||||
set zipcode(v) { this._zipcode = v; }
|
||||
set zipcode(v) {
|
||||
this._zipcode = v;
|
||||
}
|
||||
|
||||
toString(): string { return this.city || '-'; }
|
||||
toString(): string {
|
||||
return this.city || '-';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'root', template: 'empty'})
|
||||
|
@ -2063,14 +2127,16 @@ class TestData {
|
|||
@Component({selector: 'root', template: 'empty'})
|
||||
class TestDataWithGetter {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public fn !: Function;
|
||||
public fn!: Function;
|
||||
|
||||
get a() { return this.fn(); }
|
||||
get a() {
|
||||
return this.fn();
|
||||
}
|
||||
}
|
||||
|
||||
class Holder<T> {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
value !: T;
|
||||
value!: T;
|
||||
}
|
||||
|
||||
@Component({selector: 'root', template: 'empty'})
|
||||
|
|
|
@ -25,26 +25,41 @@ describe('global utils', () => {
|
|||
describe('publishDefaultGlobalUtils', () => {
|
||||
beforeEach(() => publishDefaultGlobalUtils());
|
||||
|
||||
it('should publish getComponent', () => { assertPublished('getComponent', getComponent); });
|
||||
it('should publish getComponent', () => {
|
||||
assertPublished('getComponent', getComponent);
|
||||
});
|
||||
|
||||
it('should publish getContext', () => { assertPublished('getContext', getContext); });
|
||||
it('should publish getContext', () => {
|
||||
assertPublished('getContext', getContext);
|
||||
});
|
||||
|
||||
it('should publish getListeners', () => { assertPublished('getListeners', getListeners); });
|
||||
it('should publish getListeners', () => {
|
||||
assertPublished('getListeners', getListeners);
|
||||
});
|
||||
|
||||
it('should publish getOwningComponent',
|
||||
() => { assertPublished('getOwningComponent', getOwningComponent); });
|
||||
it('should publish getOwningComponent', () => {
|
||||
assertPublished('getOwningComponent', getOwningComponent);
|
||||
});
|
||||
|
||||
it('should publish getRootComponents',
|
||||
() => { assertPublished('getRootComponents', getRootComponents); });
|
||||
it('should publish getRootComponents', () => {
|
||||
assertPublished('getRootComponents', getRootComponents);
|
||||
});
|
||||
|
||||
it('should publish getDirectives', () => { assertPublished('getDirectives', getDirectives); });
|
||||
it('should publish getDirectives', () => {
|
||||
assertPublished('getDirectives', getDirectives);
|
||||
});
|
||||
|
||||
it('should publish getHostComponent',
|
||||
() => { assertPublished('getHostElement', getHostElement); });
|
||||
it('should publish getHostComponent', () => {
|
||||
assertPublished('getHostElement', getHostElement);
|
||||
});
|
||||
|
||||
it('should publish getInjector', () => { assertPublished('getInjector', getInjector); });
|
||||
it('should publish getInjector', () => {
|
||||
assertPublished('getInjector', getInjector);
|
||||
});
|
||||
|
||||
it('should publish applyChanges', () => { assertPublished('applyChanges', applyChanges); });
|
||||
it('should publish applyChanges', () => {
|
||||
assertPublished('applyChanges', applyChanges);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -12,24 +12,21 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/
|
|||
import {CssSelector, CssSelectorList, SelectorFlags} from '../../src/render3/interfaces/projection';
|
||||
import {extractAttrsAndClassesFromSelector, getProjectAsAttrValue, isNodeMatchingSelector, isNodeMatchingSelectorList, stringifyCSSSelectorList} from '../../src/render3/node_selector_matcher';
|
||||
|
||||
function testLStaticData(tagName: string, attrs: TAttributes | null): TNode {
|
||||
return createTNode(null !, null, TNodeType.Element, 0, tagName, attrs);
|
||||
function testLStaticData(tagName: string, attrs: TAttributes|null): TNode {
|
||||
return createTNode(null!, null, TNodeType.Element, 0, tagName, attrs);
|
||||
}
|
||||
|
||||
describe('css selector matching', () => {
|
||||
function isMatching(
|
||||
tagName: string, attrsOrTNode: TAttributes | TNode | null, selector: CssSelector): boolean {
|
||||
tagName: string, attrsOrTNode: TAttributes|TNode|null, selector: CssSelector): boolean {
|
||||
const tNode = (!attrsOrTNode || Array.isArray(attrsOrTNode)) ?
|
||||
createTNode(null !, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) :
|
||||
createTNode(null!, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) :
|
||||
(attrsOrTNode as TNode);
|
||||
return isNodeMatchingSelector(tNode, selector, true);
|
||||
}
|
||||
|
||||
describe('isNodeMatchingSimpleSelector', () => {
|
||||
|
||||
|
||||
describe('element matching', () => {
|
||||
|
||||
it('should match element name only if names are the same', () => {
|
||||
expect(isMatching('span', null, ['span']))
|
||||
.toBeTruthy(`Selector 'span' should match <span>`);
|
||||
|
@ -55,11 +52,9 @@ describe('css selector matching', () => {
|
|||
});
|
||||
|
||||
describe('attributes matching', () => {
|
||||
|
||||
// TODO: do we need to differentiate no value and empty value? that is: title vs. title="" ?
|
||||
|
||||
it('should match single attribute without value', () => {
|
||||
|
||||
expect(isMatching('span', ['title', ''], [
|
||||
'', 'title', ''
|
||||
])).toBeTruthy(`Selector '[title]' should match <span title>`);
|
||||
|
@ -81,7 +76,8 @@ describe('css selector matching', () => {
|
|||
])).toBeFalsy(`Selector '[other]' should NOT match <span title="">'`);
|
||||
});
|
||||
|
||||
// TODO: Not sure how to fix this cases.
|
||||
// TODO: this case will not work, need more discussion
|
||||
// https://github.com/angular/angular/pull/34625#discussion_r401791275
|
||||
xit('should match namespaced attributes', () => {
|
||||
expect(isMatching(
|
||||
'span', [AttributeMarker.NamespaceURI, 'http://some/uri', 'title', 'name'],
|
||||
|
@ -228,7 +224,6 @@ describe('css selector matching', () => {
|
|||
});
|
||||
|
||||
describe('class matching', () => {
|
||||
|
||||
it('should match with a class selector when an element has multiple classes', () => {
|
||||
expect(isMatching('span', ['class', 'foo bar'], [
|
||||
'', SelectorFlags.CLASS, 'foo'
|
||||
|
@ -328,7 +323,6 @@ describe('css selector matching', () => {
|
|||
});
|
||||
|
||||
describe('negations', () => {
|
||||
|
||||
it('should match when negation part is null', () => {
|
||||
expect(isMatching('span', null, ['span'])).toBeTruthy(`Selector 'span' should match <span>`);
|
||||
});
|
||||
|
@ -436,13 +430,11 @@ describe('css selector matching', () => {
|
|||
expect(isMatching('div', ['name', 'name', 'title', '', 'class', 'foo bar'], selector))
|
||||
.toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isNodeMatchingSelectorList', () => {
|
||||
|
||||
function isAnyMatching(
|
||||
tagName: string, attrs: string[] | null, selector: CssSelectorList): boolean {
|
||||
tagName: string, attrs: string[]|null, selector: CssSelectorList): boolean {
|
||||
return isNodeMatchingSelectorList(testLStaticData(tagName, attrs), selector, false);
|
||||
}
|
||||
|
||||
|
@ -468,16 +460,18 @@ describe('css selector matching', () => {
|
|||
});
|
||||
|
||||
describe('reading the ngProjectAs attribute value', function() {
|
||||
|
||||
function testTNode(attrs: TAttributes | null) { return testLStaticData('tag', attrs); }
|
||||
function testTNode(attrs: TAttributes|null) {
|
||||
return testLStaticData('tag', attrs);
|
||||
}
|
||||
|
||||
it('should get ngProjectAs value if present', function() {
|
||||
expect(getProjectAsAttrValue(testTNode([AttributeMarker.ProjectAs, ['tag', 'foo', 'bar']])))
|
||||
.toEqual(['tag', 'foo', 'bar']);
|
||||
});
|
||||
|
||||
it('should return null if there are no attributes',
|
||||
function() { expect(getProjectAsAttrValue(testTNode(null))).toBe(null); });
|
||||
it('should return null if there are no attributes', function() {
|
||||
expect(getProjectAsAttrValue(testTNode(null))).toBe(null);
|
||||
});
|
||||
|
||||
it('should return if ngProjectAs is not present', function() {
|
||||
expect(getProjectAsAttrValue(testTNode(['foo', 'bar']))).toBe(null);
|
||||
|
@ -486,15 +480,13 @@ describe('css selector matching', () => {
|
|||
it('should not accidentally identify ngProjectAs in attribute values', function() {
|
||||
expect(getProjectAsAttrValue(testTNode(['foo', AttributeMarker.ProjectAs]))).toBe(null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('stringifyCSSSelectorList', () => {
|
||||
|
||||
it('should stringify selector with a tag name only',
|
||||
() => { expect(stringifyCSSSelectorList([['button']])).toBe('button'); });
|
||||
it('should stringify selector with a tag name only', () => {
|
||||
expect(stringifyCSSSelectorList([['button']])).toBe('button');
|
||||
});
|
||||
|
||||
it('should stringify selector with attributes', () => {
|
||||
expect(stringifyCSSSelectorList([['', 'id', '']])).toBe('[id]');
|
||||
|
|
|
@ -17,6 +17,7 @@ describe('utils', () => {
|
|||
beforeEach(() => {
|
||||
// TODO: @JiaLiPassion, need to wait @types/jasmine to fix the wrong return
|
||||
// type infer issue.
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
setTimeoutSpy = spyOn(window, 'setTimeout').and.returnValue(42 as any);
|
||||
clearTimeoutSpy = spyOn(window, 'clearTimeout');
|
||||
});
|
||||
|
@ -83,8 +84,9 @@ describe('utils', () => {
|
|||
expect(camelToDashCase('foo1Bar2Baz3Qux4')).toBe('foo1-bar2-baz3-qux4');
|
||||
});
|
||||
|
||||
it('should keep existing dashes',
|
||||
() => { expect(camelToDashCase('fooBar-baz-Qux')).toBe('foo-bar-baz--qux'); });
|
||||
it('should keep existing dashes', () => {
|
||||
expect(camelToDashCase('fooBar-baz-Qux')).toBe('foo-bar-baz--qux');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCustomEvent()', () => {
|
||||
|
@ -99,7 +101,6 @@ describe('utils', () => {
|
|||
expect(event.cancelable).toBe(false);
|
||||
expect(event.detail).toEqual(value);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isElement()', () => {
|
||||
|
@ -131,7 +132,7 @@ describe('utils', () => {
|
|||
it('should return true for functions', () => {
|
||||
const obj = {foo: function() {}, bar: () => null, baz() {}};
|
||||
const fns = [
|
||||
function(){},
|
||||
function() {},
|
||||
() => null,
|
||||
obj.foo,
|
||||
obj.bar,
|
||||
|
@ -182,7 +183,7 @@ describe('utils', () => {
|
|||
</ul>
|
||||
</div>
|
||||
`;
|
||||
li = div.querySelector('li') !;
|
||||
li = div.querySelector('li')!;
|
||||
});
|
||||
|
||||
it('should return whether the element matches the selector', () => {
|
||||
|
@ -218,7 +219,9 @@ describe('utils', () => {
|
|||
];
|
||||
|
||||
values.forEach((v1, i) => {
|
||||
values.forEach((v2, j) => { expect(strictEquals(v1, v2)).toBe(i === j); });
|
||||
values.forEach((v2, j) => {
|
||||
expect(strictEquals(v1, v2)).toBe(i === j);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angular/core';
|
||||
import {forwardRef, Inject, ReflectiveInjector, resolveForwardRef} from '@angular/core';
|
||||
|
||||
{
|
||||
describe('forwardRef examples', () => {
|
||||
|
@ -26,7 +26,9 @@ import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angula
|
|||
|
||||
// Door attempts to inject Lock, despite it not being defined yet.
|
||||
// forwardRef makes this possible.
|
||||
constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { this.lock = lock; }
|
||||
constructor(@Inject(forwardRef(() => Lock)) lock: Lock) {
|
||||
this.lock = lock;
|
||||
}
|
||||
}
|
||||
|
||||
// Only at this point Lock is defined.
|
||||
|
|
|
@ -17,7 +17,9 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow
|
|||
let fakeConsole: any;
|
||||
if (isNode) return;
|
||||
|
||||
beforeEach(() => { fakeConsole = {warn: jasmine.createSpy('console.warn')}; });
|
||||
beforeEach(() => {
|
||||
fakeConsole = {warn: jasmine.createSpy('console.warn')};
|
||||
});
|
||||
|
||||
describe('with no custom loader', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -61,7 +63,9 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow
|
|||
// Inject the NgZone so that we can make it available to the plugin through a fake
|
||||
// EventManager.
|
||||
let ngZone: NgZone;
|
||||
beforeEach(inject([NgZone], (z: NgZone) => { ngZone = z; }));
|
||||
beforeEach(inject([NgZone], (z: NgZone) => {
|
||||
ngZone = z;
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
originalHammerGlobal = (window as any).Hammer;
|
||||
|
@ -84,13 +88,15 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow
|
|||
plugin = new HammerGesturesPlugin(document, hammerConfig, fakeConsole, loader);
|
||||
|
||||
// Use a fake EventManager that has access to the NgZone.
|
||||
plugin.manager = { getZone: () => ngZone } as EventManager;
|
||||
plugin.manager = {getZone: () => ngZone} as EventManager;
|
||||
|
||||
someElement = document.createElement('div');
|
||||
someListener = () => {};
|
||||
});
|
||||
|
||||
afterEach(() => { (window as any).Hammer = originalHammerGlobal; });
|
||||
afterEach(() => {
|
||||
(window as any).Hammer = originalHammerGlobal;
|
||||
});
|
||||
|
||||
it('should not log a warning when HammerJS is not loaded', () => {
|
||||
plugin.addEventListener(someElement, 'swipe', () => {});
|
||||
|
|
|
@ -51,7 +51,6 @@ import {KeyEventsPlugin} from '@angular/platform-browser/src/dom/events/key_even
|
|||
.toEqual({'domEventName': 'keydown', 'fullKey': 'control.shift'});
|
||||
expect(KeyEventsPlugin.parseEventName('keyup.control.shift'))
|
||||
.toEqual({'domEventName': 'keyup', 'fullKey': 'control.shift'});
|
||||
|
||||
});
|
||||
|
||||
it('should alias esc to escape', () => {
|
||||
|
@ -67,6 +66,5 @@ import {KeyEventsPlugin} from '@angular/platform-browser/src/dom/events/key_even
|
|||
expect(() => plugin.addGlobalEventListener('window', 'keyup.control.esc', () => {}))
|
||||
.not.toThrowError();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3708,8 +3708,7 @@ describe('Integration', () => {
|
|||
router.navigate(['/user/:fedor']);
|
||||
advance(fixture);
|
||||
|
||||
expect(navigateSpy.calls.mostRecent().args[1] !.queryParams);
|
||||
|
||||
expect(navigateSpy.calls.mostRecent().args[1]!.queryParams);
|
||||
})));
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {PLATFORM_ID} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {NgswCommChannel} from '@angular/service-worker/src/low_level';
|
||||
import {SwRegistrationOptions, ngswCommChannelFactory} from '@angular/service-worker/src/module';
|
||||
import {ngswCommChannelFactory, SwRegistrationOptions} from '@angular/service-worker/src/module';
|
||||
import {SwPush} from '@angular/service-worker/src/push';
|
||||
import {SwUpdate} from '@angular/service-worker/src/update';
|
||||
import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockServiceWorkerRegistration, patchDecodeBase64} from '@angular/service-worker/testing/mock';
|
||||
|
@ -32,14 +32,18 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
|
||||
mock.setupSw();
|
||||
|
||||
(comm as any).registration.subscribe((reg: any) => { done(); });
|
||||
(comm as any).registration.subscribe((reg: any) => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('can access the registration when it comes after subscription', done => {
|
||||
const mock = new MockServiceWorkerContainer();
|
||||
const comm = new NgswCommChannel(mock as any);
|
||||
const regPromise = mock.getRegistration() as any as MockServiceWorkerRegistration;
|
||||
|
||||
(comm as any).registration.subscribe((reg: any) => { done(); });
|
||||
(comm as any).registration.subscribe((reg: any) => {
|
||||
done();
|
||||
});
|
||||
|
||||
mock.setupSw();
|
||||
});
|
||||
|
@ -158,7 +162,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
});
|
||||
|
||||
describe('requestSubscription()', () => {
|
||||
it('returns a promise that resolves to the subscription', async() => {
|
||||
it('returns a promise that resolves to the subscription', async () => {
|
||||
const promise = push.requestSubscription({serverPublicKey: 'test'});
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
|
||||
|
@ -166,7 +170,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
expect(sub).toEqual(jasmine.any(MockPushSubscription));
|
||||
});
|
||||
|
||||
it('calls `PushManager.subscribe()` (with appropriate options)', async() => {
|
||||
it('calls `PushManager.subscribe()` (with appropriate options)', async () => {
|
||||
const decode = (charCodeArr: Uint8Array) =>
|
||||
Array.from(charCodeArr).map(c => String.fromCharCode(c)).join('');
|
||||
|
||||
|
@ -183,12 +187,12 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
userVisibleOnly: true,
|
||||
});
|
||||
|
||||
const actualAppServerKey = pmSubscribeSpy.calls.first().args[0] !.applicationServerKey;
|
||||
const actualAppServerKey = pmSubscribeSpy.calls.first().args[0]!.applicationServerKey;
|
||||
const actualAppServerKeyStr = decode(actualAppServerKey as Uint8Array);
|
||||
expect(actualAppServerKeyStr).toBe(appServerKeyStr);
|
||||
});
|
||||
|
||||
it('emits the new `PushSubscription` on `SwPush.subscription`', async() => {
|
||||
it('emits the new `PushSubscription` on `SwPush.subscription`', async () => {
|
||||
const subscriptionSpy = jasmine.createSpy('subscriptionSpy');
|
||||
push.subscription.subscribe(subscriptionSpy);
|
||||
const sub = await push.requestSubscription({serverPublicKey: 'test'});
|
||||
|
@ -204,7 +208,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
psUnsubscribeSpy = spyOn(MockPushSubscription.prototype, 'unsubscribe').and.callThrough();
|
||||
});
|
||||
|
||||
it('rejects if currently not subscribed to push notifications', async() => {
|
||||
it('rejects if currently not subscribed to push notifications', async () => {
|
||||
try {
|
||||
await push.unsubscribe();
|
||||
throw new Error('`unsubscribe()` should fail');
|
||||
|
@ -213,15 +217,17 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
}
|
||||
});
|
||||
|
||||
it('calls `PushSubscription.unsubscribe()`', async() => {
|
||||
it('calls `PushSubscription.unsubscribe()`', async () => {
|
||||
await push.requestSubscription({serverPublicKey: 'test'});
|
||||
await push.unsubscribe();
|
||||
|
||||
expect(psUnsubscribeSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('rejects if `PushSubscription.unsubscribe()` fails', async() => {
|
||||
psUnsubscribeSpy.and.callFake(() => { throw new Error('foo'); });
|
||||
it('rejects if `PushSubscription.unsubscribe()` fails', async () => {
|
||||
psUnsubscribeSpy.and.callFake(() => {
|
||||
throw new Error('foo');
|
||||
});
|
||||
|
||||
try {
|
||||
await push.requestSubscription({serverPublicKey: 'test'});
|
||||
|
@ -232,7 +238,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
}
|
||||
});
|
||||
|
||||
it('rejects if `PushSubscription.unsubscribe()` returns false', async() => {
|
||||
it('rejects if `PushSubscription.unsubscribe()` returns false', async () => {
|
||||
psUnsubscribeSpy.and.returnValue(Promise.resolve(false));
|
||||
|
||||
try {
|
||||
|
@ -244,7 +250,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
}
|
||||
});
|
||||
|
||||
it('emits `null` on `SwPush.subscription`', async() => {
|
||||
it('emits `null` on `SwPush.subscription`', async () => {
|
||||
const subscriptionSpy = jasmine.createSpy('subscriptionSpy');
|
||||
push.subscription.subscribe(subscriptionSpy);
|
||||
|
||||
|
@ -254,7 +260,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
expect(subscriptionSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('does not emit on `SwPush.subscription` on failure', async() => {
|
||||
it('does not emit on `SwPush.subscription` on failure', async () => {
|
||||
const subscriptionSpy = jasmine.createSpy('subscriptionSpy');
|
||||
const initialSubEmit = new Promise(resolve => subscriptionSpy.and.callFake(resolve));
|
||||
|
||||
|
@ -271,7 +277,9 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
subscriptionSpy.calls.reset();
|
||||
|
||||
// Error due to `PushSubscription.unsubscribe()` error.
|
||||
psUnsubscribeSpy.and.callFake(() => { throw new Error('foo'); });
|
||||
psUnsubscribeSpy.and.callFake(() => {
|
||||
throw new Error('foo');
|
||||
});
|
||||
await push.unsubscribe().catch(() => undefined);
|
||||
expect(subscriptionSpy).not.toHaveBeenCalled();
|
||||
|
||||
|
@ -338,7 +346,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
push.subscription.subscribe(subscriptionSpy);
|
||||
});
|
||||
|
||||
it('emits on worker-driven changes (i.e. when the controller changes)', async() => {
|
||||
it('emits on worker-driven changes (i.e. when the controller changes)', async () => {
|
||||
// Initial emit for the current `ServiceWorkerController`.
|
||||
await nextSubEmitPromise;
|
||||
expect(subscriptionSpy).toHaveBeenCalledTimes(1);
|
||||
|
@ -353,7 +361,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
expect(subscriptionSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('emits on subscription changes (i.e. when subscribing/unsubscribing)', async() => {
|
||||
it('emits on subscription changes (i.e. when subscribing/unsubscribing)', async () => {
|
||||
await nextSubEmitPromise;
|
||||
subscriptionSpy.calls.reset();
|
||||
|
||||
|
@ -391,11 +399,16 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
});
|
||||
|
||||
it('gives an error when registering', done => {
|
||||
push.requestSubscription({serverPublicKey: 'test'}).catch(err => { done(); });
|
||||
push.requestSubscription({serverPublicKey: 'test'}).catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('gives an error when unsubscribing',
|
||||
done => { push.unsubscribe().catch(err => { done(); }); });
|
||||
it('gives an error when unsubscribing', done => {
|
||||
push.unsubscribe().catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -461,7 +474,9 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
});
|
||||
});
|
||||
return update.activateUpdate()
|
||||
.catch(err => { expect(err.message).toEqual('Failed to activate'); })
|
||||
.catch(err => {
|
||||
expect(err.message).toEqual('Failed to activate');
|
||||
})
|
||||
.then(() => done())
|
||||
.catch(err => done.fail(err));
|
||||
});
|
||||
|
@ -475,8 +490,12 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
expect(() => TestBed.inject(SwUpdate)).not.toThrow();
|
||||
});
|
||||
describe('with no SW', () => {
|
||||
beforeEach(() => { comm = new NgswCommChannel(undefined); });
|
||||
it('can be instantiated', () => { update = new SwUpdate(comm); });
|
||||
beforeEach(() => {
|
||||
comm = new NgswCommChannel(undefined);
|
||||
});
|
||||
it('can be instantiated', () => {
|
||||
update = new SwUpdate(comm);
|
||||
});
|
||||
it('does not crash on subscription to observables', () => {
|
||||
update = new SwUpdate(comm);
|
||||
update.available.toPromise().catch(err => fail(err));
|
||||
|
@ -484,11 +503,15 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
|||
});
|
||||
it('gives an error when checking for updates', done => {
|
||||
update = new SwUpdate(comm);
|
||||
update.checkForUpdate().catch(err => { done(); });
|
||||
update.checkForUpdate().catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('gives an error when activating updates', done => {
|
||||
update = new SwUpdate(comm);
|
||||
update.activateUpdate().catch(err => { done(); });
|
||||
update.activateUpdate().catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {ApplicationRef, PLATFORM_ID} from '@angular/core';
|
||||
import {TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {fakeAsync, flushMicrotasks, TestBed, tick} from '@angular/core/testing';
|
||||
import {Subject} from 'rxjs';
|
||||
import {filter, take} from 'rxjs/operators';
|
||||
|
||||
|
@ -33,7 +33,7 @@ describe('ServiceWorkerModule', () => {
|
|||
spyOn(navigator.serviceWorker, 'register').and.returnValue(Promise.resolve(null as any)));
|
||||
|
||||
describe('register()', () => {
|
||||
const configTestBed = async(opts: SwRegistrationOptions) => {
|
||||
const configTestBed = async (opts: SwRegistrationOptions) => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ServiceWorkerModule.register('sw.js', opts)],
|
||||
providers: [{provide: PLATFORM_ID, useValue: 'browser'}],
|
||||
|
@ -42,35 +42,35 @@ describe('ServiceWorkerModule', () => {
|
|||
await untilStable();
|
||||
};
|
||||
|
||||
it('sets the registration options', async() => {
|
||||
it('sets the registration options', async () => {
|
||||
await configTestBed({enabled: true, scope: 'foo'});
|
||||
|
||||
expect(TestBed.inject(SwRegistrationOptions)).toEqual({enabled: true, scope: 'foo'});
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: 'foo'});
|
||||
});
|
||||
|
||||
it('can disable the SW', async() => {
|
||||
it('can disable the SW', async () => {
|
||||
await configTestBed({enabled: false});
|
||||
|
||||
expect(TestBed.inject(SwUpdate).isEnabled).toBe(false);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can enable the SW', async() => {
|
||||
it('can enable the SW', async () => {
|
||||
await configTestBed({enabled: true});
|
||||
|
||||
expect(TestBed.inject(SwUpdate).isEnabled).toBe(true);
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
});
|
||||
|
||||
it('defaults to enabling the SW', async() => {
|
||||
it('defaults to enabling the SW', async () => {
|
||||
await configTestBed({});
|
||||
|
||||
expect(TestBed.inject(SwUpdate).isEnabled).toBe(true);
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
});
|
||||
|
||||
it('catches and a logs registration errors', async() => {
|
||||
it('catches and a logs registration errors', async () => {
|
||||
const consoleErrorSpy = spyOn(console, 'error');
|
||||
swRegisterSpy.and.returnValue(Promise.reject('no reason'));
|
||||
|
||||
|
@ -92,7 +92,7 @@ describe('ServiceWorkerModule', () => {
|
|||
});
|
||||
};
|
||||
|
||||
it('sets the registration options (and overwrites those set via `.register()`', async() => {
|
||||
it('sets the registration options (and overwrites those set via `.register()`', async () => {
|
||||
configTestBed({enabled: true, scope: 'provider'});
|
||||
await untilStable();
|
||||
|
||||
|
@ -100,7 +100,7 @@ describe('ServiceWorkerModule', () => {
|
|||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: 'provider'});
|
||||
});
|
||||
|
||||
it('can disable the SW', async() => {
|
||||
it('can disable the SW', async () => {
|
||||
configTestBed({enabled: false}, {enabled: true});
|
||||
await untilStable();
|
||||
|
||||
|
@ -108,7 +108,7 @@ describe('ServiceWorkerModule', () => {
|
|||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can enable the SW', async() => {
|
||||
it('can enable the SW', async () => {
|
||||
configTestBed({enabled: true}, {enabled: false});
|
||||
await untilStable();
|
||||
|
||||
|
@ -116,7 +116,7 @@ describe('ServiceWorkerModule', () => {
|
|||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
});
|
||||
|
||||
it('defaults to enabling the SW', async() => {
|
||||
it('defaults to enabling the SW', async () => {
|
||||
configTestBed({}, {enabled: false});
|
||||
await untilStable();
|
||||
|
||||
|
|
|
@ -11,18 +11,18 @@ import {CacheDatabase} from '../src/db-cache';
|
|||
import {Driver, DriverReadyState} from '../src/driver';
|
||||
import {AssetGroupConfig, DataGroupConfig, Manifest} from '../src/manifest';
|
||||
import {sha1} from '../src/sha1';
|
||||
import {MockCache, clearAllCaches} from '../testing/cache';
|
||||
import {clearAllCaches, MockCache} from '../testing/cache';
|
||||
import {MockRequest, MockResponse} from '../testing/fetch';
|
||||
import {MockFileSystemBuilder, MockServerStateBuilder, tmpHashTableForFs} from '../testing/mock';
|
||||
import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
||||
|
||||
(function() {
|
||||
// Skip environments that don't support the minimum APIs needed to run the SW tests.
|
||||
if (!SwTestHarness.envIsSupported()) {
|
||||
// Skip environments that don't support the minimum APIs needed to run the SW tests.
|
||||
if (!SwTestHarness.envIsSupported()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const dist =
|
||||
const dist =
|
||||
new MockFileSystemBuilder()
|
||||
.addFile('/foo.txt', 'this is foo')
|
||||
.addFile('/bar.txt', 'this is bar')
|
||||
|
@ -35,11 +35,10 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
.addUnhashedFile('/unhashed/a.txt', 'this is unhashed', {'Cache-Control': 'max-age=10'})
|
||||
.addUnhashedFile('/unhashed/b.txt', 'this is unhashed b', {'Cache-Control': 'no-cache'})
|
||||
.addUnhashedFile('/api/foo', 'this is api foo', {'Cache-Control': 'no-cache'})
|
||||
.addUnhashedFile(
|
||||
'/api-static/bar', 'this is static api bar', {'Cache-Control': 'no-cache'})
|
||||
.addUnhashedFile('/api-static/bar', 'this is static api bar', {'Cache-Control': 'no-cache'})
|
||||
.build();
|
||||
|
||||
const distUpdate =
|
||||
const distUpdate =
|
||||
new MockFileSystemBuilder()
|
||||
.addFile('/foo.txt', 'this is foo v2')
|
||||
.addFile('/bar.txt', 'this is bar')
|
||||
|
@ -49,18 +48,17 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
.addFile('/quuux.txt', 'this is quuux v2')
|
||||
.addFile('/lazy/unchanged1.txt', 'this is unchanged (1)')
|
||||
.addFile('/lazy/unchanged2.txt', 'this is unchanged (2)')
|
||||
.addUnhashedFile(
|
||||
'/unhashed/a.txt', 'this is unhashed v2', {'Cache-Control': 'max-age=10'})
|
||||
.addUnhashedFile('/unhashed/a.txt', 'this is unhashed v2', {'Cache-Control': 'max-age=10'})
|
||||
.addUnhashedFile('/ignored/file1', 'this is not handled by the SW')
|
||||
.addUnhashedFile('/ignored/dir/file2', 'this is not handled by the SW either')
|
||||
.build();
|
||||
|
||||
const brokenFs = new MockFileSystemBuilder()
|
||||
const brokenFs = new MockFileSystemBuilder()
|
||||
.addFile('/foo.txt', 'this is foo (broken)')
|
||||
.addFile('/bar.txt', 'this is bar (broken)')
|
||||
.build();
|
||||
|
||||
const brokenManifest: Manifest = {
|
||||
const brokenManifest: Manifest = {
|
||||
configVersion: 1,
|
||||
timestamp: 1234567890123,
|
||||
index: '/foo.txt',
|
||||
|
@ -76,9 +74,9 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
dataGroups: [],
|
||||
navigationUrls: processNavigationUrls(''),
|
||||
hashTable: tmpHashTableForFs(brokenFs, {'/foo.txt': true}),
|
||||
};
|
||||
};
|
||||
|
||||
const brokenLazyManifest: Manifest = {
|
||||
const brokenLazyManifest: Manifest = {
|
||||
configVersion: 1,
|
||||
timestamp: 1234567890123,
|
||||
index: '/foo.txt',
|
||||
|
@ -105,27 +103,27 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
dataGroups: [],
|
||||
navigationUrls: processNavigationUrls(''),
|
||||
hashTable: tmpHashTableForFs(brokenFs, {'/bar.txt': true}),
|
||||
};
|
||||
};
|
||||
|
||||
// Manifest without navigation urls to test backward compatibility with
|
||||
// versions < 6.0.0.
|
||||
interface ManifestV5 {
|
||||
// Manifest without navigation urls to test backward compatibility with
|
||||
// versions < 6.0.0.
|
||||
interface ManifestV5 {
|
||||
configVersion: number;
|
||||
appData?: {[key: string]: string};
|
||||
index: string;
|
||||
assetGroups?: AssetGroupConfig[];
|
||||
dataGroups?: DataGroupConfig[];
|
||||
hashTable: {[url: string]: string};
|
||||
}
|
||||
}
|
||||
|
||||
// To simulate versions < 6.0.0
|
||||
const manifestOld: ManifestV5 = {
|
||||
// To simulate versions < 6.0.0
|
||||
const manifestOld: ManifestV5 = {
|
||||
configVersion: 1,
|
||||
index: '/foo.txt',
|
||||
hashTable: tmpHashTableForFs(dist),
|
||||
};
|
||||
};
|
||||
|
||||
const manifest: Manifest = {
|
||||
const manifest: Manifest = {
|
||||
configVersion: 1,
|
||||
timestamp: 1234567890123,
|
||||
appData: {
|
||||
|
@ -193,9 +191,9 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
],
|
||||
navigationUrls: processNavigationUrls(''),
|
||||
hashTable: tmpHashTableForFs(dist),
|
||||
};
|
||||
};
|
||||
|
||||
const manifestUpdate: Manifest = {
|
||||
const manifestUpdate: Manifest = {
|
||||
configVersion: 1,
|
||||
timestamp: 1234567890123,
|
||||
appData: {
|
||||
|
@ -248,41 +246,39 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
'!/ignored/dir/**',
|
||||
]),
|
||||
hashTable: tmpHashTableForFs(distUpdate),
|
||||
};
|
||||
};
|
||||
|
||||
const serverBuilderBase =
|
||||
const serverBuilderBase =
|
||||
new MockServerStateBuilder()
|
||||
.withStaticFiles(dist)
|
||||
.withRedirect('/redirected.txt', '/redirect-target.txt', 'this was a redirect')
|
||||
.withError('/error.txt');
|
||||
|
||||
const server = serverBuilderBase.withManifest(manifest).build();
|
||||
const server = serverBuilderBase.withManifest(manifest).build();
|
||||
|
||||
const serverRollback =
|
||||
const serverRollback =
|
||||
serverBuilderBase.withManifest({...manifest, timestamp: manifest.timestamp + 1}).build();
|
||||
|
||||
const serverUpdate =
|
||||
const serverUpdate =
|
||||
new MockServerStateBuilder()
|
||||
.withStaticFiles(distUpdate)
|
||||
.withManifest(manifestUpdate)
|
||||
.withRedirect('/redirected.txt', '/redirect-target.txt', 'this was a redirect')
|
||||
.build();
|
||||
|
||||
const brokenServer =
|
||||
const brokenServer =
|
||||
new MockServerStateBuilder().withStaticFiles(brokenFs).withManifest(brokenManifest).build();
|
||||
|
||||
const brokenLazyServer = new MockServerStateBuilder()
|
||||
.withStaticFiles(brokenFs)
|
||||
.withManifest(brokenLazyManifest)
|
||||
.build();
|
||||
const brokenLazyServer =
|
||||
new MockServerStateBuilder().withStaticFiles(brokenFs).withManifest(brokenLazyManifest).build();
|
||||
|
||||
const server404 = new MockServerStateBuilder().withStaticFiles(dist).build();
|
||||
const server404 = new MockServerStateBuilder().withStaticFiles(dist).build();
|
||||
|
||||
const manifestHash = sha1(JSON.stringify(manifest));
|
||||
const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
|
||||
const manifestHash = sha1(JSON.stringify(manifest));
|
||||
const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
|
||||
|
||||
|
||||
describe('Driver', () => {
|
||||
describe('Driver', () => {
|
||||
let scope: SwTestHarness;
|
||||
let driver: Driver;
|
||||
|
||||
|
@ -296,19 +292,19 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
driver = new Driver(scope, scope, new CacheDatabase(scope, scope));
|
||||
});
|
||||
|
||||
it('activates without waiting', async() => {
|
||||
it('activates without waiting', async () => {
|
||||
const skippedWaiting = await scope.startup(true);
|
||||
expect(skippedWaiting).toBe(true);
|
||||
});
|
||||
|
||||
it('claims all clients, after activation', async() => {
|
||||
it('claims all clients, after activation', async () => {
|
||||
const claimSpy = spyOn(scope.clients, 'claim');
|
||||
|
||||
await scope.startup(true);
|
||||
expect(claimSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('cleans up old `@angular/service-worker` caches, after activation', async() => {
|
||||
it('cleans up old `@angular/service-worker` caches, after activation', async () => {
|
||||
const claimSpy = spyOn(scope.clients, 'claim');
|
||||
const cleanupOldSwCachesSpy = spyOn(driver, 'cleanupOldSwCaches');
|
||||
|
||||
|
@ -322,7 +318,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(claimSpy).toHaveBeenCalledBefore(cleanupOldSwCachesSpy);
|
||||
});
|
||||
|
||||
it('does not blow up if cleaning up old `@angular/service-worker` caches fails', async() => {
|
||||
it('does not blow up if cleaning up old `@angular/service-worker` caches fails', async () => {
|
||||
spyOn(driver, 'cleanupOldSwCaches').and.callFake(() => Promise.reject('Ooops'));
|
||||
|
||||
// Automatically advance time to trigger idle tasks as they are added.
|
||||
|
@ -338,7 +334,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('initializes prefetched content correctly, after activation', async() => {
|
||||
it('initializes prefetched content correctly, after activation', async () => {
|
||||
// Automatically advance time to trigger idle tasks as they are added.
|
||||
scope.autoAdvanceTime = true;
|
||||
await scope.startup(true);
|
||||
|
@ -354,7 +350,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('initializes prefetched content correctly, after a request kicks it off', async() => {
|
||||
it('initializes prefetched content correctly, after a request kicks it off', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.assertSawRequestFor('ngsw.json');
|
||||
|
@ -366,7 +362,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('initializes the service worker on fetch if it has not yet been initialized', async() => {
|
||||
it('initializes the service worker on fetch if it has not yet been initialized', async () => {
|
||||
// Driver is initially uninitialized.
|
||||
expect(driver.initialized).toBeNull();
|
||||
expect(driver['latestHash']).toBeNull();
|
||||
|
@ -385,7 +381,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('initializes the service worker on message if it has not yet been initialized', async() => {
|
||||
it('initializes the service worker on message if it has not yet been initialized', async () => {
|
||||
// Driver is initially uninitialized.
|
||||
expect(driver.initialized).toBeNull();
|
||||
expect(driver['latestHash']).toBeNull();
|
||||
|
@ -408,7 +404,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('handles non-relative URLs', async() => {
|
||||
it('handles non-relative URLs', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
@ -416,19 +412,19 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('handles actual errors from the browser', async() => {
|
||||
it('handles actual errors from the browser', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
||||
const [resPromise, done] = scope.handleFetch(new MockRequest('/error.txt'), 'default');
|
||||
await done;
|
||||
const res = (await resPromise) !;
|
||||
const res = (await resPromise)!;
|
||||
expect(res.status).toEqual(504);
|
||||
expect(res.statusText).toEqual('Gateway Timeout');
|
||||
});
|
||||
|
||||
it('handles redirected responses', async() => {
|
||||
it('handles redirected responses', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
@ -436,7 +432,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('caches lazy content on-request', async() => {
|
||||
it('caches lazy content on-request', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
@ -450,11 +446,11 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('updates to new content when requested', async() => {
|
||||
it('updates to new content when requested', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
const client = scope.clients.getMock('default') !;
|
||||
const client = scope.clients.getMock('default')!;
|
||||
expect(client.messages).toEqual([]);
|
||||
|
||||
scope.updateServerState(serverUpdate);
|
||||
|
@ -483,7 +479,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('detects new version even if only `manifest.timestamp` is different', async() => {
|
||||
it('detects new version even if only `manifest.timestamp` is different', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt', 'newClient')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
|
@ -496,11 +492,11 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(await makeRequest(scope, '/foo.txt', 'newestClient')).toEqual('this is foo');
|
||||
});
|
||||
|
||||
it('updates a specific client to new content on request', async() => {
|
||||
it('updates a specific client to new content on request', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
const client = scope.clients.getMock('default') !;
|
||||
const client = scope.clients.getMock('default')!;
|
||||
expect(client.messages).toEqual([]);
|
||||
|
||||
scope.updateServerState(serverUpdate);
|
||||
|
@ -524,7 +520,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo v2');
|
||||
});
|
||||
|
||||
it('handles empty client ID', async() => {
|
||||
it('handles empty client ID', async () => {
|
||||
// Initialize the SW.
|
||||
expect(await makeNavigationRequest(scope, '/foo/file1', '')).toEqual('this is foo');
|
||||
expect(await makeNavigationRequest(scope, '/bar/file2', null)).toEqual('this is foo');
|
||||
|
@ -539,7 +535,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(await makeNavigationRequest(scope, '/bar/file2', null)).toEqual('this is foo v2');
|
||||
});
|
||||
|
||||
it('checks for updates on restart', async() => {
|
||||
it('checks for updates on restart', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
|
@ -561,7 +557,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('checks for updates on navigation', async() => {
|
||||
it('checks for updates on navigation', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
@ -574,7 +570,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertSawRequestFor('ngsw.json');
|
||||
});
|
||||
|
||||
it('does not make concurrent checks for updates on navigation', async() => {
|
||||
it('does not make concurrent checks for updates on navigation', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
@ -590,7 +586,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('preserves multiple client assignments across restarts', async() => {
|
||||
it('preserves multiple client assignments across restarts', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
|
@ -610,11 +606,11 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('updates when refreshed', async() => {
|
||||
it('updates when refreshed', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
const client = scope.clients.getMock('default') !;
|
||||
const client = scope.clients.getMock('default')!;
|
||||
|
||||
scope.updateServerState(serverUpdate);
|
||||
expect(await driver.checkForUpdate()).toEqual(true);
|
||||
|
@ -637,7 +633,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('cleans up properly when manually requested', async() => {
|
||||
it('cleans up properly when manually requested', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
|
@ -657,7 +653,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('cleans up properly on restart', async() => {
|
||||
it('cleans up properly on restart', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
|
@ -688,7 +684,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(hasOriginalCaches).toEqual(false);
|
||||
});
|
||||
|
||||
it('shows notifications for push notifications', async() => {
|
||||
it('shows notifications for push notifications', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
await scope.handlePush({
|
||||
|
@ -701,7 +697,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
title: 'This is a test',
|
||||
options: {title: 'This is a test', body: 'Test body'},
|
||||
}]);
|
||||
expect(scope.clients.getMock('default') !.messages).toEqual([{
|
||||
expect(scope.clients.getMock('default')!.messages).toEqual([{
|
||||
type: 'PUSH',
|
||||
data: {
|
||||
notification: {
|
||||
|
@ -712,12 +708,12 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
}]);
|
||||
});
|
||||
|
||||
it('broadcasts notification click events with action', async() => {
|
||||
it('broadcasts notification click events with action', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
await scope.handleClick(
|
||||
{title: 'This is a test with action', body: 'Test body with action'}, 'button');
|
||||
const message: any = scope.clients.getMock('default') !.messages[0];
|
||||
const message: any = scope.clients.getMock('default')!.messages[0];
|
||||
|
||||
expect(message.type).toEqual('NOTIFICATION_CLICK');
|
||||
expect(message.data.action).toEqual('button');
|
||||
|
@ -725,12 +721,12 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(message.data.notification.body).toEqual('Test body with action');
|
||||
});
|
||||
|
||||
it('broadcasts notification click events without action', async() => {
|
||||
it('broadcasts notification click events without action', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
await scope.handleClick(
|
||||
{title: 'This is a test without action', body: 'Test body without action'});
|
||||
const message: any = scope.clients.getMock('default') !.messages[0];
|
||||
const message: any = scope.clients.getMock('default')!.messages[0];
|
||||
|
||||
expect(message.type).toEqual('NOTIFICATION_CLICK');
|
||||
expect(message.data.action).toBeUndefined();
|
||||
|
@ -738,7 +734,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(message.data.notification.body).toEqual('Test body without action');
|
||||
});
|
||||
|
||||
it('prefetches updates to lazy cache when set', async() => {
|
||||
it('prefetches updates to lazy cache when set', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
|
@ -783,14 +779,14 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('should bypass serviceworker on ngsw-bypass parameter', async() => {
|
||||
it('should bypass serviceworker on ngsw-bypass parameter', async () => {
|
||||
await makeRequest(scope, '/foo.txt', undefined, {headers: {'ngsw-bypass': 'true'}});
|
||||
server.assertNoRequestFor('/foo.txt');
|
||||
|
||||
await makeRequest(scope, '/foo.txt', undefined, {headers: {'ngsw-bypass': 'anything'}});
|
||||
server.assertNoRequestFor('/foo.txt');
|
||||
|
||||
await makeRequest(scope, '/foo.txt', undefined, {headers: {'ngsw-bypass': null !}});
|
||||
await makeRequest(scope, '/foo.txt', undefined, {headers: {'ngsw-bypass': null!}});
|
||||
server.assertNoRequestFor('/foo.txt');
|
||||
|
||||
await makeRequest(scope, '/foo.txt', undefined, {headers: {'NGSW-bypass': 'upperCASE'}});
|
||||
|
@ -848,10 +844,9 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
|
||||
await makeRequest(scope, '/bar?ngsw-byapass&testparam2');
|
||||
server.assertSawRequestFor('/bar');
|
||||
|
||||
});
|
||||
|
||||
it('unregisters when manifest 404s', async() => {
|
||||
it('unregisters when manifest 404s', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
||||
|
@ -861,7 +856,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(await scope.caches.keys()).toEqual([]);
|
||||
});
|
||||
|
||||
it('does not unregister or change state when offline (i.e. manifest 504s)', async() => {
|
||||
it('does not unregister or change state when offline (i.e. manifest 504s)', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.online = false;
|
||||
|
@ -873,10 +868,10 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
it('does not unregister or change state when status code is 503 (service unavailable)',
|
||||
async() => {
|
||||
async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
spyOn(server, 'fetch').and.callFake(async(req: Request) => new MockResponse(null, {
|
||||
spyOn(server, 'fetch').and.callFake(async (req: Request) => new MockResponse(null, {
|
||||
status: 503,
|
||||
statusText: 'Service Unavailable'
|
||||
}));
|
||||
|
@ -890,7 +885,8 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
describe('cache naming', () => {
|
||||
// Helpers
|
||||
const cacheKeysFor = (baseHref: string) =>
|
||||
[`ngsw:${baseHref}:db:control`, `ngsw:${baseHref}:${manifestHash}:assets:assets:cache`,
|
||||
[`ngsw:${baseHref}:db:control`,
|
||||
`ngsw:${baseHref}:${manifestHash}:assets:assets:cache`,
|
||||
`ngsw:${baseHref}:db:ngsw:${baseHref}:${manifestHash}:assets:assets:meta`,
|
||||
`ngsw:${baseHref}:${manifestHash}:assets:other:cache`,
|
||||
`ngsw:${baseHref}:db:ngsw:${baseHref}:${manifestHash}:assets:other:meta`,
|
||||
|
@ -904,14 +900,14 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
`ngsw:${baseHref}:db:ngsw:${baseHref}:43:data:dynamic:api-static:age`,
|
||||
];
|
||||
|
||||
const getClientAssignments = async(sw: SwTestHarness, baseHref: string) => {
|
||||
const getClientAssignments = async (sw: SwTestHarness, baseHref: string) => {
|
||||
const cache = await sw.caches.open(`ngsw:${baseHref}:db:control`) as unknown as MockCache;
|
||||
const dehydrated = cache.dehydrate();
|
||||
return JSON.parse(dehydrated['/assignments'].body !);
|
||||
return JSON.parse(dehydrated['/assignments'].body!);
|
||||
};
|
||||
|
||||
const initializeSwFor =
|
||||
async(baseHref: string, initialCacheState = '{}', serverState = server) => {
|
||||
async (baseHref: string, initialCacheState = '{}', serverState = server) => {
|
||||
const newScope = new SwTestHarnessBuilder(`http://localhost${baseHref}`)
|
||||
.withCacheState(initialCacheState)
|
||||
.withServerState(serverState)
|
||||
|
@ -924,7 +920,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
return newScope;
|
||||
};
|
||||
|
||||
it('includes the SW scope in all cache names', async() => {
|
||||
it('includes the SW scope in all cache names', async () => {
|
||||
// Default SW with scope `/`.
|
||||
await makeRequest(scope, '/foo.txt');
|
||||
await driver.initialized;
|
||||
|
@ -941,7 +937,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(fooCacheNames.every(name => name.includes('/foo/'))).toBe(true);
|
||||
});
|
||||
|
||||
it('does not affect caches from other scopes', async() => {
|
||||
it('does not affect caches from other scopes', async () => {
|
||||
// Create SW with scope `/foo/`.
|
||||
const fooScope = await initializeSwFor('/foo/');
|
||||
const fooAssignments = await getClientAssignments(fooScope, '/foo/');
|
||||
|
@ -964,7 +960,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(fooAssignments2).toEqual({_foo_: manifestHash});
|
||||
});
|
||||
|
||||
it('updates existing caches for same scope', async() => {
|
||||
it('updates existing caches for same scope', async () => {
|
||||
// Create SW with scope `/foo/`.
|
||||
const fooScope = await initializeSwFor('/foo/');
|
||||
await makeRequest(fooScope, '/foo.txt', '_bar_');
|
||||
|
@ -1000,27 +996,27 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
describe('unhashed requests', () => {
|
||||
beforeEach(async() => {
|
||||
beforeEach(async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
});
|
||||
|
||||
it('are cached appropriately', async() => {
|
||||
it('are cached appropriately', async () => {
|
||||
expect(await makeRequest(scope, '/unhashed/a.txt')).toEqual('this is unhashed');
|
||||
server.assertSawRequestFor('/unhashed/a.txt');
|
||||
expect(await makeRequest(scope, '/unhashed/a.txt')).toEqual('this is unhashed');
|
||||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it(`doesn't error when 'Cache-Control' is 'no-cache'`, async() => {
|
||||
it(`doesn't error when 'Cache-Control' is 'no-cache'`, async () => {
|
||||
expect(await makeRequest(scope, '/unhashed/b.txt')).toEqual('this is unhashed b');
|
||||
server.assertSawRequestFor('/unhashed/b.txt');
|
||||
expect(await makeRequest(scope, '/unhashed/b.txt')).toEqual('this is unhashed b');
|
||||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('avoid opaque responses', async() => {
|
||||
it('avoid opaque responses', async () => {
|
||||
expect(await makeRequest(scope, '/unhashed/a.txt', 'default', {
|
||||
credentials: 'include'
|
||||
})).toEqual('this is unhashed');
|
||||
|
@ -1029,7 +1025,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('expire according to Cache-Control headers', async() => {
|
||||
it('expire according to Cache-Control headers', async () => {
|
||||
expect(await makeRequest(scope, '/unhashed/a.txt')).toEqual('this is unhashed');
|
||||
server.clearRequests();
|
||||
|
||||
|
@ -1052,7 +1048,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('survive serialization', async() => {
|
||||
it('survive serialization', async () => {
|
||||
expect(await makeRequest(scope, '/unhashed/a.txt')).toEqual('this is unhashed');
|
||||
server.clearRequests();
|
||||
|
||||
|
@ -1075,7 +1071,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoRequestFor('/unhashed/a.txt');
|
||||
});
|
||||
|
||||
it('get carried over during updates', async() => {
|
||||
it('get carried over during updates', async () => {
|
||||
expect(await makeRequest(scope, '/unhashed/a.txt')).toEqual('this is unhashed');
|
||||
server.clearRequests();
|
||||
|
||||
|
@ -1108,28 +1104,28 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
const navRequest = (url: string, init = {}) =>
|
||||
makeNavigationRequest(scope, url, undefined, init);
|
||||
|
||||
beforeEach(async() => {
|
||||
beforeEach(async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
});
|
||||
|
||||
it('redirects to index on a route-like request', async() => {
|
||||
it('redirects to index on a route-like request', async () => {
|
||||
expect(await navRequest('/baz')).toEqual('this is foo');
|
||||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('redirects to index on a request to the origin URL request', async() => {
|
||||
it('redirects to index on a request to the origin URL request', async () => {
|
||||
expect(await navRequest('http://localhost/')).toEqual('this is foo');
|
||||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('does not redirect to index on a non-navigation request', async() => {
|
||||
it('does not redirect to index on a non-navigation request', async () => {
|
||||
expect(await navRequest('/baz', {mode: undefined})).toBeNull();
|
||||
server.assertSawRequestFor('/baz');
|
||||
});
|
||||
|
||||
it('does not redirect to index on a request that does not accept HTML', async() => {
|
||||
it('does not redirect to index on a request that does not accept HTML', async () => {
|
||||
expect(await navRequest('/baz', {headers: {}})).toBeNull();
|
||||
server.assertSawRequestFor('/baz');
|
||||
|
||||
|
@ -1137,7 +1133,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertSawRequestFor('/qux');
|
||||
});
|
||||
|
||||
it('does not redirect to index on a request with an extension', async() => {
|
||||
it('does not redirect to index on a request with an extension', async () => {
|
||||
expect(await navRequest('/baz.html')).toBeNull();
|
||||
server.assertSawRequestFor('/baz.html');
|
||||
|
||||
|
@ -1146,7 +1142,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('does not redirect to index if the URL contains `__`', async() => {
|
||||
it('does not redirect to index if the URL contains `__`', async () => {
|
||||
expect(await navRequest('/baz/x__x')).toBeNull();
|
||||
server.assertSawRequestFor('/baz/x__x');
|
||||
|
||||
|
@ -1161,13 +1157,13 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
describe('(with custom `navigationUrls`)', () => {
|
||||
beforeEach(async() => {
|
||||
beforeEach(async () => {
|
||||
scope.updateServerState(serverUpdate);
|
||||
await driver.checkForUpdate();
|
||||
serverUpdate.clearRequests();
|
||||
});
|
||||
|
||||
it('redirects to index on a request that matches any positive pattern', async() => {
|
||||
it('redirects to index on a request that matches any positive pattern', async () => {
|
||||
expect(await navRequest('/foo/file0')).toBeNull();
|
||||
serverUpdate.assertSawRequestFor('/foo/file0');
|
||||
|
||||
|
@ -1178,24 +1174,22 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('does not redirect to index on a request that matches any negative pattern', async() => {
|
||||
it('does not redirect to index on a request that matches any negative pattern', async () => {
|
||||
expect(await navRequest('/ignored/file1')).toBe('this is not handled by the SW');
|
||||
serverUpdate.assertSawRequestFor('/ignored/file1');
|
||||
|
||||
expect(await navRequest('/ignored/dir/file2'))
|
||||
.toBe('this is not handled by the SW either');
|
||||
expect(await navRequest('/ignored/dir/file2')).toBe('this is not handled by the SW either');
|
||||
serverUpdate.assertSawRequestFor('/ignored/dir/file2');
|
||||
|
||||
expect(await navRequest('/ignored/directory/file2')).toBe('this is foo v2');
|
||||
serverUpdate.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
it('strips URL query before checking `navigationUrls`', async() => {
|
||||
it('strips URL query before checking `navigationUrls`', async () => {
|
||||
expect(await navRequest('/foo/file1?query=/a/b')).toBe('this is foo v2');
|
||||
serverUpdate.assertNoOtherRequests();
|
||||
|
||||
expect(await navRequest('/ignored/file1?query=/a/b'))
|
||||
.toBe('this is not handled by the SW');
|
||||
expect(await navRequest('/ignored/file1?query=/a/b')).toBe('this is not handled by the SW');
|
||||
serverUpdate.assertSawRequestFor('/ignored/file1');
|
||||
|
||||
expect(await navRequest('/ignored/dir/file2?query=/a/b'))
|
||||
|
@ -1203,7 +1197,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
serverUpdate.assertSawRequestFor('/ignored/dir/file2');
|
||||
});
|
||||
|
||||
it('strips registration scope before checking `navigationUrls`', async() => {
|
||||
it('strips registration scope before checking `navigationUrls`', async () => {
|
||||
expect(await navRequest('http://localhost/ignored/file1'))
|
||||
.toBe('this is not handled by the SW');
|
||||
serverUpdate.assertSawRequestFor('/ignored/file1');
|
||||
|
@ -1212,7 +1206,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
describe('cleanupOldSwCaches()', () => {
|
||||
it('should delete the correct caches', async() => {
|
||||
it('should delete the correct caches', async () => {
|
||||
const oldSwCacheNames = [
|
||||
// Example cache names from the beta versions of `@angular/service-worker`.
|
||||
'ngsw:active',
|
||||
|
@ -1238,12 +1232,12 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(await scope.caches.keys()).toEqual(otherCacheNames);
|
||||
});
|
||||
|
||||
it('should delete other caches even if deleting one of them fails', async() => {
|
||||
it('should delete other caches even if deleting one of them fails', async () => {
|
||||
const oldSwCacheNames = ['ngsw:active', 'ngsw:staged', 'ngsw:manifest:a1b2c3:super:duper'];
|
||||
const deleteSpy = spyOn(scope.caches, 'delete')
|
||||
const deleteSpy =
|
||||
spyOn(scope.caches, 'delete')
|
||||
.and.callFake(
|
||||
(cacheName: string) =>
|
||||
Promise.reject(`Failed to delete cache '${cacheName}'.`));
|
||||
(cacheName: string) => Promise.reject(`Failed to delete cache '${cacheName}'.`));
|
||||
|
||||
await Promise.all(oldSwCacheNames.map(name => scope.caches.open(name)));
|
||||
const error = await driver.cleanupOldSwCaches().catch(err => err);
|
||||
|
@ -1255,7 +1249,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
describe('bugs', () => {
|
||||
it('does not crash with bad index hash', async() => {
|
||||
it('does not crash with bad index hash', async () => {
|
||||
scope = new SwTestHarnessBuilder().withServerState(brokenServer).build();
|
||||
(scope.registration as any).scope = 'http://site.com';
|
||||
driver = new Driver(scope, scope, new CacheDatabase(scope, scope));
|
||||
|
@ -1263,7 +1257,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo (broken)');
|
||||
});
|
||||
|
||||
it('enters degraded mode when update has a bad index', async() => {
|
||||
it('enters degraded mode when update has a bad index', async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
@ -1281,7 +1275,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(driver.state).toEqual(DriverReadyState.EXISTING_CLIENTS_ONLY);
|
||||
});
|
||||
|
||||
it('enters degraded mode when failing to write to cache', async() => {
|
||||
it('enters degraded mode when failing to write to cache', async () => {
|
||||
// Initialize the SW.
|
||||
await makeRequest(scope, '/foo.txt');
|
||||
await driver.initialized;
|
||||
|
@ -1304,7 +1298,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
it('keeps serving api requests with freshness strategy when failing to write to cache',
|
||||
async() => {
|
||||
async () => {
|
||||
// Initialize the SW.
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
@ -1325,7 +1319,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
it('keeps serving api requests with performance strategy when failing to write to cache',
|
||||
async() => {
|
||||
async () => {
|
||||
// Initialize the SW.
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
@ -1347,7 +1341,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
|
||||
it('keeps serving mutating api requests when failing to write to cache',
|
||||
// sw can invalidate LRU cache entry and try to write to cache storage on mutating request
|
||||
async() => {
|
||||
async () => {
|
||||
// Initialize the SW.
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
|
@ -1366,7 +1360,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
server.assertSawRequestFor('/api/foo');
|
||||
});
|
||||
|
||||
it('enters degraded mode when something goes wrong with the latest version', async() => {
|
||||
it('enters degraded mode when something goes wrong with the latest version', async () => {
|
||||
await driver.initialized;
|
||||
|
||||
// Two clients on initial version.
|
||||
|
@ -1406,7 +1400,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
|
||||
it('recovers from degraded `EXISTING_CLIENTS_ONLY` mode as soon as there is a valid update',
|
||||
async() => {
|
||||
async () => {
|
||||
await driver.initialized;
|
||||
expect(driver.state).toBe(DriverReadyState.NORMAL);
|
||||
|
||||
|
@ -1421,8 +1415,8 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(driver.state).toBe(DriverReadyState.NORMAL);
|
||||
});
|
||||
|
||||
it('ignores invalid `only-if-cached` requests ', async() => {
|
||||
const requestFoo = (cache: RequestCache | 'only-if-cached', mode: RequestMode) =>
|
||||
it('ignores invalid `only-if-cached` requests ', async () => {
|
||||
const requestFoo = (cache: RequestCache|'only-if-cached', mode: RequestMode) =>
|
||||
makeRequest(scope, '/foo.txt', undefined, {cache, mode});
|
||||
|
||||
expect(await requestFoo('default', 'no-cors')).toBe('this is foo');
|
||||
|
@ -1430,10 +1424,10 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
expect(await requestFoo('only-if-cached', 'no-cors')).toBeNull();
|
||||
});
|
||||
|
||||
it('ignores passive mixed content requests ', async() => {
|
||||
it('ignores passive mixed content requests ', async () => {
|
||||
const scopeFetchSpy = spyOn(scope, 'fetch').and.callThrough();
|
||||
const getRequestUrls = () =>
|
||||
(scopeFetchSpy.calls.allArgs() as[Request][]).map(args => args[0].url);
|
||||
(scopeFetchSpy.calls.allArgs() as [Request][]).map(args => args[0].url);
|
||||
|
||||
const httpScopeUrl = 'http://mock.origin.dev';
|
||||
const httpsScopeUrl = 'https://mock.origin.dev';
|
||||
|
@ -1476,7 +1470,7 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
|
||||
// Test this bug: https://github.com/angular/angular/issues/27209
|
||||
it('fills previous versions of manifests with default navigation urls for backwards compatibility',
|
||||
async() => {
|
||||
async () => {
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
scope.updateServerState(serverUpdate);
|
||||
|
@ -1484,12 +1478,12 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
async function makeRequest(
|
||||
scope: SwTestHarness, url: string, clientId: string | null = 'default', init?: Object):
|
||||
Promise<string|null> {
|
||||
scope: SwTestHarness, url: string, clientId: string|null = 'default',
|
||||
init?: Object): Promise<string|null> {
|
||||
const [resPromise, done] = scope.handleFetch(new MockRequest(url, init), clientId);
|
||||
await done;
|
||||
const res = await resPromise;
|
||||
|
@ -1497,16 +1491,17 @@ async function makeRequest(
|
|||
return res.text();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function makeNavigationRequest(
|
||||
scope: SwTestHarness, url: string, clientId?: string | null, init: Object = {}):
|
||||
Promise<string|null> {
|
||||
scope: SwTestHarness, url: string, clientId?: string|null,
|
||||
init: Object = {}): Promise<string|null> {
|
||||
return makeRequest(scope, url, clientId, {
|
||||
headers: {
|
||||
Accept: 'text/plain, text/html, text/css',
|
||||
...(init as any).headers,
|
||||
},
|
||||
mode: 'navigate', ...init,
|
||||
mode: 'navigate',
|
||||
...init,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, NgZone, OnChanges, OnDestroy, Output, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core';
|
||||
import {ChangeDetectorRef, Component, destroyPlatform, EventEmitter, forwardRef, Input, NgModule, NgModuleFactory, NgZone, NO_ERRORS_SCHEMA, OnChanges, OnDestroy, Output, SimpleChange, SimpleChanges, Testability} from '@angular/core';
|
||||
import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
|
@ -23,7 +23,6 @@ declare global {
|
|||
|
||||
withEachNg1Version(() => {
|
||||
describe('adapter: ng1 to ng2', () => {
|
||||
|
||||
beforeEach(() => destroyPlatform());
|
||||
afterEach(() => destroyPlatform());
|
||||
|
||||
|
@ -232,7 +231,9 @@ withEachNg1Version(() => {
|
|||
})
|
||||
class Ng2 {
|
||||
l: any;
|
||||
constructor() { this.l = l; }
|
||||
constructor() {
|
||||
this.l = l;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
|
@ -262,7 +263,9 @@ withEachNg1Version(() => {
|
|||
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
||||
class AppComponent {
|
||||
value?: number;
|
||||
constructor() { appComponent = this; }
|
||||
constructor() {
|
||||
appComponent = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -272,7 +275,9 @@ withEachNg1Version(() => {
|
|||
class ChildComponent {
|
||||
valueFromPromise?: number;
|
||||
@Input()
|
||||
set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); }
|
||||
set value(v: number) {
|
||||
expect(NgZone.isInAngularZone()).toBe(true);
|
||||
}
|
||||
|
||||
constructor(private zone: NgZone) {}
|
||||
|
||||
|
@ -352,14 +357,15 @@ withEachNg1Version(() => {
|
|||
|
||||
const element = html('<ng2></ng2>');
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent !)).toBe('It works');
|
||||
expect(multiTrim(document.body.textContent!)).toBe('It works');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind properties, events', async(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module =
|
||||
angular.module_('ng1', []).value($EXCEPTION_HANDLER, (err: any) => { throw err; });
|
||||
const ng1Module = angular.module_('ng1', []).value($EXCEPTION_HANDLER, (err: any) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
ng1Module.run(($rootScope: any) => {
|
||||
$rootScope.name = 'world';
|
||||
|
@ -409,8 +415,8 @@ withEachNg1Version(() => {
|
|||
}
|
||||
const actValue = changes[prop].currentValue;
|
||||
if (actValue != value) {
|
||||
throw new Error(
|
||||
`Expected changes record for'${prop}' to be '${value}' but was '${actValue}'`);
|
||||
throw new Error(`Expected changes record for'${prop}' to be '${
|
||||
value}' but was '${actValue}'`);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -458,7 +464,7 @@ withEachNg1Version(() => {
|
|||
| modelA: {{modelA}}; modelB: {{modelB}}; eventA: {{eventA}}; eventB: {{eventB}};
|
||||
</div>`);
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent !))
|
||||
expect(multiTrim(document.body.textContent!))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello world; ' +
|
||||
|
@ -466,7 +472,7 @@ withEachNg1Version(() => {
|
|||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
|
||||
ref.ng1RootScope.$apply('name = "everyone"');
|
||||
expect(multiTrim(document.body.textContent !))
|
||||
expect(multiTrim(document.body.textContent!))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello everyone; ' +
|
||||
|
@ -475,7 +481,6 @@ withEachNg1Version(() => {
|
|||
|
||||
ref.dispose();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should support two-way binding and event listener', async(() => {
|
||||
|
@ -541,9 +546,9 @@ withEachNg1Version(() => {
|
|||
ngOnChangesCount = 0;
|
||||
firstChangesCount = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
initialValue !: string;
|
||||
initialValue!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() foo !: string;
|
||||
@Input() foo!: string;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.ngOnChangesCount++;
|
||||
|
@ -590,7 +595,9 @@ withEachNg1Version(() => {
|
|||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
ng1Module.run(($rootScope: any /** TODO #9100 */) => { $rootScope.modelA = 'A'; });
|
||||
ng1Module.run(($rootScope: any /** TODO #9100 */) => {
|
||||
$rootScope.modelA = 'A';
|
||||
});
|
||||
|
||||
let ng2Instance: Ng2;
|
||||
@Component({selector: 'ng2', template: '{{_value}}'})
|
||||
|
@ -598,11 +605,21 @@ withEachNg1Version(() => {
|
|||
private _value: any = '';
|
||||
private _onChangeCallback: (_: any) => void = () => {};
|
||||
private _onTouchedCallback: () => void = () => {};
|
||||
constructor() { ng2Instance = this; }
|
||||
writeValue(value: any) { this._value = value; }
|
||||
registerOnChange(fn: any) { this._onChangeCallback = fn; }
|
||||
registerOnTouched(fn: any) { this._onTouchedCallback = fn; }
|
||||
doTouch() { this._onTouchedCallback(); }
|
||||
constructor() {
|
||||
ng2Instance = this;
|
||||
}
|
||||
writeValue(value: any) {
|
||||
this._value = value;
|
||||
}
|
||||
registerOnChange(fn: any) {
|
||||
this._onChangeCallback = fn;
|
||||
}
|
||||
registerOnTouched(fn: any) {
|
||||
this._onTouchedCallback = fn;
|
||||
}
|
||||
doTouch() {
|
||||
this._onTouchedCallback();
|
||||
}
|
||||
doChange(newValue: string) {
|
||||
this._value = newValue;
|
||||
this._onChangeCallback(newValue);
|
||||
|
@ -653,14 +670,18 @@ withEachNg1Version(() => {
|
|||
return {
|
||||
template: '<div ng-if="!destroyIt"><ng2></ng2></div>',
|
||||
controller: function($rootScope: any, $timeout: Function) {
|
||||
$timeout(() => { $rootScope.destroyIt = true; });
|
||||
$timeout(() => {
|
||||
$rootScope.destroyIt = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@Component({selector: 'ng2', template: 'test'})
|
||||
class Ng2 {
|
||||
ngOnDestroy() { onDestroyed.emit('destroyed'); }
|
||||
ngOnDestroy() {
|
||||
onDestroyed.emit('destroyed');
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
|
@ -673,7 +694,9 @@ withEachNg1Version(() => {
|
|||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
const element = html('<ng1></ng1>');
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
onDestroyed.subscribe(() => { ref.dispose(); });
|
||||
onDestroyed.subscribe(() => {
|
||||
ref.dispose();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
|
@ -689,7 +712,9 @@ withEachNg1Version(() => {
|
|||
|
||||
@Component({selector: 'ng2-inner', template: 'test'})
|
||||
class Ng2InnerComponent implements OnDestroy {
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
ngOnDestroy() {
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
|
@ -789,7 +814,7 @@ withEachNg1Version(() => {
|
|||
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() itemId !: string;
|
||||
@Input() itemId!: string;
|
||||
}
|
||||
|
||||
@NgModule({imports: [BrowserModule], declarations: [Ng2Component]})
|
||||
|
@ -838,7 +863,7 @@ withEachNg1Version(() => {
|
|||
ng1Module.directive('rootComponent', adapter.downgradeNg2Component(RootComponent));
|
||||
|
||||
document.body.innerHTML = '<root-component></root-component>';
|
||||
adapter.bootstrap(document.body.firstElementChild !, ['myExample']).ready((ref) => {
|
||||
adapter.bootstrap(document.body.firstElementChild!, ['myExample']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent)).toEqual('It works!');
|
||||
ref.dispose();
|
||||
});
|
||||
|
@ -868,7 +893,9 @@ withEachNg1Version(() => {
|
|||
dataA = 'foo';
|
||||
dataB = 'bar';
|
||||
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -888,8 +915,8 @@ withEachNg1Version(() => {
|
|||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
||||
|
||||
|
@ -932,7 +959,9 @@ withEachNg1Version(() => {
|
|||
dataA = {value: 'foo'};
|
||||
dataB = {value: 'bar'};
|
||||
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -952,8 +981,8 @@ withEachNg1Version(() => {
|
|||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
||||
|
||||
|
@ -996,7 +1025,9 @@ withEachNg1Version(() => {
|
|||
dataA = {value: 'foo'};
|
||||
dataB = {value: 'bar'};
|
||||
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -1016,8 +1047,8 @@ withEachNg1Version(() => {
|
|||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
||||
|
||||
|
@ -1077,8 +1108,8 @@ withEachNg1Version(() => {
|
|||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: foo, bar');
|
||||
|
||||
|
@ -1204,7 +1235,9 @@ withEachNg1Version(() => {
|
|||
restrict: 'E',
|
||||
template: '{{someText}} - Length: {{data.length}}',
|
||||
scope: {data: '='},
|
||||
controller: function($scope: any) { $scope.someText = 'ng1 - Data: ' + $scope.data; }
|
||||
controller: function($scope: any) {
|
||||
$scope.someText = 'ng1 - Data: ' + $scope.data;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1248,7 +1281,9 @@ withEachNg1Version(() => {
|
|||
restrict: 'E',
|
||||
template: '{{someText}} - Length: {{data.length}}',
|
||||
scope: {data: '='},
|
||||
link: function($scope: any) { $scope.someText = 'ng1 - Data: ' + $scope.data; }
|
||||
link: function($scope: any) {
|
||||
$scope.someText = 'ng1 - Data: ' + $scope.data;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1291,7 +1326,9 @@ withEachNg1Version(() => {
|
|||
cbFn(200, `${method}:${url}`);
|
||||
});
|
||||
|
||||
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
||||
const ng1 = () => {
|
||||
return {templateUrl: 'url.html'};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
class Ng2 {
|
||||
|
@ -1320,7 +1357,13 @@ withEachNg1Version(() => {
|
|||
cbFn(200, `${method}:${url}`);
|
||||
});
|
||||
|
||||
const ng1 = () => { return {templateUrl() { return 'url.html'; }}; };
|
||||
const ng1 = () => {
|
||||
return {
|
||||
templateUrl() {
|
||||
return 'url.html';
|
||||
}
|
||||
};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
class Ng2 {
|
||||
|
@ -1345,7 +1388,9 @@ withEachNg1Version(() => {
|
|||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
const ng1 = () => { return {template: ''}; };
|
||||
const ng1 = () => {
|
||||
return {template: ''};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
|
@ -1371,7 +1416,13 @@ withEachNg1Version(() => {
|
|||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
const ng1 = () => { return {template() { return ''; }}; };
|
||||
const ng1 = () => {
|
||||
return {
|
||||
template() {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
|
@ -1398,7 +1449,9 @@ withEachNg1Version(() => {
|
|||
const ng1Module = angular.module_('ng1', []);
|
||||
ng1Module.run(($templateCache: any) => $templateCache.put('url.html', 'WORKS'));
|
||||
|
||||
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
||||
const ng1 = () => {
|
||||
return {templateUrl: 'url.html'};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
|
@ -1431,13 +1484,20 @@ withEachNg1Version(() => {
|
|||
'{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}',
|
||||
controllerAs: 'ctl',
|
||||
controller: class {
|
||||
scope: any; hasElement: string; $element: any; isClass: any;
|
||||
scope: any;
|
||||
hasElement: string;
|
||||
$element: any;
|
||||
isClass: any;
|
||||
constructor($scope: any, $element: any) {
|
||||
this.verifyIAmAClass();
|
||||
this.scope = $scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope';
|
||||
this.hasElement = $element[0].nodeName;
|
||||
this.$element = $element;
|
||||
} verifyIAmAClass() { this.isClass = 'isClass'; } isPublished() {
|
||||
}
|
||||
verifyIAmAClass() {
|
||||
this.isClass = 'isClass';
|
||||
}
|
||||
isPublished() {
|
||||
return this.$element.controller('ng1') == this ? 'published' : 'not-published';
|
||||
}
|
||||
}
|
||||
|
@ -1543,7 +1603,9 @@ withEachNg1Version(() => {
|
|||
template: '{{ctl.status}}',
|
||||
require: 'ng1',
|
||||
controllerAs: 'ctrl',
|
||||
controller: class {status = 'WORKS';},
|
||||
controller: class {
|
||||
status = 'WORKS';
|
||||
},
|
||||
link: function(scope: any, element: any, attrs: any, linkController: any) {
|
||||
expect(scope.$root).toEqual($rootScope);
|
||||
expect(element[0].nodeName).toEqual('NG1');
|
||||
|
@ -1577,7 +1639,13 @@ withEachNg1Version(() => {
|
|||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
const parent = () => { return {controller: class {parent = 'PARENT';}}; };
|
||||
const parent = () => {
|
||||
return {
|
||||
controller: class {
|
||||
parent = 'PARENT';
|
||||
}
|
||||
};
|
||||
};
|
||||
const ng1 = () => {
|
||||
return {
|
||||
scope: {title: '@'},
|
||||
|
@ -1585,7 +1653,9 @@ withEachNg1Version(() => {
|
|||
template: '{{parent.parent}}:{{ng1.status}}',
|
||||
require: ['ng1', '^parent', '?^^notFound'],
|
||||
controllerAs: 'ctrl',
|
||||
controller: class {status = 'WORKS';},
|
||||
controller: class {
|
||||
status = 'WORKS';
|
||||
},
|
||||
link: function(scope: any, element: any, attrs: any, linkControllers: any) {
|
||||
expect(linkControllers[0].status).toEqual('WORKS');
|
||||
expect(linkControllers[1].parent).toEqual('PARENT');
|
||||
|
@ -1633,15 +1703,20 @@ withEachNg1Version(() => {
|
|||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onInit() { $onInitSpyA(); }}
|
||||
controller: class {
|
||||
$onInit() {
|
||||
$onInitSpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$onInit = $onInitSpyB; }
|
||||
controller: function(this: any) {
|
||||
this.$onInit = $onInitSpyB;
|
||||
}
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
|
@ -1718,7 +1793,9 @@ withEachNg1Version(() => {
|
|||
|
||||
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
||||
class Ng2Component {
|
||||
constructor(cd: ChangeDetectorRef) { changeDetector = cd; }
|
||||
constructor(cd: ChangeDetectorRef) {
|
||||
changeDetector = cd;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module_('ng1', [])
|
||||
|
@ -1727,15 +1804,20 @@ withEachNg1Version(() => {
|
|||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$doCheck() { $doCheckSpyA(); }}
|
||||
controller: class {
|
||||
$doCheck() {
|
||||
$doCheckSpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$doCheck = $doCheckSpyB; }
|
||||
controller: function(this: any) {
|
||||
this.$doCheck = $doCheckSpyB;
|
||||
}
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
|
@ -1773,7 +1855,9 @@ withEachNg1Version(() => {
|
|||
|
||||
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
||||
class Ng2Component {
|
||||
constructor(cd: ChangeDetectorRef) { changeDetector = cd; }
|
||||
constructor(cd: ChangeDetectorRef) {
|
||||
changeDetector = cd;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module_('ng1', [])
|
||||
|
@ -1835,15 +1919,20 @@ withEachNg1Version(() => {
|
|||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$postLink() { $postLinkSpyA(); }}
|
||||
controller: class {
|
||||
$postLink() {
|
||||
$postLinkSpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$postLink = $postLinkSpyB; }
|
||||
controller: function(this: any) {
|
||||
this.$postLink = $postLinkSpyB;
|
||||
}
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
|
@ -1924,7 +2013,9 @@ withEachNg1Version(() => {
|
|||
template: '<ng1-a [valA]="val"></ng1-a> | <ng1-b [valB]="val"></ng1-b>'
|
||||
})
|
||||
class Ng2Component {
|
||||
constructor() { ng2Instance = this; }
|
||||
constructor() {
|
||||
ng2Instance = this;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module_('ng1', [])
|
||||
|
@ -1937,15 +2028,15 @@ withEachNg1Version(() => {
|
|||
this.$onChanges = $onChangesControllerSpyA;
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B',
|
||||
() => ({
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {valB: '<'},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {
|
||||
$onChanges(changes: SimpleChanges) { $onChangesControllerSpyB(changes); }
|
||||
$onChanges(changes: SimpleChanges) {
|
||||
$onChangesControllerSpyB(changes);
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component))
|
||||
|
@ -2022,7 +2113,9 @@ withEachNg1Version(() => {
|
|||
})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
|
@ -2036,15 +2129,20 @@ withEachNg1Version(() => {
|
|||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onDestroy() { $onDestroySpyA(); }}
|
||||
controller: class {
|
||||
$onDestroy() {
|
||||
$onDestroySpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$onDestroy = $onDestroySpyB; }
|
||||
controller: function(this: any) {
|
||||
this.$onDestroy = $onDestroySpyB;
|
||||
}
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
|
@ -2112,7 +2210,9 @@ withEachNg1Version(() => {
|
|||
})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
|
@ -2187,7 +2287,9 @@ withEachNg1Version(() => {
|
|||
@Component({selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
|
@ -2233,7 +2335,9 @@ withEachNg1Version(() => {
|
|||
@Component({selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
|
@ -2245,8 +2349,8 @@ withEachNg1Version(() => {
|
|||
.component('ng1', {
|
||||
controller: class {
|
||||
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
||||
this.$element.on !('$destroy', elementDestroyListener);
|
||||
this.$element.contents !().on !('$destroy', descendantDestroyListener);
|
||||
this.$element.on!('$destroy', elementDestroyListener);
|
||||
this.$element.contents!().on!('$destroy', descendantDestroyListener);
|
||||
}
|
||||
},
|
||||
template: '<div></div>'
|
||||
|
@ -2287,8 +2391,8 @@ withEachNg1Version(() => {
|
|||
const ng1Component: angular.IComponent = {
|
||||
controller: class {
|
||||
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
||||
this.$element.data !('test', 1);
|
||||
this.$element.contents !().data !('test', 2);
|
||||
this.$element.data!('test', 1);
|
||||
this.$element.contents!().data!('test', 2);
|
||||
|
||||
ng1ComponentElement = this.$element;
|
||||
}
|
||||
|
@ -2301,7 +2405,9 @@ withEachNg1Version(() => {
|
|||
class Ng2ComponentA {
|
||||
destroyIt = false;
|
||||
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentAInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
|
||||
|
@ -2330,15 +2436,15 @@ withEachNg1Version(() => {
|
|||
const $rootScope = ref.ng1RootScope as any;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
expect(ng1ComponentElement.data !('test')).toBe(1);
|
||||
expect(ng1ComponentElement.contents !().data !('test')).toBe(2);
|
||||
expect(ng1ComponentElement.data!('test')).toBe(1);
|
||||
expect(ng1ComponentElement.contents!().data!('test')).toBe(2);
|
||||
|
||||
ng2ComponentAInstance.destroyIt = true;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(ng1ComponentElement.data !('test')).toBeUndefined();
|
||||
expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined();
|
||||
expect(ng1ComponentElement.data!('test')).toBeUndefined();
|
||||
expect(ng1ComponentElement.contents!().data!('test')).toBeUndefined();
|
||||
});
|
||||
}));
|
||||
|
||||
|
@ -2353,10 +2459,10 @@ withEachNg1Version(() => {
|
|||
const ng1Component: angular.IComponent = {
|
||||
controller: class {
|
||||
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
||||
ng1DescendantElement = this.$element.contents !();
|
||||
ng1DescendantElement = this.$element.contents!();
|
||||
|
||||
this.$element.on !('click', elementClickListener);
|
||||
ng1DescendantElement.on !('click', descendantClickListener);
|
||||
this.$element.on!('click', elementClickListener);
|
||||
ng1DescendantElement.on!('click', descendantClickListener);
|
||||
}
|
||||
},
|
||||
template: '<div></div>'
|
||||
|
@ -2367,7 +2473,9 @@ withEachNg1Version(() => {
|
|||
class Ng2ComponentA {
|
||||
destroyIt = false;
|
||||
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentAInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
|
||||
|
@ -2420,7 +2528,11 @@ withEachNg1Version(() => {
|
|||
const ng1Directive: angular.IDirective = {
|
||||
template: '',
|
||||
link: {pre: () => log.push('ng1-pre')},
|
||||
controller: class {constructor() { log.push('ng1-ctrl'); }}
|
||||
controller: class {
|
||||
constructor() {
|
||||
log.push('ng1-ctrl');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define `Ng2Component`
|
||||
|
@ -2577,7 +2689,11 @@ withEachNg1Version(() => {
|
|||
const ng1Directive: angular.IDirective = {
|
||||
template: '',
|
||||
link: () => log.push('ng1-post'),
|
||||
controller: class {$postLink() { log.push('ng1-$post'); }}
|
||||
controller: class {
|
||||
$postLink() {
|
||||
log.push('ng1-$post');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define `Ng2Component`
|
||||
|
@ -2627,13 +2743,17 @@ withEachNg1Version(() => {
|
|||
class Ng2ComponentA {
|
||||
value = 'foo';
|
||||
showB = false;
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentAInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: 'ng2B({{ value }})'})
|
||||
class Ng2ComponentB {
|
||||
value = 'bar';
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentBInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -2678,7 +2798,10 @@ withEachNg1Version(() => {
|
|||
template: 'ng1(<div ng-transclude>{{ $ctrl.value }}</div>)',
|
||||
transclude: true,
|
||||
controller: class {
|
||||
value = 'from-ng1'; constructor() { ng1ControllerInstances.push(this); }
|
||||
value = 'from-ng1';
|
||||
constructor() {
|
||||
ng1ControllerInstances.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2697,7 +2820,9 @@ withEachNg1Version(() => {
|
|||
})
|
||||
class Ng2Component {
|
||||
value = 'from-ng2';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -2756,7 +2881,9 @@ withEachNg1Version(() => {
|
|||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -2798,8 +2925,12 @@ withEachNg1Version(() => {
|
|||
const ng1Component: angular.IComponent = {
|
||||
template: 'ng1(default(<div ng-transclude="">fallback-{{ $ctrl.value }}</div>))',
|
||||
transclude: {slotX: 'contentX', slotY: 'contentY'},
|
||||
controller:
|
||||
class {value = 'ng1'; constructor() { ng1ControllerInstances.push(this); }}
|
||||
controller: class {
|
||||
value = 'ng1';
|
||||
constructor() {
|
||||
ng1ControllerInstances.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define `Ng2Component`
|
||||
|
@ -2830,7 +2961,9 @@ withEachNg1Version(() => {
|
|||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -2880,7 +3013,11 @@ withEachNg1Version(() => {
|
|||
)`,
|
||||
transclude: {slotX: '?contentX', slotY: '?contentY'},
|
||||
controller: class {
|
||||
x = 'ng1X'; y = 'ng1Y'; constructor() { ng1ControllerInstances.push(this); }
|
||||
x = 'ng1X';
|
||||
y = 'ng1Y';
|
||||
constructor() {
|
||||
ng1ControllerInstances.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2896,7 +3033,9 @@ withEachNg1Version(() => {
|
|||
class Ng2Component {
|
||||
x = 'ng2X';
|
||||
y = 'ng2Y';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -3000,7 +3139,9 @@ withEachNg1Version(() => {
|
|||
x = 'foo';
|
||||
y = 'bar';
|
||||
show = true;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
|
@ -3202,13 +3343,18 @@ withEachNg1Version(() => {
|
|||
const ng1Module = angular.module_('ng1', []);
|
||||
let a1Injector: angular.IInjectorService|undefined;
|
||||
ng1Module.run([
|
||||
'$injector', function($injector: angular.IInjectorService) { a1Injector = $injector; }
|
||||
'$injector',
|
||||
function($injector: angular.IInjectorService) {
|
||||
a1Injector = $injector;
|
||||
}
|
||||
]);
|
||||
|
||||
const element = html('<div></div>');
|
||||
window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
|
||||
|
||||
adapter.bootstrap(element, [ng1Module.name]).ready((ref) => { ref.dispose(); });
|
||||
adapter.bootstrap(element, [ng1Module.name]).ready((ref) => {
|
||||
ref.dispose();
|
||||
});
|
||||
|
||||
tick(100);
|
||||
|
||||
|
@ -3275,7 +3421,7 @@ withEachNg1Version(() => {
|
|||
|
||||
document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
||||
|
||||
adapter.bootstrap(document.body.firstElementChild !, ['myExample']).ready((ref) => {
|
||||
adapter.bootstrap(document.body.firstElementChild!, ['myExample']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual('ng2[ng1[Hello World!](transclude)](project)');
|
||||
ref.dispose();
|
||||
|
|
|
@ -284,11 +284,7 @@ describe('bluebird promise', () => {
|
|||
.each(
|
||||
BluebirdPromise.map(arr, (item: number) => BluebirdPromise.resolve(item)),
|
||||
(r: number, idx: number) => {
|
||||
<<<<<<< HEAD
|
||||
expect(r).toBe(arr[idx]);
|
||||
=======
|
||||
expect(r === arr[idx]).toBeTrue();
|
||||
>>>>>>> 253023848d... build: update jasmine to 3.5
|
||||
expect(Zone.current.name).toEqual('bluebird');
|
||||
})
|
||||
.then((r: any) => {
|
||||
|
@ -309,11 +305,7 @@ describe('bluebird promise', () => {
|
|||
.mapSeries(
|
||||
BluebirdPromise.map(arr, (item: number) => BluebirdPromise.resolve(item)),
|
||||
(r: number, idx: number) => {
|
||||
<<<<<<< HEAD
|
||||
expect(r).toBe(arr[idx]);
|
||||
=======
|
||||
expect(r === arr[idx]).toBeTrue();
|
||||
>>>>>>> 253023848d... build: update jasmine to 3.5
|
||||
expect(Zone.current.name).toEqual('bluebird');
|
||||
})
|
||||
.then((r: any) => {
|
||||
|
|
|
@ -21,7 +21,9 @@ describe('crypto test', () => {
|
|||
const zoneASpec = {
|
||||
name: 'A',
|
||||
onScheduleTask: (delegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task):
|
||||
Task => { return delegate.scheduleTask(targetZone, task); }
|
||||
Task => {
|
||||
return delegate.scheduleTask(targetZone, task);
|
||||
}
|
||||
};
|
||||
const zoneA = Zone.current.fork(zoneASpec);
|
||||
spyOn(zoneASpec, 'onScheduleTask').and.callThrough();
|
||||
|
@ -44,7 +46,9 @@ describe('crypto test', () => {
|
|||
const zoneASpec = {
|
||||
name: 'A',
|
||||
onScheduleTask: (delegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task):
|
||||
Task => { return delegate.scheduleTask(targetZone, task); }
|
||||
Task => {
|
||||
return delegate.scheduleTask(targetZone, task);
|
||||
}
|
||||
};
|
||||
const zoneA = Zone.current.fork(zoneASpec);
|
||||
spyOn(zoneASpec, 'onScheduleTask').and.callThrough();
|
||||
|
|
Loading…
Reference in New Issue