Merge pull request #12036 from mattwire/mail_unsetcc
[civicrm-core.git] / tests / karma / unit / crmCaseTypeSpec.js
CommitLineData
384f015c 1/* global $, _, CRM:true */
416abe87
PH
2'use strict';
3
4describe('crmCaseType', function() {
b109dd8b
RO
5 var $controller;
6 var $compile;
7 var $httpBackend;
c9817fac 8 var $q;
b109dd8b 9 var $rootScope;
d7a470db 10 var $timeout;
b109dd8b
RO
11 var apiCalls;
12 var ctrl;
13 var compile;
14 var scope;
416abe87
PH
15
16 beforeEach(function() {
17 CRM.resourceUrls = {
18 'civicrm': ''
19 };
4324b8d7 20 // CRM_Case_XMLProcessor::REL_TYPE_CNAME
416abe87
PH
21 CRM.crmCaseType = {
22 'REL_TYPE_CNAME': 'label_b_a'
23 };
24 module('crmCaseType');
09a09949
PH
25 module('crmJsonComparator');
26 inject(function(crmJsonComparator) {
27 crmJsonComparator.register(jasmine);
416abe87
PH
28 });
29 });
30
d7a470db 31 beforeEach(inject(function(_$controller_, _$compile_, _$httpBackend_, _$q_, _$rootScope_, _$timeout_) {
b109dd8b
RO
32 $controller = _$controller_;
33 $compile = _$compile_;
34 $httpBackend = _$httpBackend_;
c9817fac 35 $q = _$q_;
b109dd8b 36 $rootScope = _$rootScope_;
d7a470db 37 $timeout = _$timeout_;
b109dd8b 38 }));
416abe87 39
b109dd8b
RO
40 describe('CaseTypeCtrl', function() {
41 beforeEach(function () {
416abe87 42 apiCalls = {
4324b8d7
CW
43 actStatuses: {
44 values: [
45 {
416abe87
PH
46 "id": "272",
47 "option_group_id": "25",
48 "label": "Scheduled",
49 "value": "1",
50 "name": "Scheduled",
51 "filter": "0",
52 "is_default": "1",
53 "weight": "1",
54 "is_optgroup": "0",
55 "is_reserved": "1",
56 "is_active": "1"
57 },
4324b8d7 58 {
416abe87
PH
59 "id": "273",
60 "option_group_id": "25",
61 "label": "Completed",
62 "value": "2",
63 "name": "Completed",
64 "filter": "0",
65 "weight": "2",
66 "is_optgroup": "0",
67 "is_reserved": "1",
68 "is_active": "1"
69 }
4324b8d7 70 ]
416abe87 71 },
4324b8d7
CW
72 caseStatuses: {
73 values: [
74 {
75 "id": "290",
76 "option_group_id": "28",
77 "label": "Ongoing",
78 "value": "1",
79 "name": "Open",
80 "grouping": "Opened",
81 "filter": "0",
82 "is_default": "1",
83 "weight": "1",
84 "is_optgroup": "0",
85 "is_reserved": "1",
86 "is_active": "1"
87 },
88 {
89 "id": "291",
90 "option_group_id": "28",
91 "label": "Resolved",
92 "value": "2",
93 "name": "Closed",
94 "grouping": "Closed",
95 "filter": "0",
96 "weight": "2",
97 "is_optgroup": "0",
98 "is_reserved": "1",
99 "is_active": "1"
100 }
101 ]
102 },
103 actTypes: {
104 values: [
105 {
416abe87
PH
106 "id": "784",
107 "option_group_id": "2",
108 "label": "ADC referral",
c866eb5f 109 "value": "62",
416abe87
PH
110 "name": "ADC referral",
111 "filter": "0",
112 "is_default": "0",
113 "weight": "64",
114 "is_optgroup": "0",
115 "is_reserved": "0",
116 "is_active": "1",
117 "component_id": "7"
118 },
4324b8d7 119 {
416abe87
PH
120 "id": "32",
121 "option_group_id": "2",
122 "label": "Add Client To Case",
123 "value": "27",
124 "name": "Add Client To Case",
125 "filter": "0",
126 "is_default": "0",
127 "weight": "26",
128 "description": "",
129 "is_optgroup": "0",
130 "is_reserved": "1",
131 "is_active": "1",
132 "component_id": "7"
ba1ac52d
AH
133 },
134 {
135 "id": "18",
136 "option_group_id": "2",
137 "label": "Open Case",
138 "value": "13",
139 "name": "Open Case",
140 "filter": "0",
141 "is_default": "0",
142 "weight": "13",
143 "is_optgroup": "0",
144 "is_reserved": "1",
145 "is_active": "1",
146 "component_id": "7",
147 "icon": "fa-folder-open-o"
148 },
149 {
150 "id": "857",
151 "option_group_id": "2",
152 "label": "Medical evaluation",
153 "value": "55",
154 "name": "Medical evaluation",
155 "filter": "0",
156 "is_default": "0",
157 "weight": "56",
158 "is_optgroup": "0",
159 "is_reserved": "0",
160 "is_active": "1",
161 "component_id": "7"
162 },
4324b8d7 163 ]
416abe87 164 },
4324b8d7
CW
165 relTypes: {
166 values: [
167 {
416abe87
PH
168 "id": "14",
169 "name_a_b": "Benefits Specialist is",
170 "label_a_b": "Benefits Specialist is",
171 "name_b_a": "Benefits Specialist",
172 "label_b_a": "Benefits Specialist",
173 "description": "Benefits Specialist",
174 "contact_type_a": "Individual",
175 "contact_type_b": "Individual",
176 "is_reserved": "0",
177 "is_active": "1"
178 },
4324b8d7 179 {
416abe87
PH
180 "id": "9",
181 "name_a_b": "Case Coordinator is",
182 "label_a_b": "Case Coordinator is",
183 "name_b_a": "Case Coordinator",
184 "label_b_a": "Case Coordinator",
185 "description": "Case Coordinator",
186 "contact_type_a": "Individual",
187 "contact_type_b": "Individual",
188 "is_reserved": "0",
189 "is_active": "1"
190 }
4324b8d7 191 ]
416abe87 192 },
4324b8d7 193 caseType: {
416abe87
PH
194 "id": "1",
195 "name": "housing_support",
196 "title": "Housing Support",
197 "description": "Help homeless individuals obtain temporary and long-term housing",
198 "is_active": "1",
199 "is_reserved": "0",
200 "weight": "1",
201 "is_forkable": "1",
202 "is_forked": "",
203 "definition": {
204 "activityTypes": [
205 {"name": "Open Case", "max_instances": "1"}
206 ],
207 "activitySets": [
208 {
c866eb5f 209 "name": "standard_timeline",
416abe87
PH
210 "label": "Standard Timeline",
211 "timeline": "1",
212 "activityTypes": [
213 {
214 "name": "Open Case",
215 "status": "Completed"
216 },
217 {
218 "name": "Medical evaluation",
219 "reference_activity": "Open Case",
220 "reference_offset": "1",
221 "reference_select": "newest"
222 }
223 ]
224 }
225 ],
226 "caseRoles": [
227 {
228 "name": "Homeless Services Coordinator",
229 "creator": "1",
230 "manager": "1"
231 }
232 ]
233 }
234 }
235 };
b109dd8b 236 scope = $rootScope.$new();
416abe87 237 ctrl = $controller('CaseTypeCtrl', {$scope: scope, apiCalls: apiCalls});
b109dd8b 238 });
416abe87
PH
239
240 it('should load activity statuses', function() {
4324b8d7 241 expect(scope.activityStatuses).toEqualData(apiCalls.actStatuses.values);
416abe87
PH
242 });
243
244 it('should load activity types', function() {
4324b8d7 245 expect(scope.activityTypes['ADC referral']).toEqualData(apiCalls.actTypes.values[0]);
416abe87
PH
246 });
247
248 it('addActivitySet should add an activitySet to the case type', function() {
249 scope.addActivitySet('timeline');
250 var activitySets = scope.caseType.definition.activitySets;
251 var newSet = activitySets[activitySets.length - 1];
252 expect(newSet.name).toBe('timeline_1');
253 expect(newSet.timeline).toBe('1');
254 expect(newSet.label).toBe('Timeline');
255 });
256
257 it('addActivitySet handles second timeline correctly', function() {
258 scope.addActivitySet('timeline');
259 scope.addActivitySet('timeline');
260 var activitySets = scope.caseType.definition.activitySets;
261 var newSet = activitySets[activitySets.length - 1];
262 expect(newSet.name).toBe('timeline_2');
263 expect(newSet.timeline).toBe('1');
264 expect(newSet.label).toBe('Timeline #2');
265 });
c0bb8bd4
DB
266 });
267
268 describe('crmAddName', function () {
c0bb8bd4
DB
269 var element;
270
b109dd8b 271 beforeEach(function() {
c0bb8bd4
DB
272 scope = $rootScope.$new();
273 scope.activityTypeOptions = [1, 2, 3];
274 element = '<span crm-add-name crm-options="activityTypeOptions"></span>';
275
276 spyOn(CRM.$.fn, 'crmSelect2').and.callThrough();
416abe87 277
c0bb8bd4
DB
278 element = $compile(element)(scope);
279 scope.$digest();
b109dd8b 280 });
c0bb8bd4
DB
281
282 describe('when initialized', function () {
283 var returnValue;
284
285 beforeEach (function () {
286 var dataFunction = CRM.$.fn.crmSelect2.calls.argsFor(0)[0].data;
287 returnValue = dataFunction();
288 });
289
290 it('updates the UI with updated value of scope variable', function () {
291 expect(returnValue).toEqual({ results: scope.activityTypeOptions });
292 });
293 });
416abe87 294 });
d9aaf033 295
d7a470db
DB
296 describe('crmEditableTabTitle', function () {
297 var element, titleLabel, penIcon, saveButton, cancelButton;
298
299 beforeEach(function() {
300 scope = $rootScope.$new();
301 element = '<div crm-editable-tab-title title="Click to edit">' +
7c369cd7 302 '<span>{{ activitySet.label }}</span>' +
d7a470db
DB
303 '</div>';
304
305 scope.activitySet = { label: 'Title'};
306 element = $compile(element)(scope);
307
308 titleLabel = $(element).find('span');
309 penIcon = $(element).find('i.fa-pencil');
310 saveButton = $(element).find('button[type=button]');
311 cancelButton = $(element).find('button[type=cancel]');
312
313 scope.$digest();
314 });
315
316 describe('when initialized', function () {
317 it('hides the save and cancel button', function () {
318 expect(saveButton.parent().css('display') === 'none').toBe(true);
319 expect(cancelButton.parent().css('display') === 'none').toBe(true);
320 });
321 });
322
323 describe('when clicked on title label', function () {
324 beforeEach(function () {
325 titleLabel.click();
326 });
327
328 it('hides the pen icon', function () {
329 expect(penIcon.css('display') === 'none').toBe(true);
330 });
331
332 it('shows the save button', function () {
333 expect(saveButton.parent().css('display') !== 'none').toBe(true);
334 });
335
336 it('makes the title editable', function () {
337 expect(titleLabel.attr('contenteditable')).toBe('true');
338 });
339 });
340
341 describe('when clicked outside of the editable area', function () {
342 beforeEach(function () {
343 titleLabel.click();
344 titleLabel.text('Updated Title');
345 titleLabel.blur();
346 $timeout.flush();
347 scope.$digest();
348 });
349
350 it('shows the pen icon', function () {
351 expect(penIcon.css('display') !== 'none').toBe(true);
352 });
353
354 it('hides the save and cancel button', function () {
355 expect(saveButton.parent().css('display') === 'none').toBe(true);
356 expect(cancelButton.parent().css('display') === 'none').toBe(true);
357 });
358
359 it('makes the title non editable', function () {
360 expect(titleLabel.attr('contenteditable')).not.toBe('true');
361 });
362
363 it('does not update the title in angular context', function () {
364 expect(scope.activitySet.label).toBe('Title');
365 });
366 });
367
368 describe('when ESCAPE key is pressed while typing', function () {
369 beforeEach(function () {
370 var eventObj = $.Event('keydown');
371 eventObj.key = 'Escape';
372
373 titleLabel.click();
374 titleLabel.text('Updated Title');
375 titleLabel.trigger(eventObj);
376 scope.$digest();
377 });
378
379 it('shows the pen icon', function () {
380 expect(penIcon.css('display') !== 'none').toBe(true);
381 });
382
383 it('hides the save and cancel button', function () {
384 expect(saveButton.parent().css('display') === 'none').toBe(true);
385 expect(cancelButton.parent().css('display') === 'none').toBe(true);
386 });
387
388 it('makes the title non editable', function () {
389 expect(titleLabel.attr('contenteditable')).not.toBe('true');
390 });
391
392 it('does not update the title', function () {
393 expect(scope.activitySet.label).toBe('Title');
394 });
395 });
396
397 describe('when ENTER key is pressed while typing', function () {
398 beforeEach(function () {
399 var eventObj = $.Event('keydown');
400 eventObj.key = 'Enter';
401
402 titleLabel.click();
403 titleLabel.text('Updated Title');
404 titleLabel.trigger(eventObj);
405 scope.$digest();
406 });
407
408 it('shows the pen icon', function () {
409 expect(penIcon.css('display') !== 'none').toBe(true);
410 });
411
412 it('hides the save and cancel button', function () {
413 expect(saveButton.parent().css('display') === 'none').toBe(true);
414 expect(cancelButton.parent().css('display') === 'none').toBe(true);
415 });
416
417 it('makes the title non editable', function () {
418 expect(titleLabel.attr('contenteditable')).not.toBe('true');
419 });
420
421 it('updates the title in angular context', function () {
422 expect(scope.activitySet.label).toBe('Updated Title');
423 });
424 });
425
426 describe('when SAVE button is clicked', function () {
427 beforeEach(function () {
428 titleLabel.click();
429 titleLabel.text('Updated Title');
430 saveButton.click();
431 scope.$digest();
432 });
433
434 it('shows the pen icon', function () {
435 expect(penIcon.css('display') !== 'none').toBe(true);
436 });
437
438 it('hides the save and cancel button', function () {
439 expect(saveButton.parent().css('display') === 'none').toBe(true);
440 expect(cancelButton.parent().css('display') === 'none').toBe(true);
441 });
442
443 it('makes the title non editable', function () {
444 expect(titleLabel.attr('contenteditable')).not.toBe('true');
445 });
446
447 it('updates the title in angular context', function () {
448 expect(scope.activitySet.label).toBe('Updated Title');
449 });
450 });
451
452 describe('when CANCEL button is clicked', function () {
453 beforeEach(function () {
454 titleLabel.click();
455 titleLabel.text('Updated Title');
456 cancelButton.click();
457 scope.$digest();
458 });
459
460 it('shows the pen icon', function () {
461 expect(penIcon.css('display') !== 'none').toBe(true);
462 });
463
464 it('hides the save and cancel button', function () {
465 expect(saveButton.parent().css('display') === 'none').toBe(true);
466 expect(cancelButton.parent().css('display') === 'none').toBe(true);
467 });
468
469 it('makes the title non editable', function () {
470 expect(titleLabel.attr('contenteditable')).not.toBe('true');
471 });
472
473 it('does not update the title in angular context', function () {
474 expect(scope.activitySet.label).toBe('Title');
475 });
476 });
477 });
478
c9817fac
RO
479 describe('CaseTypeListCtrl', function() {
480 var caseTypes, crmApiSpy;
d9aaf033 481
c9817fac 482 beforeEach(function() {
d9aaf033
RO
483 caseTypes = {
484 values: {
485 1: { id: 1 },
486 2: { id: 2 },
487 3: { id: 3 }
488 }
489 };
c9817fac 490 crmApiSpy = jasmine.createSpy('crmApi').and.returnValue($q.resolve());
d9aaf033
RO
491 scope = $rootScope.$new();
492 ctrl = $controller('CaseTypeListCtrl', {
493 $scope: scope,
c9817fac
RO
494 caseTypes: caseTypes,
495 crmApi: crmApiSpy
d9aaf033
RO
496 });
497 });
498
499 it('should store an index of case types', function() {
500 expect(scope.caseTypes).toEqual(caseTypes.values);
501 });
c9817fac
RO
502
503 describe('toggleCaseType', function() {
504 var caseType = { id: _.uniqueId() };
505
506 describe('when the case is active', function() {
507 beforeEach(function() {
508 caseType.is_active = '1';
509
510 scope.toggleCaseType(caseType);
511 });
512
513 it('sets the case type as inactive', function() {
514 expect(crmApiSpy).toHaveBeenCalledWith('CaseType', 'create', jasmine.objectContaining({
515 id: caseType.id,
516 is_active: '0'
517 }), true);
518 });
519 });
520
521 describe('when the case is inactive', function() {
522 beforeEach(function() {
523 caseType.is_active = '0';
524
525 scope.toggleCaseType(caseType);
526 });
527
528 it('sets the case type as active', function() {
529 expect(crmApiSpy).toHaveBeenCalledWith('CaseType', 'create', jasmine.objectContaining({
530 id: caseType.id,
531 is_active: '1'
532 }), true);
533 });
534 });
535 });
2e01cc82
RO
536
537 describe('deleteCaseType', function() {
538 var caseType = { id: _.uniqueId() };
539
540 beforeEach(function() {
541 crmApiSpy.and.returnValue($q.resolve(caseType));
542 scope.caseTypes[caseType.id] = caseType;
543
544 scope.deleteCaseType(caseType);
545 scope.$digest();
546 });
547
548 describe('when the case type can be deleted', function() {
549 it('deletes the case from the api', function() {
550 expect(crmApiSpy).toHaveBeenCalledWith('CaseType', 'delete', { id: caseType.id }, jasmine.any(Object));
551 });
552
553 it('removes the case type from the list', function() {
554 expect(scope.caseTypes[caseType.id]).toBeUndefined();
555 });
556 });
557
558 describe('when the case type cannot be delted', function() {
559 var error = { error_message: 'Error Message' };
560
561 beforeEach(function() {
562 var errorHandler;
563
564 crmApiSpy.and.returnValue($q.reject(error));
565 scope.caseTypes[caseType.id] = caseType;
566
567 spyOn(CRM, 'alert');
568 scope.deleteCaseType(caseType);
569 scope.$digest();
570
571 errorHandler = crmApiSpy.calls.mostRecent().args[3].error;
572 errorHandler(error);
573 });
574
575 it('displays the error message', function() {
384f015c 576 expect(CRM.alert).toHaveBeenCalledWith(error.error_message, 'Error', 'error');
2e01cc82
RO
577 });
578 });
b4e42693
RO
579
580 describe('revertCaseType', function() {
581 var caseType = {
582 id: _.uniqueId(),
583 definition: {},
584 is_forked: '1'
585 };
586
587 describe('when reverting a case type', function() {
588 beforeEach(function() {
589 scope.revertCaseType(caseType);
590 });
591
592 it('resets the case type information using the api', function() {
593 expect(crmApiSpy).toHaveBeenCalledWith('CaseType', 'create', jasmine.objectContaining({
594 id: caseType.id,
595 definition: 'null',
596 is_forked: '0'
597 }), true);
598 });
599 });
600 });
2e01cc82 601 });
d9aaf033 602 });
416abe87 603});