Passed
Push — master ( cddcf6...1d3855 )
by Alexander
01:59
created

tcms/static/js/testrun_actions.js (3 issues)

1
Nitrate.TestRuns = {};
2
Nitrate.TestRuns.Details = {};
3
Nitrate.TestRuns.Execute = {}
4
Nitrate.TestRuns.AssignCase = {}
5
6
7
Nitrate.TestRuns.Details.on_load = function() {
8
  setAddTagAutocomplete();
9
10
  // Observe the interface buttons
11
  jQ('#id_check_all_button').bind('click', function(e) {
12
    toggleAllCheckBoxes(this, 'id_table_cases', 'case_run');
13
  });
14
15
  if (jQ('#id_check_box_highlight').attr('checked')) {
16
    jQ('.mine').addClass('highlight');
17
  }
18
19
  jQ('#id_check_box_highlight').bind('click', function(e) {
20
    e = jQ('.mine');
21
    this.checked && e.addClass('highlight') || e.removeClass('highlight');
22
  });
23
24
  jQ('#id_blind_all_link').bind('click', function(e) {
25
    if (!jQ('td[id^="id_loading_"]').length) {
26
      jQ(this).removeClass('locked');
27
    }
28
    if (jQ(this).is('.locked')) {
29
      //To disable the 'expand all' until all case runs are expanded.
30
      return false;
31
    } else {
32
      jQ(this).addClass('locked');
33
      var element = jQ(this).children();
34
      if (element.is('.collapse-all')) {
35
        this.title = "Collapse all cases";
36
        blinddownAllCases(element[0]);
37
      } else {
38
        this.title = "Expand all cases";
39
        blindupAllCases(element[0]);
40
      }
41
    }
42
  });
43
44
  // Observe the case run toggle and the comment form
45
  var toggle_case_run = function(e) {
46
    var c = jQ(this).parent(); // Container
47
    var c_container = c.next(); // Content Containers
48
    var case_id = c.find('input[name="case"]')[0].value;
49
    var case_run_id = c.find('input[name="case_run"]')[0].value;
50
    var case_text_version = c.find('input[name="case_text_version"]')[0].value;
51
    var type = 'case_run';
52
    var callback = function(t) {
53
      // Observe the update case run stauts/comment form
54
      c_container.parent().find('.update_form')
55
        .unbind('submit').bind('submit', updateCaseRunStatus);
56
57
      // Observe the delete comment form
58
      var refresh_case = function(t) {
59
        constructCaseRunZone(c_container[0], c[0], case_id);
60
      };
61
62
      var rc_callback = function(e) {
63
        e.stopPropagation();
64
        e.preventDefault();
65
        if (!window.confirm(default_messages.confirm.remove_comment)) {
66
          return false;
67
        }
68
        removeComment(this, refresh_case);
69
      };
70
      c_container.parent().find('.form_comment')
71
        .unbind('submit').bind('submit', rc_callback);
72
      c_container.find('.js-status-button').bind('click', function() {
73
        this.form.value.value = jQ(this).data('formvalue');
74
      });
75
      c_container.find('.js-show-comments').bind('click', function() {
76
        toggleDiv(this, jQ(this).data('param'));
77
      });
78
      c_container.find('.js-show-changelog').bind('click', function() {
79
        toggleDiv(this, jQ(this).data('param'));
80
      });
81
      c_container.find('.js-file-caserun-bug').bind('click', function(){
82
        var params = jQ(this).data('params');
83
        fileCaseRunBug(params[0], c[0], c_container[0], params[1], params[2]);
84
      });
85
      c_container.find('.js-add-caserun-bug').bind('click', function(){
86
        var params = jQ(this).data('params');
87
        addCaseRunBug(params[0], c[0], c_container[0], params[1], params[2]);
88
      });
89
      c_container.find('.js-remove-caserun-bug').bind('click', function(){
90
        var params = jQ(this).data('params');
91
        removeCaseBug(params[1], params[2], params[3]);
92
      });
93
      c_container.find('.js-add-testlog').bind('click', function(){
94
        var params = jQ(this).data('params');
95
        addLinkToCaseRun(this, params[0], params[1]);
96
      });
97
      c_container.find('.js-remove-testlog').bind('click', function(){
98
        removeLink(this, window.parseInt(jQ(this).data('param')));
99
      });
100
    };
101
102
    toggleTestCaseRunPane({
103
      'callback': callback,
104
      'caseId': case_id,
105
      'caserunId': case_run_id,
106
      'caseTextVersion': case_text_version,
107
      'caserunRowContainer': c,
108
      'expandPaneContainer': c_container
109
    });
110
  };
111
  jQ('.expandable').bind('click', toggle_case_run);
112
113
  // Auto show the case run contents.
114
  if (window.location.hash != '') {
115
    fireEvent(jQ('a[href=\"' + window.location.hash + '\"]')[0], 'click');
116
  };
117
118
  // Filter Case-Run
119
  if (jQ('#filter_case_run').length) {
120
    jQ('#filter_case_run').bind('click',function(e){
121
      if (jQ('#id_filter').is(':hidden')){
122
        jQ('#id_filter').show();
123
        jQ(this).html(default_messages.link.hide_filter);
124
      } else {
125
        jQ('#id_filter').hide();
126
        jQ(this).html(default_messages.link.show_filter);
127
      }
128
    });
129
  }
130
  //bind click to status btn
131
  jQ('.btn_status').live('click', function() {
132
    var from = jQ(this).siblings('.btn_status:disabled')[0].title;
133
    var to = this.title;
134
    if (jQ('span#' + to + ' a').text() === '0') {
135
      var htmlstr = "[<a href='javascript:void(0)' onclick=\"showCaseRunsWithSelectedStatus(jQ('#id_filter')[0], '"
136
        + jQ(this).attr('crs_id')+"')\">0</a>]";
137
      jQ('span#' + to).html(htmlstr);
138
    }
139
    if (jQ('span#' + from + ' a').text() === '1') {
140
      jQ('span#' + from).html("[<a>1</a>]");
141
    }
142
    jQ('span#' + to + ' a').text(window.parseInt(jQ('span#' + to + ' a').text()) + 1);
143
    jQ('span#' + from + ' a').text(window.parseInt(jQ('span#' + from + ' a').text()) - 1);
144
145
    var caseRunCount = window.parseInt(jQ('span#TOTAL').next().text()) || 0;
146
    var passedCaseRunCount = window.parseInt(jQ('span#PASSED a').text()) || 0;
147
    var errorCaseRunCount = window.parseInt(jQ('span#ERROR a').text()) || 0;
148
    var failedCaseRunCount = window.parseInt(jQ('span#FAILED a').text()) || 0;
149
    var waivedCaseRunCount = window.parseInt(jQ('span#WAIVED a').text()) || 0;
150
    var completePercent = 100 * ((passedCaseRunCount + errorCaseRunCount + failedCaseRunCount
151
      + waivedCaseRunCount) / caseRunCount).toFixed(2);
152
    var failedPercent = 100 * ((errorCaseRunCount + failedCaseRunCount) / (passedCaseRunCount
153
      + errorCaseRunCount + failedCaseRunCount + waivedCaseRunCount)).toFixed(2);
154
155
    jQ('span#complete_percent').text(completePercent);
156
    jQ('div.progress-inner').attr('style', 'width:' + completePercent + '%');
157
    jQ('div.progress-failed').attr('style', 'width:' + failedPercent + '%');
158
  });
159
160
  jQ('#btn_edit').bind('click', function() {
161
    var params = jQ(this).data('params');
162
    window.location.href = params[0] + '?from_plan=' + params[1];
163
  });
164
  jQ('#btn_clone').bind('click', function() {
165
    postToURL(jQ(this).data('param'), serializeCaseRunFromInputList('id_table_cases','case_run'));
166
  });
167
  jQ('#btn_delete').bind('click', function() {
168
    window.location.href = jQ(this).data('param');
169
  });
170
171
  bindJSRemoveTagButton();
172
  jQ('.js-add-tag').bind('click', function() {
173
    addRunTag(jQ('.js-tag-ul')[0], jQ(this).data('param'));
174
  });
175
  jQ('.js-set-running').bind('click', function() {
176
    window.location.href = jQ(this).data('param') + '?finished=0';
177
  });
178
  jQ('.js-set-finished').bind('click', function() {
179
    window.location.href = jQ(this).data('param') + '?finished=1';
180
  });
181
  jQ('.js-del-case').bind('click', function() {
182
    delCaseRun(jQ(this).data('param'));
183
  });
184
  jQ('.js-update-case').bind('click', function() {
185
    postToURL(jQ(this).data('param'), serializeCaseRunFromInputList('id_table_cases', 'case_run'));
186
  });
187
  jQ('.js-change-assignee').bind('click', function() {
188
    changeCaseRunAssignee();
189
  });
190
  jQ('.js-add-bugs').bind('click', function() {
191
    updateBugs('add');
192
  });
193
  jQ('.js-remove-bugs').bind('click', function() {
194
    updateBugs('remove');
195
  });
196
  jQ('.js-show-commentdialog').bind('click', function() {
197
    showCommentForm();
198
  });
199
  jQ('.js-add-cc').bind('click', function() {
200
    addRunCC(jQ(this).data('param'), jQ('.js-cc-ul')[0]);
201
  });
202
  jQ('.js-remove-cc').bind('click', function() {
203
    var params = jQ(this).data('params');
204
    removeRunCC(params[0], params[1], jQ('.js-cc-ul')[0]);
205
  });
206
  jQ('.js-caserun-total').bind('click', function() {
207
    showCaseRunsWithSelectedStatus(jQ('#id_filter')[0], '');
208
  });
209
  jQ('.js-status-subtotal').bind('click', function() {
210
    showCaseRunsWithSelectedStatus(jQ('#id_filter')[0], jQ(this).data('param'));
211
  });
212
};
213
214
Nitrate.TestRuns.AssignCase.on_load = function() {
215
  if (jQ('#id_check_all_button').length) {
216
    jQ('#id_check_all_button').bind('click', function(m) {
217
      toggleAllCheckBoxes(this, 'id_table_cases', 'case');
218
    });
219
  }
220
221
  jQ('input[name="case"]').bind('click', function(t) {
222
    if (this.checked) {
223
      jQ(this).closest('tr').addClass('selection_row');
224
      jQ(this).parent().siblings().eq(7).html('<div class="apply_icon"></div>');
225
    } else {
226
      jQ(this).closest('tr').removeClass('selection_row');
227
      jQ(this).parent().siblings().eq(7).html('');
228
    }
229
  });
230
231
  jQ('.js-how-assign-case').bind('click', function() {
232
    jQ('#help_assign').show();
233
  });
234
  jQ('.js-close-how-assign').bind('click', function() {
235
    jQ('#help_assign').hide();
236
  });
237
  jQ('.js-toggle-button, .js-case-summary').bind('click', function() {
238
    toggleTestCaseContents(jQ(this).data('param'));
239
  });
240
};
241
242
243
function updateRunStatus(object_pk, value, callback) {
244
  jQ.ajax({
245
    'url': '/runs/case-run-update-status/',
246
    'type': 'POST',
247
    'data': {'object_pk': object_pk, 'status_id': value },
248
    'success': function (data, textStatus, jqXHR) {
249
      callback();
250
    },
251
    'error': function (jqXHR, textStatus, errorThrown) {
252
      json_failure(jqXHR);
253
    }
254
  });
255
}
256
257
var updateCaseRunStatus = function(e) {
258
  e.stopPropagation();
259
  e.preventDefault();
260
  var container = jQ(this).parents().eq(3);
261
  var parent = container.parent();
262
  var title = parent.prev();
263
  var link = title.find('.expandable')[0];
264
  var parameters = Nitrate.Utils.formSerialize(this);
265
  var object_pk = parameters['object_pk'];
266
  var value = parameters['value'];
267
268
  // Callback when
269
  var callback = function(t) {
270
    // Update the contents
271
    if (parameters['value'] != '') {
272
      // Update the case run status icon
273
      var crs = Nitrate.TestRuns.CaseRunStatus;
274
      title.find('.icon_status').each(function(index) {
275
        for (i in crs) {
276
          if (typeof crs[i] === 'string' && jQ(this).is('.btn_' + crs[i])) {
277
            jQ(this).removeClass('btn_' + crs[i]);
278
          }
279
        }
280
        jQ(this).addClass('btn_' + Nitrate.TestRuns.CaseRunStatus[value - 1]);
281
      });
282
283
      // Update related people
284
      var usr = Nitrate.User;
285
      title.find('.link_tested_by').each(function(i) {
286
        jQ(this).html(usr.username);
287
      });
288
    }
289
290
    // Mark the case run to mine
291
    if (!title.is('.mine')) {
292
      title.addClass('mine');
293
    }
294
295
    // Blind down next case
296
    fireEvent(link, 'click');
297
    if (jQ('#id_check_box_auto_blinddown').attr('checked') && parameters['value'] != '') {
298
      var next_title = parent.next();
299
      if (!next_title.length) {
300
        return false;
301
      }
302
      if (next_title.next().is(':hidden')) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if next_title.next().is(":hidden") is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
303
        fireEvent(next_title.find('.expandable')[0], 'click');
304
      }
305
    } else {
306
      fireEvent(link, 'click');
307
    }
308
  };
