Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
feat(animate): animationsAllowed property to turn off all animations
Browse files Browse the repository at this point in the history
This is required for writing end-to-end tests against AngularDart apps
that use animations.  In such tests, you typically want to test the
functionality of the application and not necessarily deal with the
animations.  This allows you to turn off all animations in your
production site under test.

The Animate class has a new property, animationsAllowed, which can be
set to false to disable all animations.  When true (the default),
whether or not animations occur depend on the animation module installed
and the AnimationOptimizer.
  • Loading branch information
chirayuk committed Jul 10, 2014
1 parent 75c2f17 commit b3f2e6c
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 32 deletions.
7 changes: 7 additions & 0 deletions lib/animate/animation_optimizer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class AnimationOptimizer {
final Map<Animation, dom.Element> _animations = new Map<Animation,
dom.Element>();

/**
* Toggle to disable all animations through this optimizer.
*/
bool animationsAllowed = true;
final Map<dom.Node, bool> _alwaysAnimate = new Map<dom.Node, bool>();
final Map<dom.Node, bool> _alwaysAnimateChildren = new Map<dom.Node, bool>();

Expand Down Expand Up @@ -103,6 +107,9 @@ class AnimationOptimizer {
* and [false] if the optimizer thinks that it should not execute.
*/
bool shouldAnimate(dom.Node node) {
if (!animationsAllowed) {
return false;
}
bool alwaysAnimate = _alwaysAnimate[node];
if (alwaysAnimate != null) {
return alwaysAnimate;
Expand Down
5 changes: 5 additions & 0 deletions lib/animate/css_animate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ class CssAnimate implements Animate {

CssAnimate(this._runner, this._animationMap, this._optimizer);

bool get animationsAllowed => _optimizer.animationsAllowed;
void set animationsAllowed(bool allowed) {
_optimizer.animationsAllowed = allowed;
}

Animation addClass(dom.Element element, String cssClass) {
if (!_optimizer.shouldAnimate(element)) {
element.classes.add(cssClass);
Expand Down
6 changes: 6 additions & 0 deletions lib/core_dom/animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ part of angular.core.dom_internal;
*/
@Injectable()
class Animate {
/**
* When set to false, all animations are disabled. When true, animations are
* allowed.
*/
bool animationsAllowed = true;

/**
* Add the [cssClass] to the classes on [element] after running any
* defined animations.
Expand Down
3 changes: 3 additions & 0 deletions scripts/travis/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ echo ===========================================================================
. ./scripts/env.sh
$DART --version
$PUB install

# Record dart version for tests.
echo $'import "dart:js";\n\nmain() {\n context["DART_VERSION"] = \''"$($DART --version 2>&1)"$'\';\n}' > test/dart_version.dart
70 changes: 42 additions & 28 deletions test/animate/animation_optimizer_spec.dart
Original file line number Diff line number Diff line change
@@ -1,91 +1,92 @@
library animation_optimizer_spec;

import '../_specs.dart';
import 'dart:js' as js;

main() {
describe('AnimationLoop', () {
_run({bool animationsAllowed}) {
describe('animationsAllowed=$animationsAllowed', () {
TestBed _;
AnimationOptimizer optimizer;
beforeEach(inject((TestBed tb, Expando expand) {
_ = tb;
optimizer = new AnimationOptimizer(expand);
optimizer = new AnimationOptimizer(expand)..animationsAllowed = animationsAllowed;
}));

it('should prevent animations on child elements', () {
var animation = new NoOpAnimation();
_.compile('<div><div></div></div>');


expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
optimizer.track(animation, _.rootElement);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();
optimizer.forget(animation);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
});

it('should allow multiple animations on the same element', () {
var animation1 = new NoOpAnimation();
var animation2 = new NoOpAnimation();
_.compile('<div><div></div></div>');

expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
optimizer.track(animation1, _.rootElement);
expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
optimizer.track(animation2, _.rootElement);
expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();
optimizer.forget(animation1);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();
optimizer.forget(animation2);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
});

it('should always animate an element', () {
_.compile('<div><div></div></div>');
optimizer.alwaysAnimate(_.rootElement.children[0], "never");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();
optimizer.alwaysAnimate(_.rootElement.children[0], "always");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
optimizer.alwaysAnimate(_.rootElement.children[0], "auto");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
});

it('alwaysAnimate should not affect children', () {
_.compile('<div><div></div></div>');
optimizer.alwaysAnimate(_.rootElement, "never");
expect(optimizer.shouldAnimate(_.rootElement)).toBeFalsy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
optimizer.alwaysAnimate(_.rootElement, "always");
expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
optimizer.alwaysAnimate(_.rootElement, "auto");
expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
});


it('alwaysAnimateChildren should not affect element', () {
_.compile('<div><div></div></div>');

optimizer.alwaysAnimateChildren(_.rootElement, "never");
expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();

optimizer.alwaysAnimateChildren(_.rootElement, "always");
expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);

optimizer.alwaysAnimateChildren(_.rootElement, "auto");
expect(optimizer.shouldAnimate(_.rootElement)).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement)).toBe(animationsAllowed);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
});

it('alwaysAnimate should take priority over alwaysAnimateChildren', () {
_.compile('<div><div></div></div>');

optimizer.alwaysAnimateChildren(_.rootElement, "never");
optimizer.alwaysAnimate(_.rootElement.children[0], "always");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);

optimizer.alwaysAnimateChildren(_.rootElement, "always");
optimizer.alwaysAnimate(_.rootElement.children[0], "never");
Expand All @@ -100,13 +101,13 @@ main() {
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();

optimizer.alwaysAnimate(_.rootElement.children[0], "always");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);

optimizer.alwaysAnimate(_.rootElement.children[0], "auto");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();

optimizer.forget(animation);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);
});

it('alwaysAnimateChildren should take priority over running animations',
Expand All @@ -118,13 +119,13 @@ main() {
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();

optimizer.alwaysAnimateChildren(_.rootElement, "always");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);

optimizer.alwaysAnimateChildren(_.rootElement, "auto");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();

optimizer.forget(animation);
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeTruthy();
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBe(animationsAllowed);

optimizer.alwaysAnimateChildren(_.rootElement, "never");
expect(optimizer.shouldAnimate(_.rootElement.children[0])).toBeFalsy();
Expand All @@ -138,7 +139,7 @@ main() {

optimizer.alwaysAnimateChildren(_.rootElement, "always");
expect(optimizer.shouldAnimate(_.rootElement.children[0].children[0]))
.toBeTruthy();
.toBe(animationsAllowed);

optimizer.alwaysAnimateChildren(_.rootElement.children[0], "never");
expect(optimizer.shouldAnimate(_.rootElement.children[0].children[0]))
Expand All @@ -148,7 +149,20 @@ main() {
optimizer.alwaysAnimateChildren(_.rootElement, "never");
optimizer.alwaysAnimateChildren(_.rootElement.children[0], "always");
expect(optimizer.shouldAnimate(_.rootElement.children[0].children[0]))
.toBeTruthy();
.toBe(animationsAllowed);
});
});
}

main() {
describe('AnimationLoop', () {
_run(animationsAllowed: true);
if (!identical(1, 1.0) && js.context['DART_VERSION'].toString().contains("version: 1.5.")) {
// Remove this block when issue #1219 is fixed.
// In Dart 1.5's Dartium, running both describes in any order causes
// ng_model_spec to fails. This is not the case in Dart 1.4 or Dart 1.6.
return;
}
_run(animationsAllowed: false);
});
}
28 changes: 24 additions & 4 deletions test/animate/css_animate_spec.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
library css_animate_spec;

import 'dart:async';
import 'dart:js' as js;

import '../_specs.dart';

main() {
describe('CssAnimate', () {
_run({bool animationsAllowed}) {
describe('animationsAllowed=$animationsAllowed', () {
TestBed _;
Animate animate;
MockAnimationLoop runner;

beforeEach(inject((TestBed tb, Expando expand) {
_ = tb;
runner = new MockAnimationLoop();
runner = new MockAnimationLoop(animationsAllowed);
animate = new CssAnimate(runner,
new CssAnimationMap(), new AnimationOptimizer(expand));
animate.animationsAllowed = animationsAllowed;
}));

it('should add a css class to an element node', async(() {
Expand Down Expand Up @@ -91,7 +93,9 @@ main() {
_.compile('<div></div>');
animate.addClass(_.rootElement, 'test');
runner.start();
expect(_.rootElement).toHaveClass('test-add');
if (animationsAllowed) {
expect(_.rootElement).toHaveClass('test-add');
}
var spans = es('<span>A</span><span>B</span>');
animate.insert(spans, _.rootElement);
runner.start();
Expand All @@ -101,8 +105,11 @@ main() {
}

class MockAnimationLoop extends Mock implements AnimationLoop {
bool animationsAllowed;
num time = 0.0;

MockAnimationLoop(this.animationsAllowed);

Future<AnimationResult> get onCompleted {
var cmp = new Completer<AnimationResult>();
cmp.complete(AnimationResult.COMPLETED);
Expand All @@ -129,3 +136,16 @@ class MockAnimationLoop extends Mock implements AnimationLoop {

noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

main() {
describe('CssAnimate', () {
_run(animationsAllowed: true);
if (!identical(1, 1.0) && js.context['DART_VERSION'].toString().contains("version: 1.5.")) {
// Remove this block when issue #1219 is fixed.
// In Dart 1.5's Dartium, running both describes in any order causes
// ng_model_spec to fails. This is not the case in Dart 1.4 or Dart 1.6.
return;
}
_run(animationsAllowed: false);
});
}

0 comments on commit b3f2e6c

Please sign in to comment.