Merge pull request #5016 from colemanw/APIExplorer
[civicrm-core.git] / tests / karma / unit / crmAutosaveSpec.js
1 'use strict';
2
3 describe('crmAutosave', function() {
4
5 beforeEach(function() {
6 module('crmUtil');
7 module('crmAutosave');
8 });
9
10 describe('Autosave directive', function() {
11 var $compile,
12 $rootScope,
13 $interval,
14 $timeout,
15 fakeCtrl,
16 model,
17 element;
18
19 beforeEach(inject(function(_$compile_, _$rootScope_, _$interval_, _$timeout_, _$q_) {
20 // The injector unwraps the underscores (_) from around the parameter names when matching
21 $compile = _$compile_;
22 $rootScope = _$rootScope_;
23 $interval = _$interval_;
24 $timeout = _$timeout_;
25
26 $rootScope.fakeCtrl = fakeCtrl = {
27 doSave: function() {
28 },
29 doSaveWithPromise: function() {
30 var dfr = _$q_.defer();
31 $timeout(function() {
32 dfr.resolve();
33 }, 25);
34 return dfr.promise;
35 },
36 doSaveSlowly: function() {
37 var dfr = _$q_.defer();
38 fakeCtrl.savingSlowly = true;
39 $timeout(function() {
40 fakeCtrl.savingSlowly = false;
41 dfr.resolve();
42 }, 1000);
43 return dfr.promise;
44 }
45 };
46 spyOn(fakeCtrl, 'doSave').and.callThrough();
47 spyOn(fakeCtrl, 'doSaveWithPromise').and.callThrough();
48 spyOn(fakeCtrl, 'doSaveSlowly').and.callThrough();
49
50 $rootScope.model = model = {
51 fieldA: 'alpha',
52 fieldB: 'beta'
53 };
54 }));
55
56 // Fake wait - advance the interval & timeout
57 // @param int msec Total time to advance the clock
58 // @param int steps Number of times to issue flush()
59 // Higher values provide a more realistic simulation
60 // but can be a bit slower.
61 function wait(msec, steps) {
62 if (!steps) steps = 4;
63 for (var i = 0; i < steps; i++) {
64 $interval.flush(msec/steps);
65 $timeout.flush(msec/steps);
66 }
67 }
68
69 // TODO: Test: If the save function throws an error, or if its promise returns an error, reset form as dirty.
70
71 var fakeSaves = {
72 "fakeCtrl.doSave()": 'doSave',
73 "fakeCtrl.doSaveWithPromise()": 'doSaveWithPromise'
74 };
75 angular.forEach(fakeSaves, function(saveFunc, saveFuncExpr) {
76 it('calls ' + saveFuncExpr + ' twice over the course of three changes', function() {
77 element = $compile('<form name="myForm" crm-autosave="' + saveFuncExpr + '" crm-autosave-model="model" crm-autosave-interval="{poll: 25, save: 50}"><input class="fieldA" ng-model="model.fieldA"><input class="fieldB" ng-model="model.fieldB"></form>')($rootScope);
78 $rootScope.$digest();
79
80 // check that we load pristine data and don't do any saving
81 wait(100);
82 expect(element.find('.fieldA').val()).toBe('alpha');
83 expect(element.find('.fieldB').val()).toBe('beta');
84 expect($rootScope.myForm.$pristine).toBe(true);
85 expect(fakeCtrl[saveFunc].calls.count()).toBe(0);
86
87 // first round of changes
88 element.find('.fieldA').val('eh?').trigger('change');
89 $rootScope.$digest();
90 element.find('.fieldB').val('bee').trigger('change');
91 $rootScope.$digest();
92 expect(model.fieldA).toBe('eh?');
93 expect(model.fieldB).toBe('bee');
94 expect($rootScope.myForm.$pristine).toBe(false);
95 expect(fakeCtrl[saveFunc].calls.count()).toBe(0);
96
97 // first autosave
98 wait(100);
99 expect($rootScope.myForm.$pristine).toBe(true);
100 expect(fakeCtrl[saveFunc].calls.count()).toBe(1);
101
102 // a little stretch of time with nothing happening
103 wait(100);
104 expect(fakeCtrl[saveFunc].calls.count()).toBe(1);
105
106 // second round of changes
107 element.find('.fieldA').val('ah').trigger('change');
108 $rootScope.$digest();
109 expect(model.fieldA).toBe('ah');
110
111 // second autosave
112 expect($rootScope.myForm.$pristine).toBe(false);
113 expect(fakeCtrl[saveFunc].calls.count()).toBe(1);
114 wait(100);
115 expect(fakeCtrl[saveFunc].calls.count()).toBe(2);
116 expect($rootScope.myForm.$pristine).toBe(true);
117
118 // a little stretch of time with nothing happening
119 wait(100);
120 expect(fakeCtrl[saveFunc].calls.count()).toBe(2);
121 });
122 });
123
124 it('does not save an invalid form', function() {
125 element = $compile('<form name="myForm" crm-autosave="fakeCtrl.doSave()" crm-autosave-model="model" crm-autosave-interval="{poll: 25, save: 50}"><input class="fieldA" ng-model="model.fieldA"><input class="fieldB" required ng-model="model.fieldB"></form>')($rootScope);
126 $rootScope.$digest();
127
128 // check that we load pristine data and don't do any saving
129 wait(100);
130 expect(element.find('.fieldA').val()).toBe('alpha');
131 expect(element.find('.fieldB').val()).toBe('beta');
132 expect($rootScope.myForm.$pristine).toBe(true);
133 expect(fakeCtrl.doSave.calls.count()).toBe(0);
134
135 // first round of changes - fieldB is invalid
136 element.find('.fieldA').val('eh?').trigger('change');
137 $rootScope.$digest();
138 element.find('.fieldB').val('').trigger('change');
139 $rootScope.$digest();
140 expect(model.fieldA).toBe('eh?');
141 expect(model.fieldB).toBeFalsy();
142 expect($rootScope.myForm.$pristine).toBe(false);
143 expect(fakeCtrl.doSave.calls.count()).toBe(0);
144
145 // first autosave declines to run
146 wait(100);
147 expect($rootScope.myForm.$pristine).toBe(false);
148 expect(fakeCtrl.doSave.calls.count()).toBe(0);
149
150 // second round of changes
151 element.find('.fieldB').val('bee').trigger('change');
152 $rootScope.$digest();
153 expect(model.fieldB).toBe('bee');
154
155 // second autosave
156 expect($rootScope.myForm.$pristine).toBe(false);
157 expect(fakeCtrl.doSave.calls.count()).toBe(0);
158 wait(100);
159 expect(fakeCtrl.doSave.calls.count()).toBe(1);
160 expect($rootScope.myForm.$pristine).toBe(true);
161
162 // a little stretch of time with nothing happening
163 wait(100);
164 expect(fakeCtrl.doSave.calls.count()).toBe(1);
165 });
166
167 it('defers saving new changes when a save is already pending', function() {
168 element = $compile('<form name="myForm" crm-autosave="fakeCtrl.doSaveSlowly()" crm-autosave-model="model" crm-autosave-interval="{poll: 25, save: 50}"><input class="fieldA" ng-model="model.fieldA"><input class="fieldB" ng-model="model.fieldB"></form>')($rootScope);
169 $rootScope.$digest();
170
171 // check that we load pristine data and don't do any saving
172 wait(100);
173 expect(element.find('.fieldA').val()).toBe('alpha');
174 expect(element.find('.fieldB').val()).toBe('beta');
175 expect($rootScope.myForm.$pristine).toBe(true);
176 expect(fakeCtrl.doSaveSlowly.calls.count()).toBe(0);
177
178 // first round of changes
179 element.find('.fieldA').val('eh?').trigger('change');
180 $rootScope.$digest();
181 expect(model.fieldA).toBe('eh?');
182 expect($rootScope.myForm.$pristine).toBe(false);
183 expect(fakeCtrl.doSaveSlowly.calls.count()).toBe(0);
184
185 // first autosave starts
186 wait(100);
187 expect(fakeCtrl.savingSlowly).toBe(true);
188 expect(fakeCtrl.doSaveSlowly.calls.count()).toBe(1);
189
190 // second round of changes; doesn't save yet
191 element.find('.fieldA').val('aleph').trigger('change');
192 $rootScope.$digest();
193 expect(model.fieldA).toBe('aleph');
194 expect(fakeCtrl.savingSlowly).toBe(true);
195 expect(fakeCtrl.doSaveSlowly.calls.count()).toBe(1);
196 wait(100);
197 expect(fakeCtrl.doSaveSlowly.calls.count()).toBe(1);
198
199 // second autosave starts and finishes
200 wait(2500, 5);
201 expect(fakeCtrl.savingSlowly).toBe(false);
202 expect(fakeCtrl.doSaveSlowly.calls.count()).toBe(2);
203 });
204 });
205 });