309
310
  // Add comment
311
  if (parameters['comment'] != '') {
312
    // Reset the content to loading
313
    var ajax_loading = getAjaxLoading();
314
    ajax_loading.id = 'id_loading_' + parameters['case_id'];
315
    container.html(ajax_loading);
316
    var c = jQ('<div>');
317
    if (parameters['value'] != '') {
318
      submitComment(c[0], parameters);
319
    } else {
320
      submitComment(c[0], parameters, callback);
321
    }
322
  }
323
324
  // Update the object when changing the status
325
  if (parameters['value'] != '') {
326
    // Reset the content to loading
327
    var ajax_loading = getAjaxLoading();
328
    ajax_loading.id = 'id_loading_' + parameters['case_id'];
329
    container.html(ajax_loading);
330
    updateRunStatus([object_pk], value, callback);
331
  }
332
};
333
334
335
function constructCaseRunZone(container, title_container, case_id) {
336
  var link = jQ(title_container).find('.expandable')[0];
337
  if (container) {
338
    var td = jQ('<td>', {'id': 'id_loading_' + case_id, 'colspan': 12 });
339
    td.html(getAjaxLoading());
340
    jQ(container).html(td);
341
  }
342
343
  if (title_container) {
344
    fireEvent(link, 'click');
345
    fireEvent(link, 'click');
346
  }
347
}
348
349
350
//////////////////////////////////////////////////////////////////////////////////////////////
351
////////   AddIssueDialog Definition //////////////
352
353
354
/*
355
 * Dialog to allow user to add sort of issue keys to case run(s).
356
 *
357
 * Here, issue key is a general to whatever the issue tracker system is.
358
 *
359
 * options:
360
 * @param extraFormHiddenData: used for providing extra data for specific AJAX request.
361
 * @param onSubmit: callback function when user click Add button
362
 * @param action: string - Add or Remove. Default is Add
363
 */
