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