|
| 1 | +describe('md-input-container animations', function() { |
| 2 | + var $rootScope, $compile, $animate, $animateCss, |
| 3 | + el, pageScope, invalidAnimation, messagesAnimation, messageAnimation, |
| 4 | + cssTransitionsDisabled = false; |
| 5 | + |
| 6 | + // Load our modules |
| 7 | + beforeEach(module('ngAnimate', 'ngMessages', 'material.components.input')); |
| 8 | + |
| 9 | + // Run pre-test setup |
| 10 | + beforeEach(decorateAnimateCss); |
| 11 | + beforeEach(injectGlobals); |
| 12 | + beforeEach(setupVariables); |
| 13 | + |
| 14 | + // Run after-test teardown |
| 15 | + afterEach(teardown); |
| 16 | + |
| 17 | + it('set the proper styles when showing messages', function() { |
| 18 | + compile( |
| 19 | + '<form name="testForm">' + |
| 20 | + ' <md-input-container>' + |
| 21 | + ' <input name="foo" ng-model="foo" required ng-pattern="/^1234$/" />' + |
| 22 | + ' <div class="errors" ng-messages="testForm.foo.$error">' + |
| 23 | + ' <div ng-message="required">required</div>' + |
| 24 | + ' <div ng-message="pattern">pattern</div>' + |
| 25 | + ' </div>' + |
| 26 | + ' </md-input-container>' + |
| 27 | + '</form>' |
| 28 | + ); |
| 29 | + |
| 30 | + var container = el.find('md-input-container'), |
| 31 | + input = el.find('input'); |
| 32 | + |
| 33 | + // Mimic the real validations/animations that fire |
| 34 | + |
| 35 | + /* |
| 36 | + * 1. Set to an invalid pattern but don't blur (so it's not invalid yet) |
| 37 | + * |
| 38 | + * Expect nothing to happen ($animateCss called with no options) |
| 39 | + */ |
| 40 | + |
| 41 | + setFoo('asdf'); |
| 42 | + messageAnimation.enter(getError()).start().done(angular.noop); |
| 43 | + $animate.flush(); |
| 44 | + |
| 45 | + expectError(getError(), 'pattern'); |
| 46 | + expect(container).not.toHaveClass('md-input-invalid'); |
| 47 | + expect(lastAnimateCall()).toEqual({element: getError(), options: {}}); |
| 48 | + |
| 49 | + /* |
| 50 | + * 2. Blur the input, which adds the md-input-invalid class |
| 51 | + * |
| 52 | + * Expect to animate in the pattern message |
| 53 | + */ |
| 54 | + |
| 55 | + input.triggerHandler('blur'); |
| 56 | + invalidAnimation.addClass(container, 'md-input-invalid', angular.noop); |
| 57 | + $animate.flush(); |
| 58 | + |
| 59 | + expectError(getError(), 'pattern'); |
| 60 | + expect(container).toHaveClass('md-input-invalid'); |
| 61 | + expect(lastAnimateCall().element).toEqual(getError()); |
| 62 | + expect(lastAnimateCall().options.event).toEqual('enter'); |
| 63 | + expect(lastAnimateCall().options.to).toEqual({"opacity": 1, "margin-top": "0"}); |
| 64 | + |
| 65 | + /* |
| 66 | + * 3. Clear the field |
| 67 | + * |
| 68 | + * Expect to animate away pattern message and animate in the required message |
| 69 | + */ |
| 70 | + |
| 71 | + // Grab the pattern error before we change foo and it disappears |
| 72 | + var patternError = getError(); |
| 73 | + |
| 74 | + messageAnimation.leave(patternError).start().done(angular.noop); |
| 75 | + $animate.flush(); |
| 76 | + |
| 77 | + expect(lastAnimateCall().element).toEqual(patternError); |
| 78 | + expect(lastAnimateCall().options.event).toEqual('leave'); |
| 79 | + expect(parseInt(lastAnimateCall().options.to["margin-top"])).toBeLessThan(0); |
| 80 | + |
| 81 | + setFoo(''); |
| 82 | + expectError(getError(), 'required'); |
| 83 | + |
| 84 | + messageAnimation.enter(getError()).start().done(angular.noop); |
| 85 | + $animate.flush(); |
| 86 | + |
| 87 | + expect(container).toHaveClass('md-input-invalid'); |
| 88 | + expect(lastAnimateCall().element).toEqual(getError()); |
| 89 | + expect(lastAnimateCall().options.event).toEqual('enter'); |
| 90 | + expect(lastAnimateCall().options.to).toEqual({"opacity": 1, "margin-top": "0"}); |
| 91 | + }); |
| 92 | + |
| 93 | + /* |
| 94 | + * Test Helper Functions |
| 95 | + */ |
| 96 | + |
| 97 | + function compile(template) { |
| 98 | + el = $compile(template)(pageScope); |
| 99 | + angular.element(document.body).append(el); |
| 100 | + |
| 101 | + pageScope.$apply(); |
| 102 | + |
| 103 | + return el; |
| 104 | + } |
| 105 | + |
| 106 | + function lastAnimateCall() { |
| 107 | + return { |
| 108 | + element: $animateCss.calls.mostRecent().args[0], |
| 109 | + options: $animateCss.calls.mostRecent().args[1] |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + function setFoo(value) { |
| 114 | + pageScope.foo = value; |
| 115 | + pageScope.$digest(); |
| 116 | + } |
| 117 | + |
| 118 | + function getError() { |
| 119 | + return angular.element(el[0].querySelector('.errors div')); |
| 120 | + } |
| 121 | + |
| 122 | + function expectError(element, message) { |
| 123 | + expect(element.text().trim()).toBe(message); |
| 124 | + } |
| 125 | + |
| 126 | + /* |
| 127 | + * before/afterEach Helper Functions |
| 128 | + */ |
| 129 | + |
| 130 | + // Decorate the $animateCss service so we can spy on it and disable any CSS transitions |
| 131 | + function decorateAnimateCss() { |
| 132 | + module(function($provide) { |
| 133 | + $provide.decorator('$animateCss', function($delegate) { |
| 134 | + return jasmine.createSpy('$animateCss').and.callFake(function(element, options) { |
| 135 | + // Make sure any transitions happen immediately; NOTE: this is REQUIRED for the above |
| 136 | + // tests to pass without using window.setTimeout to wait for the animations |
| 137 | + if (cssTransitionsDisabled) { |
| 138 | + element.css('transition', '0s none'); |
| 139 | + } |
| 140 | + |
| 141 | + return $delegate(element, options); |
| 142 | + }); |
| 143 | + }); |
| 144 | + }); |
| 145 | + } |
| 146 | + |
| 147 | + // Setup/grab our variables |
| 148 | + function injectGlobals() { |
| 149 | + inject(function($injector) { |
| 150 | + $rootScope = $injector.get('$rootScope'); |
| 151 | + $compile = $injector.get('$compile'); |
| 152 | + $animate = $injector.get('$animate'); |
| 153 | + $animateCss = $injector.get('$animateCss'); |
| 154 | + |
| 155 | + // Grab our input animations |
| 156 | + invalidAnimation = $injector.get('mdInputInvalidAnimation'); |
| 157 | + messagesAnimation = $injector.get('mdInputMessagesAnimation'); |
| 158 | + messageAnimation = $injector.get('mdInputMessageAnimation'); |
| 159 | + }); |
| 160 | + } |
| 161 | + |
| 162 | + // Setup some custom variables for these tests |
| 163 | + function setupVariables() { |
| 164 | + pageScope = $rootScope.$new(); |
| 165 | + cssTransitionsDisabled = true; |
| 166 | + } |
| 167 | + |
| 168 | + // Teardown our tests by resetting variables and removing our element |
| 169 | + function teardown() { |
| 170 | + cssTransitionsDisabled = false; |
| 171 | + |
| 172 | + el && el.remove && el.remove(); |
| 173 | + } |
| 174 | +}); |
0 commit comments