364
/*
365
 * FIXME: which namespace is proper to hold this dialog class?
366
 */
367
function AddIssueDialog(options) {
368
  this.onSubmit = options.onSubmit;
369
  if (this.onSubmit !== undefined && typeof this.onSubmit !== "function") {
370
    throw new Error("onSubmit should be a function object.");
371
  }
372
  this.extraFormHiddenData = options.extraFormHiddenData;
373
  if (this.extraFormHiddenData !== undefined && typeof this.extraFormHiddenData !== "object") {
374
    throw new Error("extraFormHiddenData sould be an object if present.");
375
  }
376
377
  this.action = options.action;
378
  if (this.action === undefined) {
379
    this.action = "Add";
380
  }
381
382
  this.a = options.a;
383
  if (this.a === undefined) {
384
    this.a = this.action.toLowerCase();
385
  }
386
387
  if (options.hasOwnProperty("show_bug_id_field")) {
388
    this.show_bug_id_field = options.show_bug_id_field;
389
  } else {
390
    this.show_bug_id_field = false;
391
  }
392
}
393
394
395
AddIssueDialog.prototype.show = function () {
396
  var dialog = jQ("#dialog")[0];
397
  if (dialog == null) {
398
    throw new Error("No HTML element with ID dialog. This should not happen in the runtime.");
399
  }
400
401
  var hiddenPart = [];
402
  if (this.extraFormHiddenData !== undefined) {
403
    for (var name_attr in this.extraFormHiddenData) {
0 ignored issues
show
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
404
      hiddenPart.push({'name': name_attr, 'value': this.extraFormHiddenData[name_attr]});
405
    }
406
  }
407
408
  var template = Handlebars.compile(jQ("#add_issue_form_template").html());
409
  var context = {
410
    'hiddenFields': hiddenPart,
411
    'action_button_text': this.action,
412
    'show_bug_id_field': this.show_bug_id_field || this.action === 'Add',
413
    'show_add_to_bugzilla_checkbox': this.action === 'Add',
414
    'a': this.a,
415
  };
416
417
  jQ('#dialog').html(template(context))
418
    .find('.js-cancel-button').bind('click', function() {
419
      jQ('#dialog').hide();
420
    })
421
    .end().show();
422
423
  this.form = jQ("#add_issue_form")[0];
424
425
  // Used for following event callbacks to ref this dialog's instance
426
  var that = this;
427
428
  // Set custom callback functions
429
  if (this.onSubmit !== undefined) {
430
    jQ(this.form).bind('submit', function (form_event) {
431
      that.onSubmit(form_event, that);
432
    });
433
  }
434
};
435
436
AddIssueDialog.prototype.get_data = function () {
437
  var form_data = Nitrate.Utils.formSerialize(this.form);
438
  form_data.bug_validation_regexp = $('#bug_system_id option:selected').data('validation-regexp');
439
  form_data.bz_external_track = $('input[name=bz_external_track]').is(':checked');
440
  return form_data;
441
};
442
443
//// end of AddIssueDialog definition /////////
444
/////////////////////////////////////////////////////////////////////////////////////////////
445
446
function fileCaseRunBug(run_id, title_container, container, case_id, case_run_id) {
447
  var dialog = new AddIssueDialog({
448
    'action': 'Report',
449
    'onSubmit': function (e, dialog) {
450
      e.stopPropagation();
451
      e.preventDefault();
452
453
        var tracker_id = dialog.get_data()['bug_system_id'];
454
        jsonRPC('Bug.report', [case_run_id, tracker_id], function(result) {
455
            $('#dialog').hide();
456
457
            if (result.rc === 0) {
458
                window.open(result.response, '_blank');
459
            } else {
460
                window.alert(result.response);
461
            }
462
      });
463
    }
464
  });
465
466
  dialog.show();
467
}
468
469
function addCaseRunBug(run_id, title_container, container, case_id, case_run_id) {
470
  var dialog = new AddIssueDialog({
471
    'onSubmit': function (e, dialog) {
472
      e.stopPropagation();
473
      e.preventDefault();
474
475
      form_data = dialog.get_data();
476
477
      form_data.bug_id = form_data.bug_id.trim();
478
      if (!form_data.bug_id.length) {
479
        return;
480
      }
481
482
        jsonRPC('Bug.create', [{
483
                case_id: case_id,
484
                case_run_id: case_run_id,
485
                bug_id: form_data.bug_id,
486
                bug_system_id: form_data.bug_system_id
487
            }, form_data.bz_external_track],
488
            function(result) {
489
                // todo: missing error handling when bz_external_track is true
490
                $('#dialog').hide();
491
492
                // Update bugs count associated with just updated case run
493
                var jqCaserunBugCount = $('span#' + case_run_id + '_case_bug_count');
494
                jqCaserunBugCount.addClass('have_bug');
495
496
                // refresh the links of bugs
497
                constructCaseRunZone(container, title_container, case_id);
498
        });
499
    }
500
  });
501
502
  dialog.show();
503
}
504
505
506
function delCaseRun(run_id) {
507
  var caseruns = serializeCaseRunFromInputList('id_table_cases', 'case_run');
508
  var numCaseRuns = caseruns.case_run.length;
509
  if (window.confirm('You are about to delete ' + numCaseRuns + ' case run(s). Are you sure?')) {
510
    postToURL('remove_execution/', caseruns);
511
  }
512
}
513
514
515
// binds the remove buttons for all tags
516
function bindJSRemoveTagButton() {
517
  $('.js-remove-tag').bind('click', function() {
518
    var params = $(this).data('params');
519
    removeRunTag($('.js-tag-ul')[0], params[0], params[1]);
520
  });
521
}
522
523
524
// data is an array of id/name for tags
525
function updateTagContainer(container, data, run_id) {
526
    var html = '';
527
528
    data.forEach(function(element) {
529
        var li = '<li>' + element['name'] +
530
                    '<a href="#" class="js-remove-tag" data-params=\'["'+ run_id + '", "' + element['name'] + '"]\'>' +
531
                        '&nbsp;<i class="fa fa-trash-o"></i>' +
532
                    '</a>' +
533
                 '</li>';
534
        html += li;
535
    });
536
537
    $(container).html(html);
538
    bindJSRemoveTagButton();
539
}
540
541
542
function addRunTag(container, run_id) {
543
    var tag_container = $('#id_tags');
544
    var tag = tag_container.val();
545
546
    if (!tag) {
547
        return false;
548
    }
549
550
    var inner_callback = function(data) {
551
        updateTagContainer(container, data, run_id);
552
        tag_container.val('');
553
    }
554
    jsonRPC('TestRun.add_tag', [run_id, tag], inner_callback);
555
}
556
557
function removeRunTag(container, run_id, tag) {
558
    var inner_callback = function(data) {
559
        updateTagContainer(container, data, run_id);
560
    }
561
    jsonRPC('TestRun.remove_tag', [run_id, tag], inner_callback);
562
}
563
564
function constructRunCC(container, run_id, parameters) {
565
  var complete = function(t) {
566
    jQ('.js-remove-cc').bind('click', function() {
567
      var params = jQ(this).data('params');
568
      removeRunCC(params[0], params[1], jQ('.js-cc-ul')[0]);
569
    });
570
    if (jQ('#message').length) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if jQ("#message").length is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
571
      window.alert(jQ('#message').html());
572
      return false;
573
    }
574
  };
