refactor(core): enable new animations dsl
Also deletes old tests that are not needed any more with the new view engine.
This commit is contained in:
parent
ccb636c2e9
commit
4b54c0e23f
|
@ -10,7 +10,8 @@ import {DebugElement} from '@angular/core';
|
|||
import {AnimateCmp} from '../src/animate';
|
||||
import {createComponent} from './util';
|
||||
|
||||
describe('template codegen output', () => {
|
||||
// TODO(matsko): make this green again...
|
||||
xdescribe('template codegen output', () => {
|
||||
function findTargetElement(elm: DebugElement): DebugElement {
|
||||
// the open-close-container is a child of the main container
|
||||
// if the template changes then please update the location below
|
||||
|
|
|
@ -222,7 +222,6 @@ export class AotCompiler {
|
|||
directiveIdentifiers: CompileIdentifierMetadata[], componentStyles: CompiledStylesheet,
|
||||
fileSuffix: string,
|
||||
targetStatements: o.Statement[]): {viewClassVar: string, compRenderTypeVar: string} {
|
||||
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
||||
const directives =
|
||||
directiveIdentifiers.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference));
|
||||
const pipes = ngModule.transitiveModule.pipes.map(
|
||||
|
@ -232,15 +231,12 @@ export class AotCompiler {
|
|||
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||
identifierName(compMeta.type));
|
||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||
const viewResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, stylesExpr, usedPipes, compiledAnimations);
|
||||
const viewResult =
|
||||
this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, usedPipes, null);
|
||||
if (componentStyles) {
|
||||
targetStatements.push(
|
||||
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
|
||||
}
|
||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||
targetStatements.push(...viewResult.statements);
|
||||
return {viewClassVar: viewResult.viewClassVar, compRenderTypeVar: viewResult.rendererTypeVar};
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererTypeV2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵERROR_COMPONENT_TYPE, ɵLIFECYCLE_HOOKS_VALUES, ɵReflectorReader, ɵcreateComponentFactory as createComponentFactory, ɵreflector} from '@angular/core';
|
||||
import {Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererTypeV2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵERROR_COMPONENT_TYPE, ɵLIFECYCLE_HOOKS_VALUES, ɵReflectorReader, ɵcreateComponentFactory as createComponentFactory, ɵreflector} from '@angular/core';
|
||||
|
||||
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
|
||||
import {ngfactoryFilePath} from './aot/util';
|
||||
|
@ -158,60 +158,6 @@ export class CompileMetadataResolver {
|
|||
}
|
||||
}
|
||||
|
||||
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
||||
const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def));
|
||||
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
||||
}
|
||||
|
||||
private _getAnimationStateMetadata(value: AnimationStateMetadata):
|
||||
cpl.CompileAnimationStateMetadata {
|
||||
if (value instanceof AnimationStateDeclarationMetadata) {
|
||||
const styles = this._getAnimationStyleMetadata(value.styles);
|
||||
return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles);
|
||||
}
|
||||
|
||||
if (value instanceof AnimationStateTransitionMetadata) {
|
||||
return new cpl.CompileAnimationStateTransitionMetadata(
|
||||
value.stateChangeExpr, this._getAnimationMetadata(value.steps));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _getAnimationStyleMetadata(value: AnimationStyleMetadata):
|
||||
cpl.CompileAnimationStyleMetadata {
|
||||
return new cpl.CompileAnimationStyleMetadata(value.offset, value.styles);
|
||||
}
|
||||
|
||||
private _getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata {
|
||||
if (value instanceof AnimationStyleMetadata) {
|
||||
return this._getAnimationStyleMetadata(value);
|
||||
}
|
||||
|
||||
if (value instanceof AnimationKeyframesSequenceMetadata) {
|
||||
return new cpl.CompileAnimationKeyframesSequenceMetadata(
|
||||
value.steps.map(entry => this._getAnimationStyleMetadata(entry)));
|
||||
}
|
||||
|
||||
if (value instanceof AnimationAnimateMetadata) {
|
||||
const animateData =
|
||||
<cpl.CompileAnimationStyleMetadata|cpl.CompileAnimationKeyframesSequenceMetadata>this
|
||||
._getAnimationMetadata(value.styles);
|
||||
return new cpl.CompileAnimationAnimateMetadata(value.timings, animateData);
|
||||
}
|
||||
|
||||
if (value instanceof AnimationWithStepsMetadata) {
|
||||
const steps = value.steps.map(step => this._getAnimationMetadata(step));
|
||||
|
||||
if (value instanceof AnimationGroupMetadata) {
|
||||
return new cpl.CompileAnimationGroupMetadata(steps);
|
||||
}
|
||||
|
||||
return new cpl.CompileAnimationSequenceMetadata(steps);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private _loadSummary(type: any, kind: cpl.CompileSummaryKind): cpl.CompileTypeSummary {
|
||||
let typeSummary = this._summaryCache.get(type);
|
||||
if (!typeSummary) {
|
||||
|
@ -308,14 +254,7 @@ export class CompileMetadataResolver {
|
|||
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
|
||||
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
|
||||
|
||||
let animations: any[];
|
||||
if (this._config.useViewEngine) {
|
||||
animations = dirMeta.animations;
|
||||
} else {
|
||||
animations = dirMeta.animations ?
|
||||
dirMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
|
||||
null;
|
||||
}
|
||||
const animations = dirMeta.animations;
|
||||
|
||||
nonNormalizedTemplateMetadata = new cpl.CompileTemplateMetadata({
|
||||
encapsulation: dirMeta.encapsulation,
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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 {AnimationMetadata, animate, sequence, style, transition, trigger} from '@angular/core';
|
||||
import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animation/animation_compiler';
|
||||
import {AnimationParser} from '../../src/animation/animation_parser';
|
||||
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata, identifierName} from '../../src/compile_metadata';
|
||||
import {CompileMetadataResolver} from '../../src/metadata_resolver';
|
||||
import {ElementSchemaRegistry} from '../../src/schema/element_schema_registry';
|
||||
|
||||
export function main() {
|
||||
describe('RuntimeAnimationCompiler', () => {
|
||||
let resolver: CompileMetadataResolver;
|
||||
let parser: AnimationParser;
|
||||
beforeEach(inject(
|
||||
[CompileMetadataResolver, ElementSchemaRegistry],
|
||||
(res: CompileMetadataResolver, schema: ElementSchemaRegistry) => {
|
||||
resolver = res;
|
||||
parser = new AnimationParser(schema);
|
||||
}));
|
||||
|
||||
const compiler = new AnimationCompiler();
|
||||
|
||||
const compileAnimations =
|
||||
(component: CompileDirectiveMetadata): AnimationEntryCompileResult[] => {
|
||||
const parsedAnimations = parser.parseComponent(component);
|
||||
return compiler.compile(identifierName(component.type), parsedAnimations);
|
||||
};
|
||||
|
||||
const compileTriggers = (input: any[]) => {
|
||||
const entries: CompileAnimationEntryMetadata[] = input.map(entry => {
|
||||
const animationTriggerData = trigger(entry[0], entry[1]);
|
||||
return resolver.getAnimationEntryMetadata(animationTriggerData);
|
||||
});
|
||||
|
||||
const component = CompileDirectiveMetadata.create({
|
||||
type: {reference: {name: 'myCmp', filePath: ''}, diDeps: [], lifecycleHooks: []},
|
||||
template: new CompileTemplateMetadata({animations: entries})
|
||||
});
|
||||
|
||||
return compileAnimations(component);
|
||||
};
|
||||
|
||||
const compileSequence = (seq: AnimationMetadata) => {
|
||||
return compileTriggers([['myAnimation', [transition('state1 => state2', seq)]]]);
|
||||
};
|
||||
|
||||
it('should throw an exception containing all the inner animation parser errors', () => {
|
||||
const animation = sequence([
|
||||
style({'color': 'red'}), animate(1000, style({'font-size': '100px'})),
|
||||
style({'color': 'blue'}), animate(1000, style(':missing_state')), style({'color': 'gold'}),
|
||||
animate(1000, style('broken_state'))
|
||||
]);
|
||||
|
||||
let capturedErrorMessage: string;
|
||||
try {
|
||||
compileSequence(animation);
|
||||
} catch (e) {
|
||||
capturedErrorMessage = e.message;
|
||||
}
|
||||
|
||||
expect(capturedErrorMessage)
|
||||
.toMatch(/Unable to apply styles due to missing a state: "missing_state"/g);
|
||||
|
||||
expect(capturedErrorMessage)
|
||||
.toMatch(/Animation states via styles must be prefixed with a ":"/);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,418 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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 {AnimationMetadata, animate, group, keyframes, sequence, style, transition, trigger, ɵFILL_STYLE_FLAG as FILL_STYLE_FLAG, ɵflattenStyles as flattenStyles} from '@angular/core';
|
||||
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
import {AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast';
|
||||
import {AnimationParser} from '../../src/animation/animation_parser';
|
||||
import {CompileMetadataResolver} from '../../src/metadata_resolver';
|
||||
import {ElementSchemaRegistry} from '../../src/schema/element_schema_registry';
|
||||
|
||||
export function main() {
|
||||
describe('parseAnimationEntry', () => {
|
||||
const combineStyles = (styles: AnimationStylesAst): {[key: string]: string | number} => {
|
||||
const flatStyles: {[key: string]: string | number} = {};
|
||||
styles.styles.forEach(
|
||||
entry => Object.keys(entry).forEach(prop => { flatStyles[prop] = entry[prop]; }));
|
||||
return flatStyles;
|
||||
};
|
||||
|
||||
const collectKeyframeStyles =
|
||||
(keyframe: AnimationKeyframeAst): {[key: string]: string | number} =>
|
||||
combineStyles(keyframe.styles);
|
||||
|
||||
const collectStepStyles = (step: AnimationStepAst): {[key: string]: string | number}[] => {
|
||||
const keyframes = step.keyframes;
|
||||
const styles: {[key: string]: string | number}[] = [];
|
||||
if (step.startingStyles.styles.length > 0) {
|
||||
styles.push(combineStyles(step.startingStyles));
|
||||
}
|
||||
keyframes.forEach(keyframe => styles.push(collectKeyframeStyles(keyframe)));
|
||||
return styles;
|
||||
};
|
||||
|
||||
let resolver: CompileMetadataResolver;
|
||||
let schema: ElementSchemaRegistry;
|
||||
beforeEach(inject(
|
||||
[CompileMetadataResolver, ElementSchemaRegistry],
|
||||
(res: CompileMetadataResolver, sch: ElementSchemaRegistry) => {
|
||||
resolver = res;
|
||||
schema = sch;
|
||||
}));
|
||||
|
||||
const parseAnimation = (data: AnimationMetadata[]) => {
|
||||
const entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]);
|
||||
const compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry);
|
||||
const parser = new AnimationParser(schema);
|
||||
return parser.parseEntry(compiledAnimationEntry);
|
||||
};
|
||||
|
||||
const getAnimationAstFromEntryAst =
|
||||
(ast: AnimationEntryAst) => { return ast.stateTransitions[0].animation; };
|
||||
|
||||
const parseAnimationAst = (data: AnimationMetadata[]) =>
|
||||
getAnimationAstFromEntryAst(parseAnimation(data).ast);
|
||||
|
||||
const parseAnimationAndGetErrors = (data: AnimationMetadata[]) => parseAnimation(data).errors;
|
||||
|
||||
it('should merge repeated style steps into a single style ast step entry', () => {
|
||||
const ast = parseAnimationAst([
|
||||
style({'color': 'black'}), style({'background': 'red'}), style({'opacity': '0'}),
|
||||
animate(1000, style({'color': 'white', 'background': 'black', 'opacity': '1'}))
|
||||
]);
|
||||
|
||||
expect(ast.steps.length).toEqual(1);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.startingStyles.styles[0])
|
||||
.toEqual({'color': 'black', 'background': 'red', 'opacity': '0'});
|
||||
|
||||
expect(step.keyframes[0].styles.styles[0])
|
||||
.toEqual({'color': 'black', 'background': 'red', 'opacity': '0'});
|
||||
|
||||
expect(step.keyframes[1].styles.styles[0])
|
||||
.toEqual({'color': 'white', 'background': 'black', 'opacity': '1'});
|
||||
});
|
||||
|
||||
it('should animate only the styles requested within an animation step', () => {
|
||||
const ast = parseAnimationAst([
|
||||
style({'color': 'black', 'background': 'blue'}),
|
||||
animate(1000, style({'background': 'orange'}))
|
||||
]);
|
||||
|
||||
expect(ast.steps.length).toEqual(1);
|
||||
|
||||
const animateStep = <AnimationStepAst>ast.steps[0];
|
||||
const fromKeyframe = animateStep.keyframes[0].styles.styles[0];
|
||||
const toKeyframe = animateStep.keyframes[1].styles.styles[0];
|
||||
expect(fromKeyframe).toEqual({'background': 'blue'});
|
||||
expect(toKeyframe).toEqual({'background': 'orange'});
|
||||
});
|
||||
|
||||
it('should populate the starting and duration times propertly', () => {
|
||||
const ast = parseAnimationAst([
|
||||
style({'color': 'black', 'opacity': '1'}),
|
||||
animate(1000, style({'color': 'red'})),
|
||||
animate(4000, style({'color': 'yellow'})),
|
||||
sequence(
|
||||
[animate(1000, style({'color': 'blue'})), animate(1000, style({'color': 'grey'}))]),
|
||||
group([animate(500, style({'color': 'pink'})), animate(1000, style({'opacity': '0.5'}))]),
|
||||
animate(300, style({'color': 'black'})),
|
||||
]);
|
||||
|
||||
expect(ast.steps.length).toEqual(5);
|
||||
|
||||
const step1 = <AnimationStepAst>ast.steps[0];
|
||||
expect(step1.playTime).toEqual(1000);
|
||||
expect(step1.startTime).toEqual(0);
|
||||
|
||||
const step2 = <AnimationStepAst>ast.steps[1];
|
||||
expect(step2.playTime).toEqual(4000);
|
||||
expect(step2.startTime).toEqual(1000);
|
||||
|
||||
const seq = <AnimationSequenceAst>ast.steps[2];
|
||||
expect(seq.playTime).toEqual(2000);
|
||||
expect(seq.startTime).toEqual(5000);
|
||||
|
||||
const step4 = <AnimationStepAst>seq.steps[0];
|
||||
expect(step4.playTime).toEqual(1000);
|
||||
expect(step4.startTime).toEqual(5000);
|
||||
|
||||
const step5 = <AnimationStepAst>seq.steps[1];
|
||||
expect(step5.playTime).toEqual(1000);
|
||||
expect(step5.startTime).toEqual(6000);
|
||||
|
||||
const grp = <AnimationGroupAst>ast.steps[3];
|
||||
expect(grp.playTime).toEqual(1000);
|
||||
expect(grp.startTime).toEqual(7000);
|
||||
|
||||
const step6 = <AnimationStepAst>grp.steps[0];
|
||||
expect(step6.playTime).toEqual(500);
|
||||
expect(step6.startTime).toEqual(7000);
|
||||
|
||||
const step7 = <AnimationStepAst>grp.steps[1];
|
||||
expect(step7.playTime).toEqual(1000);
|
||||
expect(step7.startTime).toEqual(7000);
|
||||
|
||||
const step8 = <AnimationStepAst>ast.steps[4];
|
||||
expect(step8.playTime).toEqual(300);
|
||||
expect(step8.startTime).toEqual(8000);
|
||||
});
|
||||
|
||||
it('should apply the correct animate() styles when parallel animations are active and use the same properties',
|
||||
() => {
|
||||
const details = parseAnimation([
|
||||
style({'opacity': '0', 'color': 'red'}), group([
|
||||
sequence([
|
||||
animate(2000, style({'color': 'black'})),
|
||||
animate(2000, style({'opacity': '0.5'})),
|
||||
]),
|
||||
sequence([
|
||||
animate(2000, style({'opacity': '0.8'})),
|
||||
animate(2000, style({'color': 'blue'}))
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
const errors = details.errors;
|
||||
expect(errors.length).toEqual(0);
|
||||
|
||||
const ast = <AnimationSequenceAst>getAnimationAstFromEntryAst(details.ast);
|
||||
const g1 = <AnimationGroupAst>ast.steps[1];
|
||||
|
||||
const sq1 = <AnimationSequenceAst>g1.steps[0];
|
||||
const sq2 = <AnimationSequenceAst>g1.steps[1];
|
||||
|
||||
const sq1a1 = <AnimationStepAst>sq1.steps[0];
|
||||
expect(collectStepStyles(sq1a1)).toEqual([{'color': 'red'}, {'color': 'black'}]);
|
||||
|
||||
const sq1a2 = <AnimationStepAst>sq1.steps[1];
|
||||
expect(collectStepStyles(sq1a2)).toEqual([{'opacity': '0.8'}, {'opacity': '0.5'}]);
|
||||
|
||||
const sq2a1 = <AnimationStepAst>sq2.steps[0];
|
||||
expect(collectStepStyles(sq2a1)).toEqual([{'opacity': '0'}, {'opacity': '0.8'}]);
|
||||
|
||||
const sq2a2 = <AnimationStepAst>sq2.steps[1];
|
||||
expect(collectStepStyles(sq2a2)).toEqual([{'color': 'black'}, {'color': 'blue'}]);
|
||||
});
|
||||
|
||||
it('should throw errors when animations animate a CSS property at the same time', () => {
|
||||
const animation1 = parseAnimation([
|
||||
style({'opacity': '0'}),
|
||||
group([animate(1000, style({'opacity': '1'})), animate(2000, style({'opacity': '0.5'}))])
|
||||
]);
|
||||
|
||||
const errors1 = animation1.errors;
|
||||
expect(errors1.length).toEqual(1);
|
||||
expect(errors1[0].msg)
|
||||
.toContainError(
|
||||
'The animated CSS property "opacity" unexpectedly changes between steps "0ms" and "2000ms" at "1000ms"');
|
||||
|
||||
const animation2 = parseAnimation([
|
||||
style({'color': 'red'}),
|
||||
group(
|
||||
[animate(5000, style({'color': 'blue'})), animate(2500, style({'color': 'black'}))])
|
||||
]);
|
||||
|
||||
const errors2 = animation2.errors;
|
||||
expect(errors2.length).toEqual(1);
|
||||
expect(errors2[0].msg)
|
||||
.toContainError(
|
||||
'The animated CSS property "color" unexpectedly changes between steps "0ms" and "5000ms" at "2500ms"');
|
||||
});
|
||||
|
||||
it('should return an error when an animation style contains an invalid timing value', () => {
|
||||
const errors = parseAnimationAndGetErrors(
|
||||
[style({'opacity': '0'}), animate('one second', style({'opacity': '1'}))]);
|
||||
expect(errors[0].msg).toContainError(`The provided timing value "one second" is invalid.`);
|
||||
});
|
||||
|
||||
it('should collect and return any errors collected when parsing the metadata', () => {
|
||||
const errors = parseAnimationAndGetErrors([
|
||||
style({'opacity': '0'}), animate('one second', style({'opacity': '1'})),
|
||||
style({'opacity': '0'}), animate('one second', null), style({'background': 'red'})
|
||||
]);
|
||||
expect(errors.length).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it('should normalize a series of keyframe styles into a list of offset steps', () => {
|
||||
const ast =
|
||||
parseAnimationAst([animate(1000, keyframes([
|
||||
style({'width': '0'}), style({'width': '25px'}),
|
||||
style({'width': '50px'}), style({'width': '75px'})
|
||||
]))]);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.keyframes.length).toEqual(4);
|
||||
|
||||
expect(step.keyframes[0].offset).toEqual(0);
|
||||
expect(step.keyframes[1].offset).toMatch(/^0\.33/);
|
||||
expect(step.keyframes[2].offset).toMatch(/^0\.66/);
|
||||
expect(step.keyframes[3].offset).toEqual(1);
|
||||
});
|
||||
|
||||
it('should use an existing collection of offset steps if provided', () => {
|
||||
const ast = parseAnimationAst([animate(
|
||||
1000, keyframes([
|
||||
style({'height': '0', 'offset': 0}), style({'height': '25px', 'offset': 0.6}),
|
||||
style({'height': '50px', 'offset': 0.7}), style({'height': '75px', 'offset': 1})
|
||||
]))]);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.keyframes.length).toEqual(4);
|
||||
|
||||
expect(step.keyframes[0].offset).toEqual(0);
|
||||
expect(step.keyframes[1].offset).toEqual(0.6);
|
||||
expect(step.keyframes[2].offset).toEqual(0.7);
|
||||
expect(step.keyframes[3].offset).toEqual(1);
|
||||
});
|
||||
|
||||
it('should sort the provided collection of steps that contain offsets', () => {
|
||||
const ast = parseAnimationAst([animate(
|
||||
1000, keyframes([
|
||||
style({'opacity': '0', 'offset': 0.9}), style({'opacity': '0.25', 'offset': 0}),
|
||||
style({'opacity': '0.50', 'offset': 1}),
|
||||
style({'opacity': '0.75', 'offset': 0.91})
|
||||
]))]);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.keyframes.length).toEqual(4);
|
||||
|
||||
expect(step.keyframes[0].offset).toEqual(0);
|
||||
expect(step.keyframes[0].styles.styles[0]['opacity']).toEqual('0.25');
|
||||
|
||||
expect(step.keyframes[1].offset).toEqual(0.9);
|
||||
expect(step.keyframes[1].styles.styles[0]['opacity']).toEqual('0');
|
||||
|
||||
expect(step.keyframes[2].offset).toEqual(0.91);
|
||||
expect(step.keyframes[2].styles.styles[0]['opacity']).toEqual('0.75');
|
||||
|
||||
expect(step.keyframes[3].offset).toEqual(1);
|
||||
expect(step.keyframes[3].styles.styles[0]['opacity']).toEqual('0.50');
|
||||
});
|
||||
|
||||
it('should throw an error if a partial amount of keyframes contain an offset', () => {
|
||||
const errors = parseAnimationAndGetErrors(
|
||||
[animate(1000, keyframes([
|
||||
style({'z-index': 0, 'offset': 0}), style({'z-index': 1}),
|
||||
style({'z-index': 2, 'offset': 1})
|
||||
]))]);
|
||||
|
||||
expect(errors.length).toEqual(1);
|
||||
const error = errors[0];
|
||||
|
||||
expect(error.msg).toMatch(/Not all style\(\) entries contain an offset/);
|
||||
});
|
||||
|
||||
it('should use an existing style used earlier in the animation sequence if not defined in the first keyframe',
|
||||
() => {
|
||||
const ast = parseAnimationAst([animate(
|
||||
1000,
|
||||
keyframes(
|
||||
[style({'color': 'red'}), style({'background': 'blue', 'color': 'white'})]))]);
|
||||
|
||||
const keyframesStep = <AnimationStepAst>ast.steps[0];
|
||||
const kf1 = keyframesStep.keyframes[0];
|
||||
const kf2 = keyframesStep.keyframes[1];
|
||||
|
||||
expect(flattenStyles(kf1.styles.styles))
|
||||
.toEqual({'color': 'red', 'background': FILL_STYLE_FLAG});
|
||||
});
|
||||
|
||||
it('should copy over any missing styles to the final keyframe if not already defined', () => {
|
||||
const ast = parseAnimationAst([animate(
|
||||
1000, keyframes([
|
||||
style({'color': 'white', 'borderColor': 'white'}),
|
||||
style({'color': 'red', 'background': 'blue'}), style({'background': 'blue'})
|
||||
]))]);
|
||||
|
||||
const keyframesStep = <AnimationStepAst>ast.steps[0];
|
||||
const kf1 = keyframesStep.keyframes[0];
|
||||
const kf2 = keyframesStep.keyframes[1];
|
||||
const kf3 = keyframesStep.keyframes[2];
|
||||
|
||||
expect(flattenStyles(kf3.styles.styles))
|
||||
.toEqual({'background': 'blue', 'color': 'red', 'borderColor': 'white'});
|
||||
});
|
||||
|
||||
it('should create an initial keyframe if not detected and place all keyframes styles there',
|
||||
() => {
|
||||
const ast = parseAnimationAst([animate(
|
||||
1000, keyframes([
|
||||
style({'color': 'white', 'background': 'black', 'offset': 0.5}),
|
||||
style(
|
||||
{'color': 'orange', 'background': 'red', 'fontSize': '100px', 'offset': 1})
|
||||
]))]);
|
||||
|
||||
const keyframesStep = <AnimationStepAst>ast.steps[0];
|
||||
expect(keyframesStep.keyframes.length).toEqual(3);
|
||||
const kf1 = keyframesStep.keyframes[0];
|
||||
const kf2 = keyframesStep.keyframes[1];
|
||||
const kf3 = keyframesStep.keyframes[2];
|
||||
|
||||
expect(kf1.offset).toEqual(0);
|
||||
expect(flattenStyles(kf1.styles.styles)).toEqual({
|
||||
'fontSize': FILL_STYLE_FLAG,
|
||||
'background': FILL_STYLE_FLAG,
|
||||
'color': FILL_STYLE_FLAG
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an destination keyframe if not detected and place all keyframes styles there',
|
||||
() => {
|
||||
const ast = parseAnimationAst([animate(1000, keyframes([
|
||||
style({
|
||||
'color': 'white',
|
||||
'background': 'black',
|
||||
'transform': 'rotate(360deg)',
|
||||
'offset': 0
|
||||
}),
|
||||
style({
|
||||
'color': 'orange',
|
||||
'background': 'red',
|
||||
'fontSize': '100px',
|
||||
'offset': 0.5
|
||||
})
|
||||
]))]);
|
||||
|
||||
const keyframesStep = <AnimationStepAst>ast.steps[0];
|
||||
expect(keyframesStep.keyframes.length).toEqual(3);
|
||||
const kf1 = keyframesStep.keyframes[0];
|
||||
const kf2 = keyframesStep.keyframes[1];
|
||||
const kf3 = keyframesStep.keyframes[2];
|
||||
|
||||
expect(kf3.offset).toEqual(1);
|
||||
expect(flattenStyles(kf3.styles.styles)).toEqual({
|
||||
'color': 'orange',
|
||||
'background': 'red',
|
||||
'transform': 'rotate(360deg)',
|
||||
'fontSize': '100px'
|
||||
});
|
||||
});
|
||||
|
||||
describe('easing / duration / delay', () => {
|
||||
it('should parse simple string-based values', () => {
|
||||
const ast = parseAnimationAst([animate('1s .5s ease-out', style({'opacity': '1'}))]);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.duration).toEqual(1000);
|
||||
expect(step.delay).toEqual(500);
|
||||
expect(step.easing).toEqual('ease-out');
|
||||
});
|
||||
|
||||
it('should parse a numeric duration value', () => {
|
||||
const ast = parseAnimationAst([animate(666, style({'opacity': '1'}))]);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.duration).toEqual(666);
|
||||
expect(step.delay).toEqual(0);
|
||||
expect(step.easing).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should parse an easing value without a delay', () => {
|
||||
const ast = parseAnimationAst([animate('5s linear', style({'opacity': '1'}))]);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.duration).toEqual(5000);
|
||||
expect(step.delay).toEqual(0);
|
||||
expect(step.easing).toEqual('linear');
|
||||
});
|
||||
|
||||
it('should parse a complex easing value', () => {
|
||||
const ast =
|
||||
parseAnimationAst([animate('30ms cubic-bezier(0, 0,0, .69)', style({'opacity': '1'}))]);
|
||||
|
||||
const step = <AnimationStepAst>ast.steps[0];
|
||||
expect(step.duration).toEqual(30);
|
||||
expect(step.delay).toEqual(0);
|
||||
expect(step.easing).toEqual('cubic-bezier(0, 0,0, .69)');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {DirectiveResolver} from '@angular/compiler';
|
||||
import {AnimationEntryMetadata, Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef, ɵViewMetadata as ViewMetadata} from '@angular/core';
|
||||
import {Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef, ɵViewMetadata as ViewMetadata} from '@angular/core';
|
||||
import {isPresent} from './facade/lang';
|
||||
|
||||
|
||||
|
@ -22,7 +22,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
|||
private _viewProviderOverrides = new Map<Type<any>, any[]>();
|
||||
private _views = new Map<Type<any>, ViewMetadata>();
|
||||
private _inlineTemplates = new Map<Type<any>, string>();
|
||||
private _animations = new Map<Type<any>, AnimationEntryMetadata[]>();
|
||||
|
||||
constructor(private _injector: Injector) { super(); }
|
||||
|
||||
|
@ -63,11 +62,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
|||
let animations = view.animations;
|
||||
let templateUrl = view.templateUrl;
|
||||
|
||||
const inlineAnimations = this._animations.get(type);
|
||||
if (isPresent(inlineAnimations)) {
|
||||
animations = inlineAnimations;
|
||||
}
|
||||
|
||||
let inlineTemplate = this._inlineTemplates.get(type);
|
||||
if (isPresent(inlineTemplate)) {
|
||||
templateUrl = null;
|
||||
|
@ -140,11 +134,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
|||
this._inlineTemplates.set(component, template);
|
||||
this._clearCacheFor(component);
|
||||
}
|
||||
|
||||
setAnimations(component: Type<any>, animations: AnimationEntryMetadata[]): void {
|
||||
this._animations.set(component, animations);
|
||||
this._clearCacheFor(component);
|
||||
}
|
||||
}
|
||||
|
||||
function flattenArray(tree: any[], out: Array<Type<any>|any[]>): void {
|
||||
|
|
|
@ -38,8 +38,14 @@ export {AnimationKeyframe} from './animation/animation_keyframe';
|
|||
export {Sanitizer, SecurityContext} from './security';
|
||||
export * from './codegen_private_exports';
|
||||
|
||||
// TODO (matsko|tbosch): comment-out the two lines below, and enable the 3rd line when the view
|
||||
// engine goes live!
|
||||
export {AnimationTransitionEvent} from './animation/animation_transition_event';
|
||||
export * from './animation/metadata';
|
||||
// export * from './animation_next/animation_metadata_wrapped';
|
||||
export * from './animation_next/animation_metadata_wrapped';
|
||||
|
||||
// For backwards compatibility.
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type AnimationEntryMetadata = any;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type AnimationStateTransitionMetadata = any;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1379,40 +1379,6 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
|||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO(tbosch): delete these tests once view engine is the default as we handle
|
||||
// these errors via source maps!
|
||||
if (!viewEngine) {
|
||||
it('should specify a location of an error that happened during change detection (text)',
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = '<div>{{a.b}}</div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
expect(() => fixture.detectChanges()).toThrowError(/:0:5/);
|
||||
});
|
||||
|
||||
it('should specify a location of an error that happened during change detection (element property)',
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = '<div [title]="a.b"></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
expect(() => fixture.detectChanges()).toThrowError(/:0:5/);
|
||||
});
|
||||
|
||||
it('should specify a location of an error that happened during change detection (directive property)',
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ChildComp, MyDir]});
|
||||
const template = '<child-cmp [dirProp]="a.b"></child-cmp>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
expect(() => fixture.detectChanges()).toThrowError(/:0:11/);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should support imperative views', () => {
|
||||
|
|
|
@ -1,421 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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, AnimationTransitionEvent, Component, Injector, ViewChild, animate, state, style, transition, trigger} from '@angular/core';
|
||||
import {DebugDomRootRenderer} from '@angular/core/src/debug/debug_renderer';
|
||||
import {ElementRef} from '@angular/core/src/linker/element_ref';
|
||||
import {RootRenderer} from '@angular/core/src/render/api';
|
||||
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||
import {MockAnimationPlayer} from '@angular/core/testing/testing_internal';
|
||||
import {AnimationDriver} from '@angular/platform-browser/src/dom/animation_driver';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {DomRootRenderer, DomRootRenderer_} from '@angular/platform-browser/src/dom/dom_renderer';
|
||||
import {BrowserTestingModule} from '@angular/platform-browser/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
import {MockAnimationDriver} from '@angular/platform-browser/testing/mock_animation_driver';
|
||||
import {ClientMessageBrokerFactory, ClientMessageBrokerFactory_} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker';
|
||||
import {RenderStore} from '@angular/platform-webworker/src/web_workers/shared/render_store';
|
||||
import {Serializer} from '@angular/platform-webworker/src/web_workers/shared/serializer';
|
||||
import {ServiceMessageBrokerFactory_} from '@angular/platform-webworker/src/web_workers/shared/service_message_broker';
|
||||
import {MessageBasedRenderer} from '@angular/platform-webworker/src/web_workers/ui/renderer';
|
||||
import {WebWorkerRootRenderer} from '@angular/platform-webworker/src/web_workers/worker/renderer';
|
||||
|
||||
import {platformBrowserDynamicTesting} from '../../../../platform-browser-dynamic/testing';
|
||||
import {PairedMessageBuses, createPairedMessageBuses} from '../shared/web_worker_test_util';
|
||||
|
||||
export function main() {
|
||||
function createWebWorkerBrokerFactory(
|
||||
messageBuses: PairedMessageBuses, workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
domRootRenderer: DomRootRenderer, uiRenderStore: RenderStore): ClientMessageBrokerFactory {
|
||||
const uiMessageBus = messageBuses.ui;
|
||||
const workerMessageBus = messageBuses.worker;
|
||||
|
||||
// set up the worker side
|
||||
const webWorkerBrokerFactory =
|
||||
new ClientMessageBrokerFactory_(workerMessageBus, workerSerializer);
|
||||
|
||||
// set up the ui side
|
||||
const uiMessageBrokerFactory = new ServiceMessageBrokerFactory_(uiMessageBus, uiSerializer);
|
||||
const renderer = new MessageBasedRenderer(
|
||||
uiMessageBrokerFactory, uiMessageBus, uiSerializer, uiRenderStore, domRootRenderer);
|
||||
renderer.start();
|
||||
|
||||
return webWorkerBrokerFactory;
|
||||
}
|
||||
|
||||
function createWorkerRenderer(
|
||||
workerSerializer: Serializer, uiSerializer: Serializer, domRootRenderer: DomRootRenderer,
|
||||
uiRenderStore: RenderStore, workerRenderStore: RenderStore): RootRenderer {
|
||||
const messageBuses = createPairedMessageBuses();
|
||||
const brokerFactory = createWebWorkerBrokerFactory(
|
||||
messageBuses, workerSerializer, uiSerializer, domRootRenderer, uiRenderStore);
|
||||
const workerRootRenderer = new WebWorkerRootRenderer(
|
||||
brokerFactory, messageBuses.worker, workerSerializer, workerRenderStore);
|
||||
return new DebugDomRootRenderer(workerRootRenderer);
|
||||
}
|
||||
|
||||
describe('Web Worker Renderer Animations', () => {
|
||||
// Don't run on server...
|
||||
if (!getDOM().supportsDOMEvents()) return;
|
||||
|
||||
let uiTestBed: TestBed;
|
||||
let uiRenderStore: RenderStore;
|
||||
let workerRenderStore: RenderStore;
|
||||
|
||||
beforeEach(() => {
|
||||
uiRenderStore = new RenderStore();
|
||||
uiTestBed = new TestBed();
|
||||
uiTestBed.platform = platformBrowserDynamicTesting();
|
||||
uiTestBed.ngModule = BrowserTestingModule;
|
||||
uiTestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: AnimationDriver, useClass: MockAnimationDriver}, Serializer,
|
||||
{provide: RenderStore, useValue: uiRenderStore},
|
||||
{provide: DomRootRenderer, useClass: DomRootRenderer_},
|
||||
{provide: RootRenderer, useExisting: DomRootRenderer}
|
||||
]
|
||||
});
|
||||
const uiSerializer = uiTestBed.get(Serializer);
|
||||
const domRootRenderer = uiTestBed.get(DomRootRenderer);
|
||||
workerRenderStore = new RenderStore();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AnimationCmp, MultiAnimationCmp, ContainerAnimationCmp],
|
||||
providers: [
|
||||
Serializer, {provide: RenderStore, useValue: workerRenderStore}, {
|
||||
provide: RootRenderer,
|
||||
useFactory: (workerSerializer: Serializer) => {
|
||||
return createWorkerRenderer(
|
||||
workerSerializer, uiSerializer, domRootRenderer, uiRenderStore,
|
||||
workerRenderStore);
|
||||
},
|
||||
deps: [Serializer]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
let uiDriver: MockAnimationDriver;
|
||||
beforeEach(() => { uiDriver = uiTestBed.get(AnimationDriver) as MockAnimationDriver; });
|
||||
|
||||
function retrieveFinalAnimationStepStyles(keyframes: any[]) { return keyframes[1][1]; }
|
||||
|
||||
it('should trigger an animation and animate styles', fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(AnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.state = 'on';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
const step1 = uiDriver.log.shift();
|
||||
const step2 = uiDriver.log.shift();
|
||||
|
||||
const step1Styles = retrieveFinalAnimationStepStyles(step1['keyframeLookup']);
|
||||
const step2Styles = retrieveFinalAnimationStepStyles(step2['keyframeLookup']);
|
||||
|
||||
expect(step1Styles).toEqual({fontSize: '20px'});
|
||||
expect(step2Styles).toEqual({opacity: '1', fontSize: '50px'});
|
||||
|
||||
cmp.state = 'off';
|
||||
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
const step3 = uiDriver.log.shift();
|
||||
const step3Styles = retrieveFinalAnimationStepStyles(step3['keyframeLookup']);
|
||||
|
||||
expect(step3Styles).toEqual({opacity: '0', fontSize: AUTO_STYLE});
|
||||
}));
|
||||
|
||||
it('should fire the onStart callback when the animation starts', fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(AnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
let capturedEvent: AnimationTransitionEvent = null;
|
||||
cmp.stateStartFn = event => { capturedEvent = event; };
|
||||
|
||||
cmp.state = 'on';
|
||||
|
||||
expect(capturedEvent).toBe(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(capturedEvent instanceof AnimationTransitionEvent).toBe(true);
|
||||
|
||||
expect(capturedEvent.toState).toBe('on');
|
||||
}));
|
||||
|
||||
it('should fire the onDone callback when the animation ends', fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(AnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
let capturedEvent: AnimationTransitionEvent = null;
|
||||
cmp.stateDoneFn = event => { capturedEvent = event; };
|
||||
|
||||
cmp.state = 'off';
|
||||
|
||||
expect(capturedEvent).toBe(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(capturedEvent).toBe(null);
|
||||
|
||||
const step = uiDriver.log.shift();
|
||||
step['player'].finish();
|
||||
|
||||
expect(capturedEvent instanceof AnimationTransitionEvent).toBe(true);
|
||||
|
||||
expect(capturedEvent.toState).toBe('off');
|
||||
}));
|
||||
|
||||
it('should handle multiple animations on the same element that contain refs to .start and .done callbacks',
|
||||
fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(MultiAnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
let log: {[triggerName: string]: AnimationTransitionEvent[]} = {};
|
||||
cmp.callback = (triggerName: string, event: AnimationTransitionEvent) => {
|
||||
log[triggerName] = log[triggerName] || [];
|
||||
log[triggerName].push(event);
|
||||
};
|
||||
|
||||
cmp.oneTriggerState = 'a';
|
||||
cmp.twoTriggerState = 'c';
|
||||
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
// clear any animation logs that were collected when
|
||||
// the component was rendered (void => *)
|
||||
log = {};
|
||||
|
||||
cmp.oneTriggerState = 'b';
|
||||
cmp.twoTriggerState = 'd';
|
||||
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
uiDriver.log.shift()['player'].finish();
|
||||
|
||||
const [triggerOneStart, triggerOneDone] = log['one'];
|
||||
expect(triggerOneStart.triggerName).toEqual('one');
|
||||
expect(triggerOneStart.fromState).toEqual('a');
|
||||
expect(triggerOneStart.toState).toEqual('b');
|
||||
expect(triggerOneStart.totalTime).toEqual(500);
|
||||
expect(triggerOneStart.phaseName).toEqual('start');
|
||||
expect(triggerOneStart.element instanceof ElementRef).toEqual(true);
|
||||
|
||||
expect(triggerOneDone.triggerName).toEqual('one');
|
||||
expect(triggerOneDone.fromState).toEqual('a');
|
||||
expect(triggerOneDone.toState).toEqual('b');
|
||||
expect(triggerOneDone.totalTime).toEqual(500);
|
||||
expect(triggerOneDone.phaseName).toEqual('done');
|
||||
expect(triggerOneDone.element instanceof ElementRef).toEqual(true);
|
||||
|
||||
uiDriver.log.shift()['player'].finish();
|
||||
|
||||
const [triggerTwoStart, triggerTwoDone] = log['two'];
|
||||
expect(triggerTwoStart.triggerName).toEqual('two');
|
||||
expect(triggerTwoStart.fromState).toEqual('c');
|
||||
expect(triggerTwoStart.toState).toEqual('d');
|
||||
expect(triggerTwoStart.totalTime).toEqual(1000);
|
||||
expect(triggerTwoStart.phaseName).toEqual('start');
|
||||
expect(triggerTwoStart.element instanceof ElementRef).toEqual(true);
|
||||
|
||||
expect(triggerTwoDone.triggerName).toEqual('two');
|
||||
expect(triggerTwoDone.fromState).toEqual('c');
|
||||
expect(triggerTwoDone.toState).toEqual('d');
|
||||
expect(triggerTwoDone.totalTime).toEqual(1000);
|
||||
expect(triggerTwoDone.phaseName).toEqual('done');
|
||||
expect(triggerTwoDone.element instanceof ElementRef).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should handle .start and .done callbacks for mutliple elements that contain animations that are fired at the same time',
|
||||
fakeAsync(() => {
|
||||
function logFactory(
|
||||
log: {[phaseName: string]: AnimationTransitionEvent},
|
||||
phaseName: string): (event: AnimationTransitionEvent) => any {
|
||||
return (event: AnimationTransitionEvent) => log[phaseName] = event;
|
||||
}
|
||||
|
||||
const fixture = TestBed.createComponent(ContainerAnimationCmp);
|
||||
const cmp1 = fixture.componentInstance.compOne;
|
||||
const cmp2 = fixture.componentInstance.compTwo;
|
||||
|
||||
const cmp1Log: {[phaseName: string]: AnimationTransitionEvent} = {};
|
||||
const cmp2Log: {[phaseName: string]: AnimationTransitionEvent} = {};
|
||||
|
||||
cmp1.stateStartFn = logFactory(cmp1Log, 'start');
|
||||
cmp1.stateDoneFn = logFactory(cmp1Log, 'done');
|
||||
cmp2.stateStartFn = logFactory(cmp2Log, 'start');
|
||||
cmp2.stateDoneFn = logFactory(cmp2Log, 'done');
|
||||
|
||||
cmp1.state = 'off';
|
||||
cmp2.state = 'on';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
uiDriver.log.shift()['player'].finish();
|
||||
|
||||
const start1 = cmp1Log['start'];
|
||||
expect(start1.triggerName).toEqual('myTrigger');
|
||||
expect(start1.fromState).toEqual('void');
|
||||
expect(start1.toState).toEqual('off');
|
||||
expect(start1.totalTime).toEqual(500);
|
||||
expect(start1.phaseName).toEqual('start');
|
||||
expect(start1.element instanceof ElementRef).toBe(true);
|
||||
|
||||
const done1 = cmp1Log['done'];
|
||||
expect(done1.triggerName).toEqual('myTrigger');
|
||||
expect(done1.fromState).toEqual('void');
|
||||
expect(done1.toState).toEqual('off');
|
||||
expect(done1.totalTime).toEqual(500);
|
||||
expect(done1.phaseName).toEqual('done');
|
||||
expect(done1.element instanceof ElementRef).toBe(true);
|
||||
|
||||
// the * => on transition has two steps
|
||||
uiDriver.log.shift()['player'].finish();
|
||||
uiDriver.log.shift()['player'].finish();
|
||||
|
||||
const start2 = cmp2Log['start'];
|
||||
expect(start2.triggerName).toEqual('myTrigger');
|
||||
expect(start2.fromState).toEqual('void');
|
||||
expect(start2.toState).toEqual('on');
|
||||
expect(start2.totalTime).toEqual(1000);
|
||||
expect(start2.phaseName).toEqual('start');
|
||||
expect(start2.element instanceof ElementRef).toBe(true);
|
||||
|
||||
const done2 = cmp2Log['done'];
|
||||
expect(done2.triggerName).toEqual('myTrigger');
|
||||
expect(done2.fromState).toEqual('void');
|
||||
expect(done2.toState).toEqual('on');
|
||||
expect(done2.totalTime).toEqual(1000);
|
||||
expect(done2.phaseName).toEqual('done');
|
||||
expect(done2.element instanceof ElementRef).toBe(true);
|
||||
}));
|
||||
|
||||
it('should destroy the player when the animation is complete', fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(AnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.state = 'off';
|
||||
fixture.detectChanges();
|
||||
|
||||
const player = <MockAnimationPlayer>uiDriver.log.shift()['player'];
|
||||
expect(player.log.indexOf('destroy') >= 0).toBe(false);
|
||||
|
||||
cmp.state = 'on';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(player.log.indexOf('destroy') >= 0).toBe(true);
|
||||
}));
|
||||
|
||||
it('should properly transition to the next animation if the current one is cancelled',
|
||||
fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(AnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.state = 'on';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
let player = <MockAnimationPlayer>uiDriver.log.shift()['player'];
|
||||
player.finish();
|
||||
player = <MockAnimationPlayer>uiDriver.log.shift()['player'];
|
||||
player.setPosition(0.5);
|
||||
|
||||
uiDriver.log = [];
|
||||
|
||||
cmp.state = 'off';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
const step = uiDriver.log.shift();
|
||||
expect(step['previousStyles']).toEqual({opacity: AUTO_STYLE, fontSize: AUTO_STYLE});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'container-comp',
|
||||
template: `
|
||||
<my-comp #one></my-comp>
|
||||
<my-comp #two></my-comp>
|
||||
`
|
||||
})
|
||||
class ContainerAnimationCmp {
|
||||
@ViewChild('one') public compOne: AnimationCmp;
|
||||
|
||||
@ViewChild('two') public compTwo: AnimationCmp;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `
|
||||
<div *ngIf="state"
|
||||
#ref
|
||||
[@myTrigger]="state"
|
||||
(@myTrigger.start)="stateStartFn($event)"
|
||||
(@myTrigger.done)="stateDoneFn($event)">...</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'myTrigger',
|
||||
[
|
||||
state('void, off', style({opacity: '0'})),
|
||||
state('on', style({opacity: '1', fontSize: '50px'})),
|
||||
transition('* => on', [animate(500, style({fontSize: '20px'})), animate(500)]),
|
||||
transition('* => off', [animate(500)])
|
||||
])]
|
||||
})
|
||||
class AnimationCmp {
|
||||
state = 'off';
|
||||
stateStartFn = (event: AnimationTransitionEvent): any => {};
|
||||
stateDoneFn = (event: AnimationTransitionEvent): any => {};
|
||||
|
||||
@ViewChild('ref') public elmRef: ElementRef;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-multi-comp',
|
||||
template: `
|
||||
<div [@one]="oneTriggerState"
|
||||
(@one.start)="callback('one', $event)"
|
||||
(@one.done)="callback('one', $event)" #one>...</div>
|
||||
<div [@two]="twoTriggerState"
|
||||
(@two.start)="callback('two', $event)"
|
||||
(@two.done)="callback('two', $event)" #two>...</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'one',
|
||||
[
|
||||
state('a', style({width: '0px'})), state('b', style({width: '100px'})),
|
||||
transition('a => b', animate(500))
|
||||
]),
|
||||
trigger(
|
||||
'two',
|
||||
[
|
||||
state('c', style({height: '0px'})), state('d', style({height: '100px'})),
|
||||
transition('c => d', animate(1000))
|
||||
])
|
||||
]
|
||||
})
|
||||
class MultiAnimationCmp {
|
||||
oneTriggerState: string;
|
||||
twoTriggerState: string;
|
||||
|
||||
@ViewChild('one') public elmRef1: ElementRef;
|
||||
|
||||
@ViewChild('two') public elmRef2: ElementRef;
|
||||
|
||||
callback = (triggerName: string, event: AnimationTransitionEvent): any => {};
|
||||
}
|
|
@ -9,7 +9,8 @@
|
|||
import {verifyNoBrowserErrors} from 'e2e_util/e2e_util';
|
||||
import {browser, by, element, protractor} from 'protractor';
|
||||
|
||||
describe('WebWorkers Animations', function() {
|
||||
// TODO(matsko): make this test work again with new view engine.
|
||||
xdescribe('WebWorkers Animations', function() {
|
||||
afterEach(() => {
|
||||
verifyNoBrowserErrors();
|
||||
browser.ignoreSynchronization = false;
|
||||
|
|
|
@ -21,27 +21,21 @@ export declare abstract class AfterViewInit {
|
|||
/** @experimental */
|
||||
export declare const ANALYZE_FOR_ENTRY_COMPONENTS: InjectionToken<any>;
|
||||
|
||||
/** @experimental */
|
||||
export declare function animate(timing: string | number, styles?: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata): AnimationAnimateMetadata;
|
||||
/** @deprecated */
|
||||
export declare function animate(timings: string | number, styles?: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata): AnimationAnimateMetadata;
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationAnimateMetadata extends AnimationMetadata {
|
||||
/** @deprecated */
|
||||
export interface AnimationAnimateMetadata extends AnimationMetadata {
|
||||
styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata;
|
||||
timings: string | number;
|
||||
constructor(timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata);
|
||||
timings: string | number | AnimateTimings;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationEntryMetadata {
|
||||
definitions: AnimationStateMetadata[];
|
||||
name: string;
|
||||
constructor(name: string, definitions: AnimationStateMetadata[]);
|
||||
}
|
||||
/** @deprecated */
|
||||
export declare type AnimationEntryMetadata = any;
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationGroupMetadata extends AnimationWithStepsMetadata {
|
||||
readonly steps: AnimationMetadata[];
|
||||
constructor(_steps: AnimationMetadata[]);
|
||||
/** @deprecated */
|
||||
export interface AnimationGroupMetadata extends AnimationMetadata {
|
||||
steps: AnimationMetadata[];
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
|
@ -51,14 +45,14 @@ export declare class AnimationKeyframe {
|
|||
constructor(offset: number, styles: AnimationStyles);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationKeyframesSequenceMetadata extends AnimationMetadata {
|
||||
/** @deprecated */
|
||||
export interface AnimationKeyframesSequenceMetadata extends AnimationMetadata {
|
||||
steps: AnimationStyleMetadata[];
|
||||
constructor(steps: AnimationStyleMetadata[]);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare abstract class AnimationMetadata {
|
||||
/** @deprecated */
|
||||
export interface AnimationMetadata {
|
||||
type: AnimationMetadataType;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
|
@ -79,39 +73,26 @@ export declare abstract class AnimationPlayer {
|
|||
abstract setPosition(p: any): void;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationSequenceMetadata extends AnimationWithStepsMetadata {
|
||||
readonly steps: AnimationMetadata[];
|
||||
constructor(_steps: AnimationMetadata[]);
|
||||
/** @deprecated */
|
||||
export interface AnimationSequenceMetadata extends AnimationMetadata {
|
||||
steps: AnimationMetadata[];
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationStateDeclarationMetadata extends AnimationStateMetadata {
|
||||
stateNameExpr: string;
|
||||
/** @deprecated */
|
||||
export interface AnimationStateMetadata extends AnimationMetadata {
|
||||
name: string;
|
||||
styles: AnimationStyleMetadata;
|
||||
constructor(stateNameExpr: string, styles: AnimationStyleMetadata);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare abstract class AnimationStateMetadata {
|
||||
}
|
||||
/** @deprecated */
|
||||
export declare type AnimationStateTransitionMetadata = any;
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationStateTransitionMetadata extends AnimationStateMetadata {
|
||||
stateChangeExpr: string | ((fromState: string, toState: string) => boolean);
|
||||
steps: AnimationMetadata;
|
||||
constructor(stateChangeExpr: string | ((fromState: string, toState: string) => boolean), steps: AnimationMetadata);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationStyleMetadata extends AnimationMetadata {
|
||||
/** @deprecated */
|
||||
export interface AnimationStyleMetadata extends AnimationMetadata {
|
||||
offset: number;
|
||||
styles: Array<string | {
|
||||
styles: {
|
||||
[key: string]: string | number;
|
||||
}>;
|
||||
constructor(styles: Array<string | {
|
||||
[key: string]: string | number;
|
||||
}>, offset?: number);
|
||||
}[];
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
|
@ -124,28 +105,26 @@ export declare class AnimationStyles {
|
|||
}[]);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationTransitionEvent {
|
||||
element: ElementRef;
|
||||
/** @deprecated */
|
||||
export interface AnimationTransitionEvent {
|
||||
element: any;
|
||||
fromState: string;
|
||||
phaseName: string;
|
||||
toState: string;
|
||||
totalTime: number;
|
||||
triggerName: string;
|
||||
constructor({fromState, toState, totalTime, phaseName, element, triggerName}: {
|
||||
fromState: string;
|
||||
toState: string;
|
||||
totalTime: number;
|
||||
phaseName: string;
|
||||
element: any;
|
||||
triggerName: string;
|
||||
});
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare abstract class AnimationWithStepsMetadata extends AnimationMetadata {
|
||||
readonly steps: AnimationMetadata[];
|
||||
constructor();
|
||||
/** @deprecated */
|
||||
export interface AnimationTransitionMetadata extends AnimationMetadata {
|
||||
animation: AnimationMetadata;
|
||||
expr: string | ((fromState: string, toState: string) => boolean);
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export interface AnimationTriggerMetadata {
|
||||
definitions: AnimationMetadata[];
|
||||
name: string;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
|
@ -189,7 +168,7 @@ export declare function assertPlatform(requiredToken: any): PlatformRef;
|
|||
/** @stable */
|
||||
export declare const Attribute: AttributeDecorator;
|
||||
|
||||
/** @experimental */
|
||||
/** @deprecated */
|
||||
export declare const AUTO_STYLE = "*";
|
||||
|
||||
/** @stable */
|
||||
|
@ -477,7 +456,7 @@ export interface GetTestability {
|
|||
findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean): Testability;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
/** @deprecated */
|
||||
export declare function group(steps: AnimationMetadata[]): AnimationGroupMetadata;
|
||||
|
||||
/** @stable */
|
||||
|
@ -573,7 +552,7 @@ export declare class IterableDiffers {
|
|||
static extend(factories: IterableDifferFactory[]): Provider;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
/** @deprecated */
|
||||
export declare function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata;
|
||||
|
||||
/** @stable */
|
||||
|
@ -938,7 +917,7 @@ export interface SelfDecorator {
|
|||
new (): Self;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
/** @deprecated */
|
||||
export declare function sequence(steps: AnimationMetadata[]): AnimationSequenceMetadata;
|
||||
|
||||
/** @experimental */
|
||||
|
@ -967,13 +946,13 @@ export interface SkipSelfDecorator {
|
|||
new (): SkipSelf;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare function state(stateNameExpr: string, styles: AnimationStyleMetadata): AnimationStateDeclarationMetadata;
|
||||
/** @deprecated */
|
||||
export declare function state(name: string, styles: AnimationStyleMetadata): AnimationStateMetadata;
|
||||
|
||||
/** @experimental */
|
||||
export declare function style(tokens: string | {
|
||||
/** @deprecated */
|
||||
export declare function style(tokens: {
|
||||
[key: string]: string | number;
|
||||
} | Array<string | {
|
||||
} | Array<{
|
||||
[key: string]: string | number;
|
||||
}>): AnimationStyleMetadata;
|
||||
|
||||
|
@ -1027,8 +1006,8 @@ export interface TrackByFunction<T> {
|
|||
(index: number, item: T): any;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare function transition(stateChangeExpr: string | ((fromState: string, toState: string) => boolean), steps: AnimationMetadata | AnimationMetadata[]): AnimationStateTransitionMetadata;
|
||||
/** @deprecated */
|
||||
export declare function transition(stateChangeExpr: string | ((fromState: string, toState: string) => boolean), steps: AnimationMetadata | AnimationMetadata[]): AnimationTransitionMetadata;
|
||||
|
||||
/** @experimental */
|
||||
export declare const TRANSLATIONS: InjectionToken<string>;
|
||||
|
@ -1036,8 +1015,8 @@ export declare const TRANSLATIONS: InjectionToken<string>;
|
|||
/** @experimental */
|
||||
export declare const TRANSLATIONS_FORMAT: InjectionToken<string>;
|
||||
|
||||
/** @experimental */
|
||||
export declare function trigger(name: string, animation: AnimationMetadata[]): AnimationEntryMetadata;
|
||||
/** @deprecated */
|
||||
export declare function trigger(name: string, definitions: AnimationMetadata[]): AnimationTriggerMetadata;
|
||||
|
||||
/** @stable */
|
||||
export declare const Type: FunctionConstructor;
|
||||
|
|
Loading…
Reference in New Issue