##// END OF EJS Templates
Don't set default due date in the past (#21488)....
Jean-Philippe Lang -
r14587:430b0b7a812a
parent child
Show More
@@ -1,673 +1,679
1 /* Redmine - project management software
1 /* Redmine - project management software
2 Copyright (C) 2006-2015 Jean-Philippe Lang */
2 Copyright (C) 2006-2015 Jean-Philippe Lang */
3
3
4 function checkAll(id, checked) {
4 function checkAll(id, checked) {
5 $('#'+id).find('input[type=checkbox]:enabled').prop('checked', checked);
5 $('#'+id).find('input[type=checkbox]:enabled').prop('checked', checked);
6 }
6 }
7
7
8 function toggleCheckboxesBySelector(selector) {
8 function toggleCheckboxesBySelector(selector) {
9 var all_checked = true;
9 var all_checked = true;
10 $(selector).each(function(index) {
10 $(selector).each(function(index) {
11 if (!$(this).is(':checked')) { all_checked = false; }
11 if (!$(this).is(':checked')) { all_checked = false; }
12 });
12 });
13 $(selector).prop('checked', !all_checked);
13 $(selector).prop('checked', !all_checked);
14 }
14 }
15
15
16 function showAndScrollTo(id, focus) {
16 function showAndScrollTo(id, focus) {
17 $('#'+id).show();
17 $('#'+id).show();
18 if (focus !== null) {
18 if (focus !== null) {
19 $('#'+focus).focus();
19 $('#'+focus).focus();
20 }
20 }
21 $('html, body').animate({scrollTop: $('#'+id).offset().top}, 100);
21 $('html, body').animate({scrollTop: $('#'+id).offset().top}, 100);
22 }
22 }
23
23
24 function toggleRowGroup(el) {
24 function toggleRowGroup(el) {
25 var tr = $(el).parents('tr').first();
25 var tr = $(el).parents('tr').first();
26 var n = tr.next();
26 var n = tr.next();
27 tr.toggleClass('open');
27 tr.toggleClass('open');
28 while (n.length && !n.hasClass('group')) {
28 while (n.length && !n.hasClass('group')) {
29 n.toggle();
29 n.toggle();
30 n = n.next('tr');
30 n = n.next('tr');
31 }
31 }
32 }
32 }
33
33
34 function collapseAllRowGroups(el) {
34 function collapseAllRowGroups(el) {
35 var tbody = $(el).parents('tbody').first();
35 var tbody = $(el).parents('tbody').first();
36 tbody.children('tr').each(function(index) {
36 tbody.children('tr').each(function(index) {
37 if ($(this).hasClass('group')) {
37 if ($(this).hasClass('group')) {
38 $(this).removeClass('open');
38 $(this).removeClass('open');
39 } else {
39 } else {
40 $(this).hide();
40 $(this).hide();
41 }
41 }
42 });
42 });
43 }
43 }
44
44
45 function expandAllRowGroups(el) {
45 function expandAllRowGroups(el) {
46 var tbody = $(el).parents('tbody').first();
46 var tbody = $(el).parents('tbody').first();
47 tbody.children('tr').each(function(index) {
47 tbody.children('tr').each(function(index) {
48 if ($(this).hasClass('group')) {
48 if ($(this).hasClass('group')) {
49 $(this).addClass('open');
49 $(this).addClass('open');
50 } else {
50 } else {
51 $(this).show();
51 $(this).show();
52 }
52 }
53 });
53 });
54 }
54 }
55
55
56 function toggleAllRowGroups(el) {
56 function toggleAllRowGroups(el) {
57 var tr = $(el).parents('tr').first();
57 var tr = $(el).parents('tr').first();
58 if (tr.hasClass('open')) {
58 if (tr.hasClass('open')) {
59 collapseAllRowGroups(el);
59 collapseAllRowGroups(el);
60 } else {
60 } else {
61 expandAllRowGroups(el);
61 expandAllRowGroups(el);
62 }
62 }
63 }
63 }
64
64
65 function toggleFieldset(el) {
65 function toggleFieldset(el) {
66 var fieldset = $(el).parents('fieldset').first();
66 var fieldset = $(el).parents('fieldset').first();
67 fieldset.toggleClass('collapsed');
67 fieldset.toggleClass('collapsed');
68 fieldset.children('div').toggle();
68 fieldset.children('div').toggle();
69 }
69 }
70
70
71 function hideFieldset(el) {
71 function hideFieldset(el) {
72 var fieldset = $(el).parents('fieldset').first();
72 var fieldset = $(el).parents('fieldset').first();
73 fieldset.toggleClass('collapsed');
73 fieldset.toggleClass('collapsed');
74 fieldset.children('div').hide();
74 fieldset.children('div').hide();
75 }
75 }
76
76
77 // columns selection
77 // columns selection
78 function moveOptions(theSelFrom, theSelTo) {
78 function moveOptions(theSelFrom, theSelTo) {
79 $(theSelFrom).find('option:selected').detach().prop("selected", false).appendTo($(theSelTo));
79 $(theSelFrom).find('option:selected').detach().prop("selected", false).appendTo($(theSelTo));
80 }
80 }
81
81
82 function moveOptionUp(theSel) {
82 function moveOptionUp(theSel) {
83 $(theSel).find('option:selected').each(function(){
83 $(theSel).find('option:selected').each(function(){
84 $(this).prev(':not(:selected)').detach().insertAfter($(this));
84 $(this).prev(':not(:selected)').detach().insertAfter($(this));
85 });
85 });
86 }
86 }
87
87
88 function moveOptionTop(theSel) {
88 function moveOptionTop(theSel) {
89 $(theSel).find('option:selected').detach().prependTo($(theSel));
89 $(theSel).find('option:selected').detach().prependTo($(theSel));
90 }
90 }
91
91
92 function moveOptionDown(theSel) {
92 function moveOptionDown(theSel) {
93 $($(theSel).find('option:selected').get().reverse()).each(function(){
93 $($(theSel).find('option:selected').get().reverse()).each(function(){
94 $(this).next(':not(:selected)').detach().insertBefore($(this));
94 $(this).next(':not(:selected)').detach().insertBefore($(this));
95 });
95 });
96 }
96 }
97
97
98 function moveOptionBottom(theSel) {
98 function moveOptionBottom(theSel) {
99 $(theSel).find('option:selected').detach().appendTo($(theSel));
99 $(theSel).find('option:selected').detach().appendTo($(theSel));
100 }
100 }
101
101
102 function initFilters() {
102 function initFilters() {
103 $('#add_filter_select').change(function() {
103 $('#add_filter_select').change(function() {
104 addFilter($(this).val(), '', []);
104 addFilter($(this).val(), '', []);
105 });
105 });
106 $('#filters-table td.field input[type=checkbox]').each(function() {
106 $('#filters-table td.field input[type=checkbox]').each(function() {
107 toggleFilter($(this).val());
107 toggleFilter($(this).val());
108 });
108 });
109 $('#filters-table').on('click', 'td.field input[type=checkbox]', function() {
109 $('#filters-table').on('click', 'td.field input[type=checkbox]', function() {
110 toggleFilter($(this).val());
110 toggleFilter($(this).val());
111 });
111 });
112 $('#filters-table').on('click', '.toggle-multiselect', function() {
112 $('#filters-table').on('click', '.toggle-multiselect', function() {
113 toggleMultiSelect($(this).siblings('select'));
113 toggleMultiSelect($(this).siblings('select'));
114 });
114 });
115 $('#filters-table').on('keypress', 'input[type=text]', function(e) {
115 $('#filters-table').on('keypress', 'input[type=text]', function(e) {
116 if (e.keyCode == 13) $(this).closest('form').submit();
116 if (e.keyCode == 13) $(this).closest('form').submit();
117 });
117 });
118 }
118 }
119
119
120 function addFilter(field, operator, values) {
120 function addFilter(field, operator, values) {
121 var fieldId = field.replace('.', '_');
121 var fieldId = field.replace('.', '_');
122 var tr = $('#tr_'+fieldId);
122 var tr = $('#tr_'+fieldId);
123 if (tr.length > 0) {
123 if (tr.length > 0) {
124 tr.show();
124 tr.show();
125 } else {
125 } else {
126 buildFilterRow(field, operator, values);
126 buildFilterRow(field, operator, values);
127 }
127 }
128 $('#cb_'+fieldId).prop('checked', true);
128 $('#cb_'+fieldId).prop('checked', true);
129 toggleFilter(field);
129 toggleFilter(field);
130 $('#add_filter_select').val('').find('option').each(function() {
130 $('#add_filter_select').val('').find('option').each(function() {
131 if ($(this).attr('value') == field) {
131 if ($(this).attr('value') == field) {
132 $(this).attr('disabled', true);
132 $(this).attr('disabled', true);
133 }
133 }
134 });
134 });
135 }
135 }
136
136
137 function buildFilterRow(field, operator, values) {
137 function buildFilterRow(field, operator, values) {
138 var fieldId = field.replace('.', '_');
138 var fieldId = field.replace('.', '_');
139 var filterTable = $("#filters-table");
139 var filterTable = $("#filters-table");
140 var filterOptions = availableFilters[field];
140 var filterOptions = availableFilters[field];
141 if (!filterOptions) return;
141 if (!filterOptions) return;
142 var operators = operatorByType[filterOptions['type']];
142 var operators = operatorByType[filterOptions['type']];
143 var filterValues = filterOptions['values'];
143 var filterValues = filterOptions['values'];
144 var i, select;
144 var i, select;
145
145
146 var tr = $('<tr class="filter">').attr('id', 'tr_'+fieldId).html(
146 var tr = $('<tr class="filter">').attr('id', 'tr_'+fieldId).html(
147 '<td class="field"><input checked="checked" id="cb_'+fieldId+'" name="f[]" value="'+field+'" type="checkbox"><label for="cb_'+fieldId+'"> '+filterOptions['name']+'</label></td>' +
147 '<td class="field"><input checked="checked" id="cb_'+fieldId+'" name="f[]" value="'+field+'" type="checkbox"><label for="cb_'+fieldId+'"> '+filterOptions['name']+'</label></td>' +
148 '<td class="operator"><select id="operators_'+fieldId+'" name="op['+field+']"></td>' +
148 '<td class="operator"><select id="operators_'+fieldId+'" name="op['+field+']"></td>' +
149 '<td class="values"></td>'
149 '<td class="values"></td>'
150 );
150 );
151 filterTable.append(tr);
151 filterTable.append(tr);
152
152
153 select = tr.find('td.operator select');
153 select = tr.find('td.operator select');
154 for (i = 0; i < operators.length; i++) {
154 for (i = 0; i < operators.length; i++) {
155 var option = $('<option>').val(operators[i]).text(operatorLabels[operators[i]]);
155 var option = $('<option>').val(operators[i]).text(operatorLabels[operators[i]]);
156 if (operators[i] == operator) { option.attr('selected', true); }
156 if (operators[i] == operator) { option.attr('selected', true); }
157 select.append(option);
157 select.append(option);
158 }
158 }
159 select.change(function(){ toggleOperator(field); });
159 select.change(function(){ toggleOperator(field); });
160
160
161 switch (filterOptions['type']) {
161 switch (filterOptions['type']) {
162 case "list":
162 case "list":
163 case "list_optional":
163 case "list_optional":
164 case "list_status":
164 case "list_status":
165 case "list_subprojects":
165 case "list_subprojects":
166 tr.find('td.values').append(
166 tr.find('td.values').append(
167 '<span style="display:none;"><select class="value" id="values_'+fieldId+'_1" name="v['+field+'][]"></select>' +
167 '<span style="display:none;"><select class="value" id="values_'+fieldId+'_1" name="v['+field+'][]"></select>' +
168 ' <span class="toggle-multiselect">&nbsp;</span></span>'
168 ' <span class="toggle-multiselect">&nbsp;</span></span>'
169 );
169 );
170 select = tr.find('td.values select');
170 select = tr.find('td.values select');
171 if (values.length > 1) { select.attr('multiple', true); }
171 if (values.length > 1) { select.attr('multiple', true); }
172 for (i = 0; i < filterValues.length; i++) {
172 for (i = 0; i < filterValues.length; i++) {
173 var filterValue = filterValues[i];
173 var filterValue = filterValues[i];
174 var option = $('<option>');
174 var option = $('<option>');
175 if ($.isArray(filterValue)) {
175 if ($.isArray(filterValue)) {
176 option.val(filterValue[1]).text(filterValue[0]);
176 option.val(filterValue[1]).text(filterValue[0]);
177 if ($.inArray(filterValue[1], values) > -1) {option.attr('selected', true);}
177 if ($.inArray(filterValue[1], values) > -1) {option.attr('selected', true);}
178 } else {
178 } else {
179 option.val(filterValue).text(filterValue);
179 option.val(filterValue).text(filterValue);
180 if ($.inArray(filterValue, values) > -1) {option.attr('selected', true);}
180 if ($.inArray(filterValue, values) > -1) {option.attr('selected', true);}
181 }
181 }
182 select.append(option);
182 select.append(option);
183 }
183 }
184 break;
184 break;
185 case "date":
185 case "date":
186 case "date_past":
186 case "date_past":
187 tr.find('td.values').append(
187 tr.find('td.values').append(
188 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_1" size="10" class="value date_value" /></span>' +
188 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_1" size="10" class="value date_value" /></span>' +
189 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_2" size="10" class="value date_value" /></span>' +
189 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_2" size="10" class="value date_value" /></span>' +
190 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="3" class="value" /> '+labelDayPlural+'</span>'
190 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="3" class="value" /> '+labelDayPlural+'</span>'
191 );
191 );
192 $('#values_'+fieldId+'_1').val(values[0]).datepicker(datepickerOptions);
192 $('#values_'+fieldId+'_1').val(values[0]).datepicker(datepickerOptions);
193 $('#values_'+fieldId+'_2').val(values[1]).datepicker(datepickerOptions);
193 $('#values_'+fieldId+'_2').val(values[1]).datepicker(datepickerOptions);
194 $('#values_'+fieldId).val(values[0]);
194 $('#values_'+fieldId).val(values[0]);
195 break;
195 break;
196 case "string":
196 case "string":
197 case "text":
197 case "text":
198 tr.find('td.values').append(
198 tr.find('td.values').append(
199 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="30" class="value" /></span>'
199 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="30" class="value" /></span>'
200 );
200 );
201 $('#values_'+fieldId).val(values[0]);
201 $('#values_'+fieldId).val(values[0]);
202 break;
202 break;
203 case "relation":
203 case "relation":
204 tr.find('td.values').append(
204 tr.find('td.values').append(
205 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="6" class="value" /></span>' +
205 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="6" class="value" /></span>' +
206 '<span style="display:none;"><select class="value" name="v['+field+'][]" id="values_'+fieldId+'_1"></select></span>'
206 '<span style="display:none;"><select class="value" name="v['+field+'][]" id="values_'+fieldId+'_1"></select></span>'
207 );
207 );
208 $('#values_'+fieldId).val(values[0]);
208 $('#values_'+fieldId).val(values[0]);
209 select = tr.find('td.values select');
209 select = tr.find('td.values select');
210 for (i = 0; i < allProjects.length; i++) {
210 for (i = 0; i < allProjects.length; i++) {
211 var filterValue = allProjects[i];
211 var filterValue = allProjects[i];
212 var option = $('<option>');
212 var option = $('<option>');
213 option.val(filterValue[1]).text(filterValue[0]);
213 option.val(filterValue[1]).text(filterValue[0]);
214 if (values[0] == filterValue[1]) { option.attr('selected', true); }
214 if (values[0] == filterValue[1]) { option.attr('selected', true); }
215 select.append(option);
215 select.append(option);
216 }
216 }
217 break;
217 break;
218 case "integer":
218 case "integer":
219 case "float":
219 case "float":
220 case "tree":
220 case "tree":
221 tr.find('td.values').append(
221 tr.find('td.values').append(
222 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_1" size="6" class="value" /></span>' +
222 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_1" size="6" class="value" /></span>' +
223 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_2" size="6" class="value" /></span>'
223 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_2" size="6" class="value" /></span>'
224 );
224 );
225 $('#values_'+fieldId+'_1').val(values[0]);
225 $('#values_'+fieldId+'_1').val(values[0]);
226 $('#values_'+fieldId+'_2').val(values[1]);
226 $('#values_'+fieldId+'_2').val(values[1]);
227 break;
227 break;
228 }
228 }
229 }
229 }
230
230
231 function toggleFilter(field) {
231 function toggleFilter(field) {
232 var fieldId = field.replace('.', '_');
232 var fieldId = field.replace('.', '_');
233 if ($('#cb_' + fieldId).is(':checked')) {
233 if ($('#cb_' + fieldId).is(':checked')) {
234 $("#operators_" + fieldId).show().removeAttr('disabled');
234 $("#operators_" + fieldId).show().removeAttr('disabled');
235 toggleOperator(field);
235 toggleOperator(field);
236 } else {
236 } else {
237 $("#operators_" + fieldId).hide().attr('disabled', true);
237 $("#operators_" + fieldId).hide().attr('disabled', true);
238 enableValues(field, []);
238 enableValues(field, []);
239 }
239 }
240 }
240 }
241
241
242 function enableValues(field, indexes) {
242 function enableValues(field, indexes) {
243 var fieldId = field.replace('.', '_');
243 var fieldId = field.replace('.', '_');
244 $('#tr_'+fieldId+' td.values .value').each(function(index) {
244 $('#tr_'+fieldId+' td.values .value').each(function(index) {
245 if ($.inArray(index, indexes) >= 0) {
245 if ($.inArray(index, indexes) >= 0) {
246 $(this).removeAttr('disabled');
246 $(this).removeAttr('disabled');
247 $(this).parents('span').first().show();
247 $(this).parents('span').first().show();
248 } else {
248 } else {
249 $(this).val('');
249 $(this).val('');
250 $(this).attr('disabled', true);
250 $(this).attr('disabled', true);
251 $(this).parents('span').first().hide();
251 $(this).parents('span').first().hide();
252 }
252 }
253
253
254 if ($(this).hasClass('group')) {
254 if ($(this).hasClass('group')) {
255 $(this).addClass('open');
255 $(this).addClass('open');
256 } else {
256 } else {
257 $(this).show();
257 $(this).show();
258 }
258 }
259 });
259 });
260 }
260 }
261
261
262 function toggleOperator(field) {
262 function toggleOperator(field) {
263 var fieldId = field.replace('.', '_');
263 var fieldId = field.replace('.', '_');
264 var operator = $("#operators_" + fieldId);
264 var operator = $("#operators_" + fieldId);
265 switch (operator.val()) {
265 switch (operator.val()) {
266 case "!*":
266 case "!*":
267 case "*":
267 case "*":
268 case "t":
268 case "t":
269 case "ld":
269 case "ld":
270 case "w":
270 case "w":
271 case "lw":
271 case "lw":
272 case "l2w":
272 case "l2w":
273 case "m":
273 case "m":
274 case "lm":
274 case "lm":
275 case "y":
275 case "y":
276 case "o":
276 case "o":
277 case "c":
277 case "c":
278 case "*o":
278 case "*o":
279 case "!o":
279 case "!o":
280 enableValues(field, []);
280 enableValues(field, []);
281 break;
281 break;
282 case "><":
282 case "><":
283 enableValues(field, [0,1]);
283 enableValues(field, [0,1]);
284 break;
284 break;
285 case "<t+":
285 case "<t+":
286 case ">t+":
286 case ">t+":
287 case "><t+":
287 case "><t+":
288 case "t+":
288 case "t+":
289 case ">t-":
289 case ">t-":
290 case "<t-":
290 case "<t-":
291 case "><t-":
291 case "><t-":
292 case "t-":
292 case "t-":
293 enableValues(field, [2]);
293 enableValues(field, [2]);
294 break;
294 break;
295 case "=p":
295 case "=p":
296 case "=!p":
296 case "=!p":
297 case "!p":
297 case "!p":
298 enableValues(field, [1]);
298 enableValues(field, [1]);
299 break;
299 break;
300 default:
300 default:
301 enableValues(field, [0]);
301 enableValues(field, [0]);
302 break;
302 break;
303 }
303 }
304 }
304 }
305
305
306 function toggleMultiSelect(el) {
306 function toggleMultiSelect(el) {
307 if (el.attr('multiple')) {
307 if (el.attr('multiple')) {
308 el.removeAttr('multiple');
308 el.removeAttr('multiple');
309 el.attr('size', 1);
309 el.attr('size', 1);
310 } else {
310 } else {
311 el.attr('multiple', true);
311 el.attr('multiple', true);
312 if (el.children().length > 10)
312 if (el.children().length > 10)
313 el.attr('size', 10);
313 el.attr('size', 10);
314 else
314 else
315 el.attr('size', 4);
315 el.attr('size', 4);
316 }
316 }
317 }
317 }
318
318
319 function showTab(name, url) {
319 function showTab(name, url) {
320 $('#tab-content-' + name).parent().find('.tab-content').hide();
320 $('#tab-content-' + name).parent().find('.tab-content').hide();
321 $('#tab-content-' + name).parent().find('div.tabs a').removeClass('selected');
321 $('#tab-content-' + name).parent().find('div.tabs a').removeClass('selected');
322 $('#tab-content-' + name).show();
322 $('#tab-content-' + name).show();
323 $('#tab-' + name).addClass('selected');
323 $('#tab-' + name).addClass('selected');
324 //replaces current URL with the "href" attribute of the current link
324 //replaces current URL with the "href" attribute of the current link
325 //(only triggered if supported by browser)
325 //(only triggered if supported by browser)
326 if ("replaceState" in window.history) {
326 if ("replaceState" in window.history) {
327 window.history.replaceState(null, document.title, url);
327 window.history.replaceState(null, document.title, url);
328 }
328 }
329 return false;
329 return false;
330 }
330 }
331
331
332 function moveTabRight(el) {
332 function moveTabRight(el) {
333 var lis = $(el).parents('div.tabs').first().find('ul').children();
333 var lis = $(el).parents('div.tabs').first().find('ul').children();
334 var tabsWidth = 0;
334 var tabsWidth = 0;
335 var i = 0;
335 var i = 0;
336 lis.each(function() {
336 lis.each(function() {
337 if ($(this).is(':visible')) {
337 if ($(this).is(':visible')) {
338 tabsWidth += $(this).width() + 6;
338 tabsWidth += $(this).width() + 6;
339 }
339 }
340 });
340 });
341 if (tabsWidth < $(el).parents('div.tabs').first().width() - 60) { return; }
341 if (tabsWidth < $(el).parents('div.tabs').first().width() - 60) { return; }
342 while (i<lis.length && !lis.eq(i).is(':visible')) { i++; }
342 while (i<lis.length && !lis.eq(i).is(':visible')) { i++; }
343 lis.eq(i).hide();
343 lis.eq(i).hide();
344 }
344 }
345
345
346 function moveTabLeft(el) {
346 function moveTabLeft(el) {
347 var lis = $(el).parents('div.tabs').first().find('ul').children();
347 var lis = $(el).parents('div.tabs').first().find('ul').children();
348 var i = 0;
348 var i = 0;
349 while (i < lis.length && !lis.eq(i).is(':visible')) { i++; }
349 while (i < lis.length && !lis.eq(i).is(':visible')) { i++; }
350 if (i > 0) {
350 if (i > 0) {
351 lis.eq(i-1).show();
351 lis.eq(i-1).show();
352 }
352 }
353 }
353 }
354
354
355 function displayTabsButtons() {
355 function displayTabsButtons() {
356 var lis;
356 var lis;
357 var tabsWidth;
357 var tabsWidth;
358 var el;
358 var el;
359 $('div.tabs').each(function() {
359 $('div.tabs').each(function() {
360 el = $(this);
360 el = $(this);
361 lis = el.find('ul').children();
361 lis = el.find('ul').children();
362 tabsWidth = 0;
362 tabsWidth = 0;
363 lis.each(function(){
363 lis.each(function(){
364 if ($(this).is(':visible')) {
364 if ($(this).is(':visible')) {
365 tabsWidth += $(this).width() + 6;
365 tabsWidth += $(this).width() + 6;
366 }
366 }
367 });
367 });
368 if ((tabsWidth < el.width() - 60) && (lis.first().is(':visible'))) {
368 if ((tabsWidth < el.width() - 60) && (lis.first().is(':visible'))) {
369 el.find('div.tabs-buttons').hide();
369 el.find('div.tabs-buttons').hide();
370 } else {
370 } else {
371 el.find('div.tabs-buttons').show();
371 el.find('div.tabs-buttons').show();
372 }
372 }
373 });
373 });
374 }
374 }
375
375
376 function setPredecessorFieldsVisibility() {
376 function setPredecessorFieldsVisibility() {
377 var relationType = $('#relation_relation_type');
377 var relationType = $('#relation_relation_type');
378 if (relationType.val() == "precedes" || relationType.val() == "follows") {
378 if (relationType.val() == "precedes" || relationType.val() == "follows") {
379 $('#predecessor_fields').show();
379 $('#predecessor_fields').show();
380 } else {
380 } else {
381 $('#predecessor_fields').hide();
381 $('#predecessor_fields').hide();
382 }
382 }
383 }
383 }
384
384
385 function showModal(id, width, title) {
385 function showModal(id, width, title) {
386 var el = $('#'+id).first();
386 var el = $('#'+id).first();
387 if (el.length === 0 || el.is(':visible')) {return;}
387 if (el.length === 0 || el.is(':visible')) {return;}
388 if (!title) title = el.find('h3.title').text();
388 if (!title) title = el.find('h3.title').text();
389 // moves existing modals behind the transparent background
389 // moves existing modals behind the transparent background
390 $(".modal").zIndex(99);
390 $(".modal").zIndex(99);
391 el.dialog({
391 el.dialog({
392 width: width,
392 width: width,
393 modal: true,
393 modal: true,
394 resizable: false,
394 resizable: false,
395 dialogClass: 'modal',
395 dialogClass: 'modal',
396 title: title
396 title: title
397 }).on('dialogclose', function(){
397 }).on('dialogclose', function(){
398 $(".modal").zIndex(101);
398 $(".modal").zIndex(101);
399 });
399 });
400 el.find("input[type=text], input[type=submit]").first().focus();
400 el.find("input[type=text], input[type=submit]").first().focus();
401 }
401 }
402
402
403 function hideModal(el) {
403 function hideModal(el) {
404 var modal;
404 var modal;
405 if (el) {
405 if (el) {
406 modal = $(el).parents('.ui-dialog-content');
406 modal = $(el).parents('.ui-dialog-content');
407 } else {
407 } else {
408 modal = $('#ajax-modal');
408 modal = $('#ajax-modal');
409 }
409 }
410 modal.dialog("close");
410 modal.dialog("close");
411 }
411 }
412
412
413 function submitPreview(url, form, target) {
413 function submitPreview(url, form, target) {
414 $.ajax({
414 $.ajax({
415 url: url,
415 url: url,
416 type: 'post',
416 type: 'post',
417 data: $('#'+form).serialize(),
417 data: $('#'+form).serialize(),
418 success: function(data){
418 success: function(data){
419 $('#'+target).html(data);
419 $('#'+target).html(data);
420 }
420 }
421 });
421 });
422 }
422 }
423
423
424 function collapseScmEntry(id) {
424 function collapseScmEntry(id) {
425 $('.'+id).each(function() {
425 $('.'+id).each(function() {
426 if ($(this).hasClass('open')) {
426 if ($(this).hasClass('open')) {
427 collapseScmEntry($(this).attr('id'));
427 collapseScmEntry($(this).attr('id'));
428 }
428 }
429 $(this).hide();
429 $(this).hide();
430 });
430 });
431 $('#'+id).removeClass('open');
431 $('#'+id).removeClass('open');
432 }
432 }
433
433
434 function expandScmEntry(id) {
434 function expandScmEntry(id) {
435 $('.'+id).each(function() {
435 $('.'+id).each(function() {
436 $(this).show();
436 $(this).show();
437 if ($(this).hasClass('loaded') && !$(this).hasClass('collapsed')) {
437 if ($(this).hasClass('loaded') && !$(this).hasClass('collapsed')) {
438 expandScmEntry($(this).attr('id'));
438 expandScmEntry($(this).attr('id'));
439 }
439 }
440 });
440 });
441 $('#'+id).addClass('open');
441 $('#'+id).addClass('open');
442 }
442 }
443
443
444 function scmEntryClick(id, url) {
444 function scmEntryClick(id, url) {
445 var el = $('#'+id);
445 var el = $('#'+id);
446 if (el.hasClass('open')) {
446 if (el.hasClass('open')) {
447 collapseScmEntry(id);
447 collapseScmEntry(id);
448 el.addClass('collapsed');
448 el.addClass('collapsed');
449 return false;
449 return false;
450 } else if (el.hasClass('loaded')) {
450 } else if (el.hasClass('loaded')) {
451 expandScmEntry(id);
451 expandScmEntry(id);
452 el.removeClass('collapsed');
452 el.removeClass('collapsed');
453 return false;
453 return false;
454 }
454 }
455 if (el.hasClass('loading')) {
455 if (el.hasClass('loading')) {
456 return false;
456 return false;
457 }
457 }
458 el.addClass('loading');
458 el.addClass('loading');
459 $.ajax({
459 $.ajax({
460 url: url,
460 url: url,
461 success: function(data) {
461 success: function(data) {
462 el.after(data);
462 el.after(data);
463 el.addClass('open').addClass('loaded').removeClass('loading');
463 el.addClass('open').addClass('loaded').removeClass('loading');
464 }
464 }
465 });
465 });
466 return true;
466 return true;
467 }
467 }
468
468
469 function randomKey(size) {
469 function randomKey(size) {
470 var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
470 var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
471 var key = '';
471 var key = '';
472 for (var i = 0; i < size; i++) {
472 for (var i = 0; i < size; i++) {
473 key += chars.charAt(Math.floor(Math.random() * chars.length));
473 key += chars.charAt(Math.floor(Math.random() * chars.length));
474 }
474 }
475 return key;
475 return key;
476 }
476 }
477
477
478 function updateIssueFrom(url, el) {
478 function updateIssueFrom(url, el) {
479 $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
479 $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
480 $(this).data('valuebeforeupdate', $(this).val());
480 $(this).data('valuebeforeupdate', $(this).val());
481 });
481 });
482 if (el) {
482 if (el) {
483 $("#form_update_triggered_by").val($(el).attr('id'));
483 $("#form_update_triggered_by").val($(el).attr('id'));
484 }
484 }
485 return $.ajax({
485 return $.ajax({
486 url: url,
486 url: url,
487 type: 'post',
487 type: 'post',
488 data: $('#issue-form').serialize()
488 data: $('#issue-form').serialize()
489 });
489 });
490 }
490 }
491
491
492 function replaceIssueFormWith(html){
492 function replaceIssueFormWith(html){
493 var replacement = $(html);
493 var replacement = $(html);
494 $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
494 $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
495 var object_id = $(this).attr('id');
495 var object_id = $(this).attr('id');
496 if (object_id && $(this).data('valuebeforeupdate')!=$(this).val()) {
496 if (object_id && $(this).data('valuebeforeupdate')!=$(this).val()) {
497 replacement.find('#'+object_id).val($(this).val());
497 replacement.find('#'+object_id).val($(this).val());
498 }
498 }
499 });
499 });
500 $('#all_attributes').empty();
500 $('#all_attributes').empty();
501 $('#all_attributes').prepend(replacement);
501 $('#all_attributes').prepend(replacement);
502 }
502 }
503
503
504 function updateBulkEditFrom(url) {
504 function updateBulkEditFrom(url) {
505 $.ajax({
505 $.ajax({
506 url: url,
506 url: url,
507 type: 'post',
507 type: 'post',
508 data: $('#bulk_edit_form').serialize()
508 data: $('#bulk_edit_form').serialize()
509 });
509 });
510 }
510 }
511
511
512 function observeAutocompleteField(fieldId, url, options) {
512 function observeAutocompleteField(fieldId, url, options) {
513 $(document).ready(function() {
513 $(document).ready(function() {
514 $('#'+fieldId).autocomplete($.extend({
514 $('#'+fieldId).autocomplete($.extend({
515 source: url,
515 source: url,
516 minLength: 2,
516 minLength: 2,
517 search: function(){$('#'+fieldId).addClass('ajax-loading');},
517 search: function(){$('#'+fieldId).addClass('ajax-loading');},
518 response: function(){$('#'+fieldId).removeClass('ajax-loading');}
518 response: function(){$('#'+fieldId).removeClass('ajax-loading');}
519 }, options));
519 }, options));
520 $('#'+fieldId).addClass('autocomplete');
520 $('#'+fieldId).addClass('autocomplete');
521 });
521 });
522 }
522 }
523
523
524 function observeSearchfield(fieldId, targetId, url) {
524 function observeSearchfield(fieldId, targetId, url) {
525 $('#'+fieldId).each(function() {
525 $('#'+fieldId).each(function() {
526 var $this = $(this);
526 var $this = $(this);
527 $this.addClass('autocomplete');
527 $this.addClass('autocomplete');
528 $this.attr('data-value-was', $this.val());
528 $this.attr('data-value-was', $this.val());
529 var check = function() {
529 var check = function() {
530 var val = $this.val();
530 var val = $this.val();
531 if ($this.attr('data-value-was') != val){
531 if ($this.attr('data-value-was') != val){
532 $this.attr('data-value-was', val);
532 $this.attr('data-value-was', val);
533 $.ajax({
533 $.ajax({
534 url: url,
534 url: url,
535 type: 'get',
535 type: 'get',
536 data: {q: $this.val()},
536 data: {q: $this.val()},
537 success: function(data){ if(targetId) $('#'+targetId).html(data); },
537 success: function(data){ if(targetId) $('#'+targetId).html(data); },
538 beforeSend: function(){ $this.addClass('ajax-loading'); },
538 beforeSend: function(){ $this.addClass('ajax-loading'); },
539 complete: function(){ $this.removeClass('ajax-loading'); }
539 complete: function(){ $this.removeClass('ajax-loading'); }
540 });
540 });
541 }
541 }
542 };
542 };
543 var reset = function() {
543 var reset = function() {
544 if (timer) {
544 if (timer) {
545 clearInterval(timer);
545 clearInterval(timer);
546 timer = setInterval(check, 300);
546 timer = setInterval(check, 300);
547 }
547 }
548 };
548 };
549 var timer = setInterval(check, 300);
549 var timer = setInterval(check, 300);
550 $this.bind('keyup click mousemove', reset);
550 $this.bind('keyup click mousemove', reset);
551 });
551 });
552 }
552 }
553
553
554 function beforeShowDatePicker(input, inst) {
554 function beforeShowDatePicker(input, inst) {
555 var default_date = null;
555 var default_date = null;
556 switch ($(input).attr("id")) {
556 switch ($(input).attr("id")) {
557 case "issue_start_date" :
557 case "issue_start_date" :
558 if ($("#issue_due_date").size() > 0) {
558 if ($("#issue_due_date").size() > 0) {
559 default_date = $("#issue_due_date").val();
559 default_date = $("#issue_due_date").val();
560 }
560 }
561 break;
561 break;
562 case "issue_due_date" :
562 case "issue_due_date" :
563 if ($("#issue_start_date").size() > 0) {
563 if ($("#issue_start_date").size() > 0) {
564 default_date = $("#issue_start_date").val();
564 var start_date = $("#issue_start_date").val();
565 if (start_date != "") {
566 start_date = new Date(Date.parse(start_date));
567 if (start_date > new Date()) {
568 default_date = $("#issue_start_date").val();
569 }
570 }
565 }
571 }
566 break;
572 break;
567 }
573 }
568 $(input).datepicker("option", "defaultDate", default_date);
574 $(input).datepicker("option", "defaultDate", default_date);
569 }
575 }
570
576
571 function initMyPageSortable(list, url) {
577 function initMyPageSortable(list, url) {
572 $('#list-'+list).sortable({
578 $('#list-'+list).sortable({
573 connectWith: '.block-receiver',
579 connectWith: '.block-receiver',
574 tolerance: 'pointer',
580 tolerance: 'pointer',
575 update: function(){
581 update: function(){
576 $.ajax({
582 $.ajax({
577 url: url,
583 url: url,
578 type: 'post',
584 type: 'post',
579 data: {'blocks': $.map($('#list-'+list).children(), function(el){return $(el).attr('id');})}
585 data: {'blocks': $.map($('#list-'+list).children(), function(el){return $(el).attr('id');})}
580 });
586 });
581 }
587 }
582 });
588 });
583 $("#list-top, #list-left, #list-right").disableSelection();
589 $("#list-top, #list-left, #list-right").disableSelection();
584 }
590 }
585
591
586 var warnLeavingUnsavedMessage;
592 var warnLeavingUnsavedMessage;
587 function warnLeavingUnsaved(message) {
593 function warnLeavingUnsaved(message) {
588 warnLeavingUnsavedMessage = message;
594 warnLeavingUnsavedMessage = message;
589 $(document).on('submit', 'form', function(){
595 $(document).on('submit', 'form', function(){
590 $('textarea').removeData('changed');
596 $('textarea').removeData('changed');
591 });
597 });
592 $(document).on('change', 'textarea', function(){
598 $(document).on('change', 'textarea', function(){
593 $(this).data('changed', 'changed');
599 $(this).data('changed', 'changed');
594 });
600 });
595 window.onbeforeunload = function(){
601 window.onbeforeunload = function(){
596 var warn = false;
602 var warn = false;
597 $('textarea').blur().each(function(){
603 $('textarea').blur().each(function(){
598 if ($(this).data('changed')) {
604 if ($(this).data('changed')) {
599 warn = true;
605 warn = true;
600 }
606 }
601 });
607 });
602 if (warn) {return warnLeavingUnsavedMessage;}
608 if (warn) {return warnLeavingUnsavedMessage;}
603 };
609 };
604 }
610 }
605
611
606 function setupAjaxIndicator() {
612 function setupAjaxIndicator() {
607 $(document).bind('ajaxSend', function(event, xhr, settings) {
613 $(document).bind('ajaxSend', function(event, xhr, settings) {
608 if ($('.ajax-loading').length === 0 && settings.contentType != 'application/octet-stream') {
614 if ($('.ajax-loading').length === 0 && settings.contentType != 'application/octet-stream') {
609 $('#ajax-indicator').show();
615 $('#ajax-indicator').show();
610 }
616 }
611 });
617 });
612 $(document).bind('ajaxStop', function() {
618 $(document).bind('ajaxStop', function() {
613 $('#ajax-indicator').hide();
619 $('#ajax-indicator').hide();
614 });
620 });
615 }
621 }
616
622
617 function hideOnLoad() {
623 function hideOnLoad() {
618 $('.hol').hide();
624 $('.hol').hide();
619 }
625 }
620
626
621 function addFormObserversForDoubleSubmit() {
627 function addFormObserversForDoubleSubmit() {
622 $('form[method=post]').each(function() {
628 $('form[method=post]').each(function() {
623 if (!$(this).hasClass('multiple-submit')) {
629 if (!$(this).hasClass('multiple-submit')) {
624 $(this).submit(function(form_submission) {
630 $(this).submit(function(form_submission) {
625 if ($(form_submission.target).attr('data-submitted')) {
631 if ($(form_submission.target).attr('data-submitted')) {
626 form_submission.preventDefault();
632 form_submission.preventDefault();
627 } else {
633 } else {
628 $(form_submission.target).attr('data-submitted', true);
634 $(form_submission.target).attr('data-submitted', true);
629 }
635 }
630 });
636 });
631 }
637 }
632 });
638 });
633 }
639 }
634
640
635 function defaultFocus(){
641 function defaultFocus(){
636 if (($('#content :focus').length == 0) && (window.location.hash == '')) {
642 if (($('#content :focus').length == 0) && (window.location.hash == '')) {
637 $('#content input[type=text], #content textarea').first().focus();
643 $('#content input[type=text], #content textarea').first().focus();
638 }
644 }
639 }
645 }
640
646
641 function blockEventPropagation(event) {
647 function blockEventPropagation(event) {
642 event.stopPropagation();
648 event.stopPropagation();
643 event.preventDefault();
649 event.preventDefault();
644 }
650 }
645
651
646 function toggleDisabledOnChange() {
652 function toggleDisabledOnChange() {
647 var checked = $(this).is(':checked');
653 var checked = $(this).is(':checked');
648 $($(this).data('disables')).attr('disabled', checked);
654 $($(this).data('disables')).attr('disabled', checked);
649 $($(this).data('enables')).attr('disabled', !checked);
655 $($(this).data('enables')).attr('disabled', !checked);
650 }
656 }
651 function toggleDisabledInit() {
657 function toggleDisabledInit() {
652 $('input[data-disables], input[data-enables]').each(toggleDisabledOnChange);
658 $('input[data-disables], input[data-enables]').each(toggleDisabledOnChange);
653 }
659 }
654 $(document).ready(function(){
660 $(document).ready(function(){
655 $('#content').on('change', 'input[data-disables], input[data-enables]', toggleDisabledOnChange);
661 $('#content').on('change', 'input[data-disables], input[data-enables]', toggleDisabledOnChange);
656 toggleDisabledInit();
662 toggleDisabledInit();
657 });
663 });
658
664
659 function keepAnchorOnSignIn(form){
665 function keepAnchorOnSignIn(form){
660 var hash = decodeURIComponent(self.document.location.hash);
666 var hash = decodeURIComponent(self.document.location.hash);
661 if (hash) {
667 if (hash) {
662 if (hash.indexOf("#") === -1) {
668 if (hash.indexOf("#") === -1) {
663 hash = "#" + hash;
669 hash = "#" + hash;
664 }
670 }
665 form.action = form.action + hash;
671 form.action = form.action + hash;
666 }
672 }
667 return true;
673 return true;
668 }
674 }
669
675
670 $(document).ready(setupAjaxIndicator);
676 $(document).ready(setupAjaxIndicator);
671 $(document).ready(hideOnLoad);
677 $(document).ready(hideOnLoad);
672 $(document).ready(addFormObserversForDoubleSubmit);
678 $(document).ready(addFormObserversForDoubleSubmit);
673 $(document).ready(defaultFocus);
679 $(document).ready(defaultFocus);
@@ -1,278 +1,293
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../base', __FILE__)
18 require File.expand_path('../base', __FILE__)
19
19
20 class Redmine::UiTest::IssuesTest < Redmine::UiTest::Base
20 class Redmine::UiTest::IssuesTest < Redmine::UiTest::Base
21 fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles,
21 fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles,
22 :trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues,
22 :trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues,
23 :enumerations, :custom_fields, :custom_values, :custom_fields_trackers,
23 :enumerations, :custom_fields, :custom_values, :custom_fields_trackers,
24 :watchers, :journals, :journal_details
24 :watchers, :journals, :journal_details
25
25
26 def test_create_issue
26 def test_create_issue
27 log_user('jsmith', 'jsmith')
27 log_user('jsmith', 'jsmith')
28 visit '/projects/ecookbook/issues/new'
28 visit '/projects/ecookbook/issues/new'
29 within('form#issue-form') do
29 within('form#issue-form') do
30 select 'Bug', :from => 'Tracker'
30 select 'Bug', :from => 'Tracker'
31 select 'Low', :from => 'Priority'
31 select 'Low', :from => 'Priority'
32 fill_in 'Subject', :with => 'new test issue'
32 fill_in 'Subject', :with => 'new test issue'
33 fill_in 'Description', :with => 'new issue'
33 fill_in 'Description', :with => 'new issue'
34 select '0 %', :from => 'Done'
34 select '0 %', :from => 'Done'
35 fill_in 'Due date', :with => ''
35 fill_in 'Due date', :with => ''
36 fill_in 'Searchable field', :with => 'Value for field 2'
36 fill_in 'Searchable field', :with => 'Value for field 2'
37 # click_button 'Create' would match both 'Create' and 'Create and continue' buttons
37 # click_button 'Create' would match both 'Create' and 'Create and continue' buttons
38 find('input[name=commit]').click
38 find('input[name=commit]').click
39 end
39 end
40
40
41 # find created issue
41 # find created issue
42 issue = Issue.find_by_subject("new test issue")
42 issue = Issue.find_by_subject("new test issue")
43 assert_kind_of Issue, issue
43 assert_kind_of Issue, issue
44
44
45 # check redirection
45 # check redirection
46 find 'div#flash_notice', :visible => true, :text => "Issue \##{issue.id} created."
46 find 'div#flash_notice', :visible => true, :text => "Issue \##{issue.id} created."
47 assert_equal issue_path(:id => issue), current_path
47 assert_equal issue_path(:id => issue), current_path
48
48
49 # check issue attributes
49 # check issue attributes
50 assert_equal 'jsmith', issue.author.login
50 assert_equal 'jsmith', issue.author.login
51 assert_equal 1, issue.project.id
51 assert_equal 1, issue.project.id
52 assert_equal IssueStatus.find_by_name('New'), issue.status
52 assert_equal IssueStatus.find_by_name('New'), issue.status
53 assert_equal Tracker.find_by_name('Bug'), issue.tracker
53 assert_equal Tracker.find_by_name('Bug'), issue.tracker
54 assert_equal IssuePriority.find_by_name('Low'), issue.priority
54 assert_equal IssuePriority.find_by_name('Low'), issue.priority
55 assert_equal 'Value for field 2', issue.custom_field_value(CustomField.find_by_name('Searchable field'))
55 assert_equal 'Value for field 2', issue.custom_field_value(CustomField.find_by_name('Searchable field'))
56 end
56 end
57
57
58 def test_create_issue_with_form_update
58 def test_create_issue_with_form_update
59 field1 = IssueCustomField.create!(
59 field1 = IssueCustomField.create!(
60 :field_format => 'string',
60 :field_format => 'string',
61 :name => 'Field1',
61 :name => 'Field1',
62 :is_for_all => true,
62 :is_for_all => true,
63 :trackers => Tracker.where(:id => [1, 2])
63 :trackers => Tracker.where(:id => [1, 2])
64 )
64 )
65 field2 = IssueCustomField.create!(
65 field2 = IssueCustomField.create!(
66 :field_format => 'string',
66 :field_format => 'string',
67 :name => 'Field2',
67 :name => 'Field2',
68 :is_for_all => true,
68 :is_for_all => true,
69 :trackers => Tracker.where(:id => 2)
69 :trackers => Tracker.where(:id => 2)
70 )
70 )
71
71
72 Role.non_member.add_permission! :add_issues
72 Role.non_member.add_permission! :add_issues
73 Role.non_member.remove_permission! :edit_issues, :add_issue_notes
73 Role.non_member.remove_permission! :edit_issues, :add_issue_notes
74
74
75 log_user('someone', 'foo')
75 log_user('someone', 'foo')
76 visit '/projects/ecookbook/issues/new'
76 visit '/projects/ecookbook/issues/new'
77 assert page.has_no_content?(field2.name)
77 assert page.has_no_content?(field2.name)
78 assert page.has_content?(field1.name)
78 assert page.has_content?(field1.name)
79
79
80 fill_in 'Subject', :with => 'New test issue'
80 fill_in 'Subject', :with => 'New test issue'
81 fill_in 'Description', :with => 'New test issue description'
81 fill_in 'Description', :with => 'New test issue description'
82 fill_in field1.name, :with => 'CF1 value'
82 fill_in field1.name, :with => 'CF1 value'
83 select 'Low', :from => 'Priority'
83 select 'Low', :from => 'Priority'
84
84
85 # field2 should show up when changing tracker
85 # field2 should show up when changing tracker
86 select 'Feature request', :from => 'Tracker'
86 select 'Feature request', :from => 'Tracker'
87 assert page.has_content?(field2.name)
87 assert page.has_content?(field2.name)
88 assert page.has_content?(field1.name)
88 assert page.has_content?(field1.name)
89
89
90 fill_in field2.name, :with => 'CF2 value'
90 fill_in field2.name, :with => 'CF2 value'
91 assert_difference 'Issue.count' do
91 assert_difference 'Issue.count' do
92 page.first(:button, 'Create').click
92 page.first(:button, 'Create').click
93 end
93 end
94
94
95 issue = Issue.order('id desc').first
95 issue = Issue.order('id desc').first
96 assert_equal 'New test issue', issue.subject
96 assert_equal 'New test issue', issue.subject
97 assert_equal 'New test issue description', issue.description
97 assert_equal 'New test issue description', issue.description
98 assert_equal 'Low', issue.priority.name
98 assert_equal 'Low', issue.priority.name
99 assert_equal 'CF1 value', issue.custom_field_value(field1)
99 assert_equal 'CF1 value', issue.custom_field_value(field1)
100 assert_equal 'CF2 value', issue.custom_field_value(field2)
100 assert_equal 'CF2 value', issue.custom_field_value(field2)
101 end
101 end
102
102
103 def test_create_issue_with_watchers
103 def test_create_issue_with_watchers
104 user = User.generate!(:firstname => 'Some', :lastname => 'Watcher')
104 user = User.generate!(:firstname => 'Some', :lastname => 'Watcher')
105 assert_equal 'Some Watcher', user.name
105 assert_equal 'Some Watcher', user.name
106 log_user('jsmith', 'jsmith')
106 log_user('jsmith', 'jsmith')
107 visit '/projects/ecookbook/issues/new'
107 visit '/projects/ecookbook/issues/new'
108 fill_in 'Subject', :with => 'Issue with watchers'
108 fill_in 'Subject', :with => 'Issue with watchers'
109 # Add a project member as watcher
109 # Add a project member as watcher
110 check 'Dave Lopper'
110 check 'Dave Lopper'
111 # Search for another user
111 # Search for another user
112 assert page.has_no_css?('form#new-watcher-form')
112 assert page.has_no_css?('form#new-watcher-form')
113 assert page.has_no_content?('Some Watcher')
113 assert page.has_no_content?('Some Watcher')
114 click_link 'Search for watchers to add'
114 click_link 'Search for watchers to add'
115 within('form#new-watcher-form') do
115 within('form#new-watcher-form') do
116 fill_in 'user_search', :with => 'watch'
116 fill_in 'user_search', :with => 'watch'
117 assert page.has_content?('Some Watcher')
117 assert page.has_content?('Some Watcher')
118 check 'Some Watcher'
118 check 'Some Watcher'
119 click_button 'Add'
119 click_button 'Add'
120 end
120 end
121 assert page.has_css?('form#issue-form')
121 assert page.has_css?('form#issue-form')
122 assert page.has_css?('p#watchers_form')
122 assert page.has_css?('p#watchers_form')
123 using_wait_time(30) do
123 using_wait_time(30) do
124 within('span#watchers_inputs') do
124 within('span#watchers_inputs') do
125 within("label#issue_watcher_user_ids_#{user.id}") do
125 within("label#issue_watcher_user_ids_#{user.id}") do
126 assert has_content?('Some Watcher'), "No watcher content"
126 assert has_content?('Some Watcher'), "No watcher content"
127 end
127 end
128 end
128 end
129 end
129 end
130 assert_difference 'Issue.count' do
130 assert_difference 'Issue.count' do
131 find('input[name=commit]').click
131 find('input[name=commit]').click
132 end
132 end
133
133
134 issue = Issue.order('id desc').first
134 issue = Issue.order('id desc').first
135 assert_equal ['Dave Lopper', 'Some Watcher'], issue.watcher_users.map(&:name).sort
135 assert_equal ['Dave Lopper', 'Some Watcher'], issue.watcher_users.map(&:name).sort
136 end
136 end
137
137
138 def test_create_issue_start_due_date
138 def test_create_issue_start_due_date
139 with_settings :default_issue_start_date_to_creation_date => 0 do
139 with_settings :default_issue_start_date_to_creation_date => 0 do
140 log_user('jsmith', 'jsmith')
140 log_user('jsmith', 'jsmith')
141 visit '/projects/ecookbook/issues/new'
141 visit '/projects/ecookbook/issues/new'
142 assert_equal "", page.find('input#issue_start_date').value
142 assert_equal "", page.find('input#issue_start_date').value
143 assert_equal "", page.find('input#issue_due_date').value
143 assert_equal "", page.find('input#issue_due_date').value
144 page.first('p#start_date_area img').click
144 page.first('p#start_date_area img').click
145 page.first("td.ui-datepicker-days-cell-over a").click
145 page.first("td.ui-datepicker-days-cell-over a").click
146 assert_equal Date.today.to_s, page.find('input#issue_start_date').value
146 assert_equal Date.today.to_s, page.find('input#issue_start_date').value
147 page.first('p#due_date_area img').click
147 page.first('p#due_date_area img').click
148 page.first("td.ui-datepicker-days-cell-over a").click
148 page.first("td.ui-datepicker-days-cell-over a").click
149 assert_equal Date.today.to_s, page.find('input#issue_due_date').value
149 assert_equal Date.today.to_s, page.find('input#issue_due_date').value
150 end
150 end
151 end
151 end
152
152
153 def test_create_issue_start_due_date_default
153 def test_default_due_date_proposed_in_date_picker
154 log_user('jsmith', 'jsmith')
154 log_user('jsmith', 'jsmith')
155 visit '/projects/ecookbook/issues/new'
155 visit '/projects/ecookbook/issues/new'
156
157 # Future start date: due date should default to start date
158 fill_in 'Start date', :with => '2027-04-01'
159 fill_in 'Due date', :with => ''
160 page.first('p#due_date_area img').click
161 page.first("td.ui-datepicker-days-cell-over a").click
162 assert_equal '2027-04-01', page.find('input#issue_due_date').value
163
164 # Passed start date: due date should default to today
156 fill_in 'Start date', :with => '2012-04-01'
165 fill_in 'Start date', :with => '2012-04-01'
157 fill_in 'Due date', :with => ''
166 fill_in 'Due date', :with => ''
158 page.first('p#due_date_area img').click
167 page.first('p#due_date_area img').click
159 page.first("td.ui-datepicker-days-cell-over a").click
168 page.first("td.ui-datepicker-days-cell-over a").click
160 assert_equal '2012-04-01', page.find('input#issue_due_date').value
169 assert_equal Date.today.to_s, page.find('input#issue_due_date').value
170 end
171
172 def test_default_start_date_proposed_in_date_picker
173 log_user('jsmith', 'jsmith')
174 visit '/projects/ecookbook/issues/new'
161
175
176 # Passed due date: start date should default to due date
162 fill_in 'Start date', :with => ''
177 fill_in 'Start date', :with => ''
163 fill_in 'Due date', :with => '2012-04-01'
178 fill_in 'Due date', :with => '2012-04-01'
164 page.first('p#start_date_area img').click
179 page.first('p#start_date_area img').click
165 page.first("td.ui-datepicker-days-cell-over a").click
180 page.first("td.ui-datepicker-days-cell-over a").click
166 assert_equal '2012-04-01', page.find('input#issue_start_date').value
181 assert_equal '2012-04-01', page.find('input#issue_start_date').value
167 end
182 end
168
183
169 def test_preview_issue_description
184 def test_preview_issue_description
170 log_user('jsmith', 'jsmith')
185 log_user('jsmith', 'jsmith')
171 visit '/projects/ecookbook/issues/new'
186 visit '/projects/ecookbook/issues/new'
172 within('form#issue-form') do
187 within('form#issue-form') do
173 fill_in 'Subject', :with => 'new issue subject'
188 fill_in 'Subject', :with => 'new issue subject'
174 fill_in 'Description', :with => 'new issue description'
189 fill_in 'Description', :with => 'new issue description'
175 click_link 'Preview'
190 click_link 'Preview'
176 end
191 end
177 find 'div#preview fieldset', :visible => true, :text => 'new issue description'
192 find 'div#preview fieldset', :visible => true, :text => 'new issue description'
178 assert_difference 'Issue.count' do
193 assert_difference 'Issue.count' do
179 find('input[name=commit]').click
194 find('input[name=commit]').click
180 end
195 end
181
196
182 issue = Issue.order('id desc').first
197 issue = Issue.order('id desc').first
183 assert_equal 'new issue description', issue.description
198 assert_equal 'new issue description', issue.description
184 end
199 end
185
200
186 def test_update_issue_with_form_update
201 def test_update_issue_with_form_update
187 field = IssueCustomField.create!(
202 field = IssueCustomField.create!(
188 :field_format => 'string',
203 :field_format => 'string',
189 :name => 'Form update CF',
204 :name => 'Form update CF',
190 :is_for_all => true,
205 :is_for_all => true,
191 :trackers => Tracker.where(:name => 'Feature request')
206 :trackers => Tracker.where(:name => 'Feature request')
192 )
207 )
193
208
194 Role.non_member.add_permission! :edit_issues
209 Role.non_member.add_permission! :edit_issues
195 Role.non_member.remove_permission! :add_issues, :add_issue_notes
210 Role.non_member.remove_permission! :add_issues, :add_issue_notes
196
211
197 log_user('someone', 'foo')
212 log_user('someone', 'foo')
198 visit '/issues/1'
213 visit '/issues/1'
199 assert page.has_no_content?('Form update CF')
214 assert page.has_no_content?('Form update CF')
200
215
201 page.first(:link, 'Edit').click
216 page.first(:link, 'Edit').click
202 # the custom field should show up when changing tracker
217 # the custom field should show up when changing tracker
203 select 'Feature request', :from => 'Tracker'
218 select 'Feature request', :from => 'Tracker'
204 assert page.has_content?('Form update CF')
219 assert page.has_content?('Form update CF')
205
220
206 fill_in 'Form update', :with => 'CF value'
221 fill_in 'Form update', :with => 'CF value'
207 assert_no_difference 'Issue.count' do
222 assert_no_difference 'Issue.count' do
208 page.first(:button, 'Submit').click
223 page.first(:button, 'Submit').click
209 end
224 end
210
225
211 issue = Issue.find(1)
226 issue = Issue.find(1)
212 assert_equal 'CF value', issue.custom_field_value(field)
227 assert_equal 'CF value', issue.custom_field_value(field)
213 end
228 end
214
229
215 def test_remove_issue_watcher_from_sidebar
230 def test_remove_issue_watcher_from_sidebar
216 user = User.find(3)
231 user = User.find(3)
217 Watcher.create!(:watchable => Issue.find(1), :user => user)
232 Watcher.create!(:watchable => Issue.find(1), :user => user)
218
233
219 log_user('jsmith', 'jsmith')
234 log_user('jsmith', 'jsmith')
220 visit '/issues/1'
235 visit '/issues/1'
221 assert page.first('#sidebar').has_content?('Watchers (1)')
236 assert page.first('#sidebar').has_content?('Watchers (1)')
222 assert page.first('#sidebar').has_content?(user.name)
237 assert page.first('#sidebar').has_content?(user.name)
223 assert_difference 'Watcher.count', -1 do
238 assert_difference 'Watcher.count', -1 do
224 page.first('ul.watchers .user-3 a.delete').click
239 page.first('ul.watchers .user-3 a.delete').click
225 assert page.first('#sidebar').has_content?('Watchers (0)')
240 assert page.first('#sidebar').has_content?('Watchers (0)')
226 end
241 end
227 assert page.first('#sidebar').has_no_content?(user.name)
242 assert page.first('#sidebar').has_no_content?(user.name)
228 end
243 end
229
244
230 def test_watch_should_update_watchers_list
245 def test_watch_should_update_watchers_list
231 user = User.find(2)
246 user = User.find(2)
232 log_user('jsmith', 'jsmith')
247 log_user('jsmith', 'jsmith')
233 visit '/issues/1'
248 visit '/issues/1'
234 assert page.first('#sidebar').has_content?('Watchers (0)')
249 assert page.first('#sidebar').has_content?('Watchers (0)')
235
250
236 page.first('a.issue-1-watcher').click
251 page.first('a.issue-1-watcher').click
237 assert page.first('#sidebar').has_content?('Watchers (1)')
252 assert page.first('#sidebar').has_content?('Watchers (1)')
238 assert page.first('#sidebar').has_content?(user.name)
253 assert page.first('#sidebar').has_content?(user.name)
239 end
254 end
240
255
241 def test_watch_issue_via_context_menu
256 def test_watch_issue_via_context_menu
242 log_user('jsmith', 'jsmith')
257 log_user('jsmith', 'jsmith')
243 visit '/issues'
258 visit '/issues'
244 assert page.has_css?('tr#issue-1')
259 assert page.has_css?('tr#issue-1')
245 find('tr#issue-1 td.updated_on').click
260 find('tr#issue-1 td.updated_on').click
246 page.execute_script "$('tr#issue-1 td.updated_on').trigger('contextmenu');"
261 page.execute_script "$('tr#issue-1 td.updated_on').trigger('contextmenu');"
247 assert_difference 'Watcher.count' do
262 assert_difference 'Watcher.count' do
248 within('#context-menu') do
263 within('#context-menu') do
249 click_link 'Watch'
264 click_link 'Watch'
250 end
265 end
251 # wait for ajax response
266 # wait for ajax response
252 assert page.has_css?('#context-menu .issue-1-watcher.icon-fav')
267 assert page.has_css?('#context-menu .issue-1-watcher.icon-fav')
253 assert page.has_css?('tr#issue-1')
268 assert page.has_css?('tr#issue-1')
254 end
269 end
255 assert Issue.find(1).watched_by?(User.find_by_login('jsmith'))
270 assert Issue.find(1).watched_by?(User.find_by_login('jsmith'))
256 end
271 end
257
272
258 def test_bulk_watch_issues_via_context_menu
273 def test_bulk_watch_issues_via_context_menu
259 log_user('jsmith', 'jsmith')
274 log_user('jsmith', 'jsmith')
260 visit '/issues'
275 visit '/issues'
261 assert page.has_css?('tr#issue-1')
276 assert page.has_css?('tr#issue-1')
262 assert page.has_css?('tr#issue-4')
277 assert page.has_css?('tr#issue-4')
263 find('tr#issue-1 input[type=checkbox]').click
278 find('tr#issue-1 input[type=checkbox]').click
264 find('tr#issue-4 input[type=checkbox]').click
279 find('tr#issue-4 input[type=checkbox]').click
265 page.execute_script "$('tr#issue-1 td.updated_on').trigger('contextmenu');"
280 page.execute_script "$('tr#issue-1 td.updated_on').trigger('contextmenu');"
266 assert_difference 'Watcher.count', 2 do
281 assert_difference 'Watcher.count', 2 do
267 within('#context-menu') do
282 within('#context-menu') do
268 click_link 'Watch'
283 click_link 'Watch'
269 end
284 end
270 # wait for ajax response
285 # wait for ajax response
271 assert page.has_css?('#context-menu .issue-bulk-watcher.icon-fav')
286 assert page.has_css?('#context-menu .issue-bulk-watcher.icon-fav')
272 assert page.has_css?('tr#issue-1')
287 assert page.has_css?('tr#issue-1')
273 assert page.has_css?('tr#issue-4')
288 assert page.has_css?('tr#issue-4')
274 end
289 end
275 assert Issue.find(1).watched_by?(User.find_by_login('jsmith'))
290 assert Issue.find(1).watched_by?(User.find_by_login('jsmith'))
276 assert Issue.find(4).watched_by?(User.find_by_login('jsmith'))
291 assert Issue.find(4).watched_by?(User.find_by_login('jsmith'))
277 end
292 end
278 end
293 end
General Comments 0
You need to be logged in to leave comments. Login now