575
  var url = '/runs/' + run_id + '/cc/';
576
  jQ.ajax({
577
    'url': url,
578
    'type': 'GET',
579
    'data': parameters,
580
    'success': function (data, textStatus, jqXHR) {
581
      jQ(container).html(data);
582
    },
583
    'complete': function() {
584
      complete();
585
    }
586
  });
587
}
588
589
function addRunCC(run_id, container) {
590
  var user = window.prompt('Please type new email or username for CC.');
591
  if (!user) {
592
    return false;
593
  }
594
  var parameters = {'do': 'add', 'user': user};
595
  constructRunCC(container, run_id, parameters);
596
}
597
598
function removeRunCC(run_id, user, container) {
599
  var c = window.confirm('Are you sure to delete this user from CC?');
600
601
  if (!c) {
602
    return false;
603
  }
604
605
  var parameters = { 'do': 'remove', 'user': user };
606
  constructRunCC(container, run_id, parameters);
607
}
608
609
function changeCaseRunAssignee() {
610
  var runs = serializeCaseRunFromInputList(jQ('#id_table_cases')[0]);
611
  if (!runs.length) {
612
    window.alert(default_messages.alert.no_case_selected);
613
    return false;
614
  }
615
616
  var p = window.prompt('Please type new email or username for assignee');
617
  if (!p) {
618
    return false;
619
  }
620
621
  jQ.ajax({
622
    'url': '/runs/update-assignee/',
623
    'type': 'POST',
624
    'data': { ids: runs, assignee: p },
625
    'success': function (data, textStatus, jqXHR) {
626
      window.location.reload();
627
    },
628
    'error': function (jqXHR, textStatus, errorThrown) {
629
      json_failure(jqXHR);
630
    }
631
  });
632
}
633
634
function serializeCaseRunFromInputList(table, name) {
635
  var elements;
636
  if (typeof table === 'string') {
637
    elements = jQ('#' + table).parent().find('input[name="case_run"]:checked');
638
  } else {
639
    elements = jQ(table).parent().find('input[name="case_run"]:checked');
640
  }
641
642
  var returnobj_list = [];
643
  elements.each(function(i) {
644
    if (typeof this.value === 'string') {
645
      returnobj_list.push(this.value);
646
    }
647
  });
648
  if (name) {
649
    var returnobj = {};
650
    returnobj[name] = returnobj_list;
651
    return returnobj;
652
  }
653
654
  return returnobj_list;
655
}
656
657
function serialzeCaseForm(form, table, serialized) {
658
  if (typeof serialized !== 'boolean') {
659
    var serialized = true;
660
  }
661
  var data;
662
  if (serialized) {
663
    data = Nitrate.Utils.formSerialize(form);
664
  } else {
665
    data = jQ(form).serialize();
666
  }
667
668
  data['case_run'] = serializeCaseFromInputList(table);
669
  return data;
670
}
671
672
function showCaseRunsWithSelectedStatus(form, status_id) {
673
  form.status__pk.value = status_id;
674
  fireEvent(jQ(form).find('input[type="submit"]')[0], 'click');
675
}
676
677
function updateBugsActionAdd(case_runs) {
678
  var dialog = new AddIssueDialog({
679
    'extraFormHiddenData': { 'case_runs': case_runs.join() },
680
    'onSubmit': function(e, dialog) {
681
      e.stopPropagation();
682
      e.preventDefault();
683
      var form_data = dialog.get_data();
684
      form_data.bug_id = form_data.bug_id.trim();
685
686
      if (!form_data.bug_id.length) {
687
        return;
688
      }
689
690
      if (!validateIssueID(form_data.bug_validation_regexp, form_data.bug_id)) {
691
        return false;
692
      }
693
694
      jQ.ajax({
695
        url: '/caserun/update-bugs-for-many/',
696
        dataType: 'json',
697
        data: form_data,
698
        success: function(res){
699
          if (res.rc === 0) {
700
            reloadWindow();
701
          } else {
702
            window.alert(res.response);
703
            return false;
704
          }
705
        }
706
      });
707
    }
708
  });
709
  dialog.show();
710
}
711
712
function updateBugsActionRemove(case_runs) {
713
  var dialog = new AddIssueDialog({
714
    'action': 'Remove',
715
    'show_bug_id_field': true,
716
    'extraFormHiddenData': { 'case_runs': case_runs.join() },
717
    'onSubmit': function(e, dialog) {
718
      e.stopPropagation();
719
      e.preventDefault();
720
      var form_data = dialog.get_data();
721
      form_data.bug_id = form_data.bug_id.trim();
722
723
      if (!form_data.bug_id.length) {
724
        return;
725
      }
726
727
      if (!validateIssueID(form_data.bug_validation_regexp, form_data.bug_id)) {
728
        return false;
729
      }
730
731
      jQ.ajax({
732
        url: '/caserun/update-bugs-for-many/',
733
        dataType: 'json',
734
        success: function(res) {
735
          if (res.rc == 0) {
736
            reloadWindow();
737
          } else {
738
            window.alert(res.response);
739
            return false;
740
          }
741
        },
742
        data: form_data,
743
      });
744
    }
745
  });
746
  dialog.show();
747
}
748
749
function updateBugs(action) {
750
  var runs = serializeCaseRunFromInputList(jQ('#id_table_cases')[0]);
751
  if (!runs.length) {
752
    window.alert(default_messages.alert.no_case_selected);
753
    return false;
754
  }
755
756
  if (action === "add") {
757
    updateBugsActionAdd(runs);
758
  } else if (action === "remove") {
759
    updateBugsActionRemove(runs);
760
  } else {
761
    throw new Error("Unknown operation when update case runs' bugs. This should not happen.");
762
  }
763
}
764
765
function showCommentForm() {
766
  var dialog = getDialog();
767
  var runs = serializeCaseRunFromInputList(jQ('#id_table_cases')[0]);
768
  if (!runs.length) {
769
    return window.alert(default_messages.alert.no_case_selected);
770
  }
771
  var template = Handlebars.compile(jQ("#batch_add_comment_to_caseruns_template").html());
772
  jQ(dialog).html(template());
773
774
  var commentText = jQ('#commentText');
775
  var commentsErr = jQ('#commentsErr');
776
  jQ('#btnComment').live('click', function() {
777
    var error;
778
    var comments = jQ.trim(commentText.val());
779
    if (!comments) {
780
      error = 'No comments given.';
781
    }
782
    if (error) {
783
      commentsErr.html(error);
784
      return false;
785
    }
786
    jQ.ajax({
787
      url: '/caserun/comment-many/',
788
      data: {'comment': comments, 'run': runs.join()},
789
      dataType: 'json',
790
      type: 'post',
791
      success: function(res) {
792
        if (res.rc == 0) {
793
          reloadWindow();
794
        } else {
795
          commentsErr.html(res.response);
796
          return false;
797
        }
798
      }
799
    });
800
  });
801
  jQ('#btnCancelComment').live('click', function(){
802
    jQ(dialog).hide();
803
    commentText.val('');
804
  });
805
  jQ(dialog).show();
806
}
807
808
jQ(document).ready(function(){
809
  jQ('.btnBlueCaserun').mouseover(function() {
810
    jQ(this).find('ul').show();
811
  }).mouseout(function() {
812
    jQ(this).find('ul').hide();
813
  });
814
  jQ('ul.statusOptions a').click(function() {
815
    var option = jQ(this).attr('value');
816
    var object_pks = serializeCaseRunFromInputList(jQ('#id_table_cases')[0]);
817
    if (option == '') {
818
      return false;
819
    }
820
    if (!object_pks.length) {
821
      window.alert(default_messages.alert.no_case_selected);
822
      return false;
823
    }
824
    if (!window.confirm(default_messages.confirm.change_case_status)) {
825
      return false;
826
    }
827
    updateRunStatus(object_pks, option, reloadWindow);
828
  });
829
});
830
831
function get_addlink_dialog() {
832
  return jQ('#addlink_dialog');
833
}
834
835
/*
836
 * Do AJAX request to backend to remove a link
837
 *
838
 * - sender:
839
 * - link_id: the ID of an arbitrary link.
840
 */
841
function removeLink(sender, link_id) {
842
  jQ.ajax({
843
    url: '/linkref/remove/' + link_id + '/',
844
    type: 'GET',
845
    dataType: 'json',
846
    success: function(data, textStatus, jqXHR) {
847
      if (data.rc !== 0) {
848
        window.alert(data.response);
849
        return false;
850
      }
851
      var li_node = sender.parentNode;
852
      li_node.parentNode.removeChild(li_node);
853
    },
854
    error: function(jqXHR, textStatus, errorThrown) {
855
      var data = JSON.parse(jqXHR.responseText);
856
      window.alert(data.message);
857
    }
858
  });
859
}
860
861
/*
862
 * Add link to case run
863
 *
864
 * - sender: the Add link button, which is pressed to fire this event.
865
 * - target_id: to which TestCaseRun the new link will be linked.
866
 */
867
function addLinkToCaseRun(sender, case_id, case_run_id) {
868
  var dialog_p = get_addlink_dialog();
869
870
  dialog_p.dialog('option', 'target_id', case_run_id);
871
  // These two options are used for reloading TestCaseRun when successfully.
872
  var container = jQ(sender).parents('.case_content.hide')[0];
873
  dialog_p.dialog('option', 'container', container);
874
  var title_container = jQ(container).prev()[0];
875
  dialog_p.dialog('option', 'title_container', title_container);
876
  dialog_p.dialog('option', 'case_id', case_id);
877
  dialog_p.dialog('open');
878
}
879
880
/*
881
 * Initialize dialog for getting information about new link, which is attached
882
 * to an arbitrary instance of TestCaseRun
883
 */
884
function initialize_addlink_dialog() {
885
  var dialog_p = get_addlink_dialog();
886
887
  dialog_p.dialog({
888
    autoOpen: false,
889
    modal: true,
890
    resizable: false,
891
    height: 300,
892
    width: 400,
893
    open: function() {
894
      jQ(this).unbind('submit').bind('submit', function (e) {
895
        e.stopPropagation();
896
        e.preventDefault();
897
        jQ(this).dialog('widget').find('span:contains("OK")').click();
898
      });
899
    },
900
    buttons: {
901
      "OK": function() {
902
        // TODO: validate name and url
903
        var name = jQ('#testlog_name').attr('value');
904
        var url = jQ('#testlog_url').attr('value');
905
        var target_id = jQ(this).dialog('option', 'target_id');
906
907
        jQ.ajax({
908
          url: '/linkref/add/',
909
          type: 'POST',
910
          data: { name: name, url: url, target_id: target_id },
911
          dataType: 'json',
912
          success: function(data, textStatus, jqXHR) {
913
            if (data.rc !== 0) {
914
              window.alert(data.response);
915
              return false;
916
            }
917
            dialog_p.dialog('close');
918
919
            // Begin to construct case run area
920
            var container = dialog_p.dialog('option', 'container');
921
            var title_container = dialog_p.dialog('option', 'title_container');
922
            var case_id = dialog_p.dialog('option', 'case_id');
923
            constructCaseRunZone(container, title_container, case_id);
924
          },
925
          error: function (jqXHR, textStatus, errorThrown) {
926
            var data = JSON.parse(jqXHR.responseText);
927
            window.alert(data.response);
928
          }
929
        });
930
      },
931
      "Cancel": function() {
932
        jQ(this).dialog('close');
933
      }
934
    },
935
    beforeClose: function() {
936
      // clean name and url for next input
937
      jQ('#testlog_name').val('');
938
      jQ('#testlog_url').val('');
939
940
      return true;
941
    },
942
    /* ATTENTION: target_id can be determined when open this dialog, and
943
     * this must be set
944
     */
945
    target_id: null
946
  });
947
}
948
949
950
/*
951
 * Toggle TestCaseRun panel to edit a case run in run page.
952
 *
953
 * Arguments:
954
 * options.casrunContainer:
955
 * options.expandPaneContainer:
956
 * options.caseId:
957
 * options.caserunId:
958
 * options.caseTextVersion:
959
 * options.callback:
960
 */
961
function toggleTestCaseRunPane(options) {
962
  var container = options.caserunRowContainer;
963
  var content_container = options.expandPaneContainer;
964
  var callback = options.callback;
965
966
  content_container.toggle();
967
968
  if (content_container.find('.ajax_loading').length) {
969
    var url = '/case/' + options.caseId + '/execution-detail-pane/';
970
    var data = { case_run_id: options.caserunId, case_text_version: options.caseTextVersion };
971
972
    jQ.get(url, data, function(data, textStatus) {
973
      content_container.html(data);
974
      callback();
975
    }, 'html');
976
  }
977
978
  toggleExpandArrow({ caseRowContainer: container, expandPaneContainer: content_container });
979
}
980