5055 lines
213 KiB
JavaScript
5055 lines
213 KiB
JavaScript
odoo.define('web_gantt.tests', function (require) {
|
|
'use strict';
|
|
|
|
const { browser } = require("@web/core/browser/browser");
|
|
const Domain = require('web.Domain');
|
|
var GanttView = require('web_gantt.GanttView');
|
|
var GanttRenderer = require('web_gantt.GanttRenderer');
|
|
var GanttRow = require('web_gantt.GanttRow');
|
|
const SampleServer = require('web.SampleServer');
|
|
const session = require("web.session");
|
|
var testUtils = require('web.test_utils');
|
|
const { createWebClient, doAction } = require('@web/../tests/webclient/helpers');
|
|
const { registerCleanup } = require("@web/../tests/helpers/cleanup");
|
|
const { click, editInput, getFixture, patchTimeZone, patchWithCleanup } = require('@web/../tests/helpers/utils');
|
|
const { prepareWowlFormViewDialogs } = require("@web/../tests/views/helpers");
|
|
|
|
const patchDate = testUtils.mock.patchDate;
|
|
const nextTick = testUtils.nextTick;
|
|
|
|
var initialDate = new Date(2018, 11, 20, 8, 0, 0);
|
|
initialDate = new Date(initialDate.getTime() - initialDate.getTimezoneOffset() * 60 * 1000);
|
|
|
|
const { toggleFilterMenu, toggleMenuItem } = require("@web/../tests/search/helpers");
|
|
|
|
const { markup } = owl;
|
|
|
|
const FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
// This function is used to be sure that the unavailabilities will be displayed
|
|
// where we want (independently of the timezone used when lauching the tests)
|
|
function convertUnavailability(u) {
|
|
return {
|
|
start: moment(u.start).utc().format(FORMAT),
|
|
stop: moment(u.stop).utc().format(FORMAT)
|
|
};
|
|
}
|
|
|
|
async function createView(params) {
|
|
testUtils.mock.patch(SampleServer.prototype, {
|
|
/**
|
|
* Sometimes, it can happen that none of the generated dates is in
|
|
* the interval. To fix that, we simply return the initial date.
|
|
*/
|
|
_getRandomDate(format) {
|
|
return moment(params.viewOptions.initialDate).format(format);
|
|
},
|
|
});
|
|
const view = await testUtils.createView(...arguments);
|
|
const oldDestroy = view.destroy;
|
|
view.destroy = function () {
|
|
oldDestroy.call(this, ...arguments);
|
|
testUtils.mock.unpatch(SampleServer.prototype);
|
|
};
|
|
return view;
|
|
}
|
|
|
|
// The gantt view uses momentjs for all time computation, which bypasses
|
|
// tzOffset, making it hard to test. We had two solutions to test this:
|
|
//
|
|
// 1. Change everywhere in gantt where we use momentjs to manual
|
|
// manipulations using tzOffset so that we can manipulate it tests.
|
|
// Pros:
|
|
// - Consistent with other mechanisms in Odoo.
|
|
// - Full coverage of the behavior since we can manipulate in tests
|
|
// Cons:
|
|
// - We need to change nearly everything everywhere in gantt
|
|
// - The code works, it is sad to have to change it, risking to
|
|
// introduce new bugs, just to be able to test this.
|
|
// - Just applying the tzOffset to the date is not as easy as it
|
|
// sounds. Moment is smart. It offsets the date with the offset
|
|
// that you would locally have AT THAT DATE. Meaning that it
|
|
// sometimes offsets of 1 hour or 2 hour in the same locale
|
|
// depending as if the particular datetime we are offseting would
|
|
// be in DST or not at that time. We would have to handle all
|
|
// the DST conversion shenanigans manually to be correct.
|
|
//
|
|
// 2. Use the same computation path as the production code to compute
|
|
// the expected value.
|
|
// Pros:
|
|
// - Very easy to implement
|
|
// - Momentjs is smart, see last Con above. It does all the heavy
|
|
// lifting for us and it is a well known, stable and maintained
|
|
// library, so we can trust it on these matters.
|
|
// Cons:
|
|
// - The test relies on the behavior of Momentjs. If the library
|
|
// has a bug, the gantt view will have an issue that this test
|
|
// will never be able to see.
|
|
//
|
|
// Considering the Cons of the first option are tremendous and the one
|
|
// of the second option is offest by the fact that we consider Momentjs
|
|
// to be a trustworthy library, we chose option 2. It was required in
|
|
// only a few test but we think it was still interesting to mention it.
|
|
|
|
function getPillItemWidth($el) {
|
|
return $el.attr('style').split('width: ')[1].split(';')[0];
|
|
}
|
|
|
|
QUnit.module('LegacyViews', {
|
|
beforeEach: function () {
|
|
// Avoid animation to not have to wait until the tooltip is removed
|
|
this.initialPopoverDefaultAnimation = Popover.Default.animation;
|
|
Popover.Default.animation = false;
|
|
|
|
this.data = {
|
|
tasks: {
|
|
fields: {
|
|
id: {string: 'ID', type: 'integer'},
|
|
name: {string: 'Name', type: 'char'},
|
|
start: {string: 'Start Date', type: 'datetime'},
|
|
stop: {string: 'Stop Date', type: 'datetime'},
|
|
time: {string: "Time", type: "float"},
|
|
stage: {string: 'Stage', type: 'selection', selection: [['todo', 'To Do'], ['in_progress', 'In Progress'], ['done', 'Done'], ['cancel', 'Cancelled']]},
|
|
project_id: {string: 'Project', type: 'many2one', relation: 'projects'},
|
|
user_id: {string: 'Assign To', type: 'many2one', relation: 'users'},
|
|
color: {string: 'Color', type: 'integer'},
|
|
progress: {string: 'Progress', type: 'integer'},
|
|
exclude: {string: 'Excluded from Consolidation', type: 'boolean'},
|
|
stage_id: {string: "Stage", type: "many2one", relation: 'stage'}
|
|
},
|
|
records: [
|
|
{ id: 1, name: 'Task 1', start: '2018-11-30 18:30:00', stop: '2018-12-31 18:29:59', stage: 'todo', stage_id: 1, project_id: 1, user_id: 1, color: 0, progress: 0},
|
|
{ id: 2, name: 'Task 2', start: '2018-12-17 11:30:00', stop: '2018-12-22 06:29:59', stage: 'done', stage_id: 4, project_id: 1, user_id: 2, color: 2, progress: 30},
|
|
{ id: 3, name: 'Task 3', start: '2018-12-27 06:30:00', stop: '2019-01-03 06:29:59', stage: 'cancel', stage_id: 3, project_id: 1, user_id: 2, color: 10, progress: 60},
|
|
{ id: 4, name: 'Task 4', start: '2018-12-19 22:30:00', stop: '2018-12-20 06:29:59', stage: 'in_progress', stage_id: 3, project_id: 1, user_id: 1, color: 1, progress: false, exclude: 0},
|
|
{ id: 5, name: 'Task 5', start: '2018-11-08 01:53:10', stop: '2018-12-04 01:34:34', stage: 'done', stage_id: 2, project_id: 2, user_id: 1, color: 2, progress: 100, exclude: 1},
|
|
{ id: 6, name: 'Task 6', start: '2018-11-19 23:00:00', stop: '2018-11-20 04:21:01', stage: 'in_progress', stage_id: 4, project_id: 2, user_id: 1, color: 1, progress: 0},
|
|
{ id: 7, name: 'Task 7', start: '2018-12-20 10:30:12', stop: '2018-12-20 18:29:59', stage: 'cancel', stage_id: 1, project_id: 2, user_id: 2, color: 10, progress: 80},
|
|
{ id: 8, name: 'Task 8', start: '2020-03-28 06:30:12', stop: '2020-03-28 18:29:59', stage: 'in_progress', stage_id: 1, project_id: 2, user_id: 2, color: 10, progress: 80},
|
|
],
|
|
},
|
|
projects: {
|
|
fields: {
|
|
id: {string: 'ID', type: 'integer'},
|
|
name: {string: 'Name', type: 'char'},
|
|
},
|
|
records: [
|
|
{id: 1, name: 'Project 1'},
|
|
{id: 2, name: 'Project 2'},
|
|
],
|
|
},
|
|
users: {
|
|
fields: {
|
|
id: {string: 'ID', type: 'integer'},
|
|
name: {string: 'Name', type: 'char'},
|
|
},
|
|
records: [
|
|
{id: 1, name: 'User 1'},
|
|
{id: 2, name: 'User 2'},
|
|
],
|
|
},
|
|
stage: {
|
|
fields: {
|
|
name: {string: "Name", type: "char"},
|
|
sequence: {string: "Sequence", type: "integer"}
|
|
},
|
|
records: [{
|
|
id: 1,
|
|
name: "in_progress",
|
|
sequence: 2,
|
|
}, {
|
|
id: 3,
|
|
name: "cancel",
|
|
sequence: 4,
|
|
}, {
|
|
id: 2,
|
|
name: "todo",
|
|
sequence: 1,
|
|
}, {
|
|
id: 4,
|
|
name: "done",
|
|
sequence: 3,
|
|
}]
|
|
},
|
|
};
|
|
patchTimeZone(0);
|
|
},
|
|
afterEach: async function() {
|
|
Popover.Default.animation = this.initialPopoverDefaultAnimation;
|
|
},
|
|
}, function () {
|
|
QUnit.module('GanttView (legacy)');
|
|
|
|
// BASIC TESTS
|
|
|
|
QUnit.test('empty ungrouped gantt rendering', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 0]],
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have a 31 slots for month view');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row',
|
|
'should have a 1 row');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('ungrouped gantt rendering', async function (assert) {
|
|
assert.expect(20);
|
|
|
|
// This is one of the few tests which have dynamic assertions, see
|
|
// our justification for it in the comment at the top of this file
|
|
|
|
var task2 = this.data.tasks.records[1];
|
|
var startDateUTCString = task2.start;
|
|
var startDateUTC = moment.utc(startDateUTCString);
|
|
var startDateLocalString = startDateUTC.local().format('DD MMM, LT');
|
|
|
|
var stopDateUTCString = task2.stop;
|
|
var stopDateUTC = moment.utc(stopDateUTCString);
|
|
var stopDateLocalString = stopDateUTC.local().format('DD MMM, LT');
|
|
|
|
var POPOVER_DELAY = GanttRow.prototype.POPOVER_DELAY;
|
|
GanttRow.prototype.POPOVER_DELAY = 0;
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (route === '/web/dataset/search_read') {
|
|
assert.strictEqual(args.model, 'tasks',
|
|
"should read on the correct model");
|
|
} else if (route === '/web/dataset/call_kw/tasks/read_group') {
|
|
throw Error("Should not call read_group when no groupby !");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
getTZOffset: function () {
|
|
return 60;
|
|
},
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'), 'active',
|
|
'month view should be activated by default');
|
|
assert.notOk(gantt.$buttons.find('.o_gantt_button_expand_rows').is(':visible'),
|
|
"the expand button should be invisible (only displayed if useful)");
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
'should contain "December 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have a 31 slots for month view');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row',
|
|
'should have a 1 row');
|
|
assert.containsNone(gantt, '.o_gantt_row_container .o_gantt_row .o_gantt_row_sidebar',
|
|
'should not have a sidebar');
|
|
assert.containsN(gantt, '.o_gantt_pill_wrapper', 6,
|
|
'should have a 6 pills');
|
|
|
|
// verify that the level offset is correctly applied (add 1px gap border compensation for each level)
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_cell[data-date="2018-12-01 00:00:00"] .o_gantt_pill_wrapper:contains(Task 5)').css('margin-top'), '2px',
|
|
'task 5 should be in first level');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_cell[data-date="2018-12-17 00:00:00"] .o_gantt_pill_wrapper:contains(Task 2)').css('margin-top'), '2px',
|
|
'task 2 should be in first level');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_cell[data-date="2018-12-27 00:00:00"] .o_gantt_pill_wrapper:contains(Task 3)').css('margin-top'), '2px',
|
|
'task 3 should be in first level');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_cell[data-date="2018-12-01 00:00:00"] .o_gantt_pill_wrapper:contains(Task 1)').css('margin-top'), GanttRow.prototype.LEVEL_TOP_OFFSET + 4 + 2 +'px',
|
|
'task 1 should be in second level');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_cell[data-date="2018-12-20 00:00:00"] .o_gantt_pill_wrapper:contains(Task 4)').css('margin-top'), 2 * (GanttRow.prototype.LEVEL_TOP_OFFSET + 4) + 2 +'px',
|
|
'task 4 should be in third level');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_cell[data-date="2018-12-20 00:00:00"] .o_gantt_pill_wrapper:contains(Task 7)').css('margin-top'), 2 * (GanttRow.prototype.LEVEL_TOP_OFFSET + 4) + 2 +'px',
|
|
'task 7 should be in third level');
|
|
|
|
// test popover and local timezone
|
|
assert.containsNone(gantt, 'div.popover', 'should not have a popover');
|
|
gantt.$('.o_gantt_pill:contains("Task 2")').trigger('mouseenter');
|
|
await testUtils.nextTick();
|
|
assert.containsOnce($, 'div.popover', 'should have a popover');
|
|
|
|
assert.strictEqual($('div.popover .flex-column span:nth-child(2)').text(), startDateLocalString,
|
|
'popover should display start date of task 2 in local time');
|
|
assert.strictEqual($('div.popover .flex-column span:nth-child(3)').text(), stopDateLocalString,
|
|
'popover should display start date of task 2 in local time');
|
|
|
|
gantt.destroy();
|
|
assert.containsNone(gantt, 'div.popover', 'should not have a popover anymore');
|
|
GanttRow.prototype.POPOVER_DELAY = POPOVER_DELAY;
|
|
});
|
|
|
|
QUnit.test('ordered gantt view', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" progress="progress"></gantt>',
|
|
groupBy: ['stage_id'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
})
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_row_title').text().replace(/\s/g, ''),
|
|
"todoin_progressdonecancel");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('empty single-level grouped gantt rendering', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
domain: [['id', '=', 0]],
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have a 31 slots for month view');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row',
|
|
'should have a 1 row');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('single-level grouped gantt rendering', async function (assert) {
|
|
assert.expect(12);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'), 'active',
|
|
'month view should by default activated');
|
|
assert.notOk(gantt.$buttons.find('.o_gantt_button_expand_rows').is(':visible'),
|
|
"the expand button should be invisible (only displayed if useful)");
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .o_gantt_row_sidebar').text().trim(), 'Tasks',
|
|
'should contain "Tasks" in header sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
'should contain "December 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have a 31 slots for month view');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row', 2,
|
|
'should have a 2 rows');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_row_sidebar',
|
|
'should have a sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_row_title').text().trim(), 'Project 1',
|
|
'should contain "Project 1" in sidebar title');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_pill_wrapper', 4,
|
|
'should have a 4 pills in first row');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:last-child .o_gantt_row_title').text().trim(), 'Project 2',
|
|
'should contain "Project 2" in sidebar title');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row:last-child .o_gantt_pill_wrapper', 2,
|
|
'should have a 2 pills in first row');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('single-level grouped gantt rendering with group_expand', async function (assert) {
|
|
assert.expect(12);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
mockRPC: function (route) {
|
|
if (route === '/web/dataset/call_kw/tasks/read_group') {
|
|
return Promise.resolve([
|
|
{ project_id: [20, "Unused Project 1"], project_id_count: 0 },
|
|
{ project_id: [50, "Unused Project 2"], project_id_count: 0 },
|
|
{ project_id: [2, "Project 2"], project_id_count: 2 },
|
|
{ project_id: [30, "Unused Project 3"], project_id_count: 0 },
|
|
{ project_id: [1, "Project 1"], project_id_count: 4 }
|
|
]);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'), 'active',
|
|
'month view should by default activated');
|
|
assert.notOk(gantt.$buttons.find('.o_gantt_button_expand_rows').is(':visible'),
|
|
"the expand button should be invisible (only displayed if useful)");
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .o_gantt_row_sidebar').text().trim(), 'Tasks',
|
|
'should contain "Tasks" in header sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
'should contain "December 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have a 31 slots for month view');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row', 5,
|
|
'should have a 5 rows');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_row_sidebar',
|
|
'should have a sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_row_title').text().trim(), 'Unused Project 1',
|
|
'should contain "Unused Project" in sidebar title');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_pill_wrapper', 0,
|
|
'should have 0 pills in first row');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:last-child .o_gantt_row_title').text().trim(), 'Project 1',
|
|
'should contain "Project 1" in sidebar title');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row:not(.o_gantt_total_row):last-child .o_gantt_pill_wrapper', 4,
|
|
'should have 4 pills in last row');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('multi-level grouped gantt rendering', async function (assert) {
|
|
assert.expect(31);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'), 'active',
|
|
'month view should by default activated');
|
|
assert.ok(gantt.$buttons.find('.o_gantt_button_expand_rows').is(':visible'),
|
|
"there should be an expand button");
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .o_gantt_row_sidebar').text().trim(), 'Tasks',
|
|
'should contain "Tasks" in header sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
'should contain "December 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have a 31 slots for month view');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row', 12,
|
|
'should have a 12 rows');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row_group.open', 6,
|
|
'should have a 6 opened groups');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row:not(.o_gantt_row_group)', 6,
|
|
'should have a 6 rows');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:first .o_gantt_row_sidebar',
|
|
'should have a sidebar');
|
|
|
|
// Check grouped rows
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:first'), 'o_gantt_row_group',
|
|
'1st row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:first .o_gantt_row_title').text().trim(), 'User 1',
|
|
'1st row title should be "User 1"');
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:nth(1)'), 'o_gantt_row_group',
|
|
'2nd row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(1) .o_gantt_row_title').text().trim(), 'Project 1',
|
|
'2nd row title should be "Project 1"');
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:nth(4)'), 'o_gantt_row_group',
|
|
'5th row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(4) .o_gantt_row_title').text().trim(), 'Project 2',
|
|
'5th row title should be "Project 2"');
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:nth(6)'), 'o_gantt_row_group',
|
|
'7th row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(6) .o_gantt_row_title').text().trim(), 'User 2',
|
|
'7th row title should be "User 2"');
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:nth(7)'), 'o_gantt_row_group',
|
|
'8th row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(7) .o_gantt_row_title').text().trim(), 'Project 1',
|
|
'8th row title should be "Project 1"');
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:nth(10)'), 'o_gantt_row_group',
|
|
'11th row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(10) .o_gantt_row_title').text().trim(), 'Project 2',
|
|
'11th row title should be "Project 2"');
|
|
|
|
// group row count and greyscale
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_consolidated_pill_title').text().replace(/\s+/g, ''), "2121",
|
|
"the count should be correctly computed");
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill:eq(0)').css('background-color'), "rgb(1, 126, 132)",
|
|
"the 1st group pill should have the correct grey scale)");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill:eq(1)').css('background-color'), "rgb(1, 126, 132)",
|
|
"the 2nd group pill should have the correct grey scale)");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill:eq(2)').css('background-color'), "rgb(1, 126, 132)",
|
|
"the 3rd group pill should have the correct grey scale");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill:eq(3)').css('background-color'), "rgb(1, 126, 132)",
|
|
"the 4th group pill should have the correct grey scale");
|
|
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(0)')), "calc(300% + 2px - 0px)",
|
|
"the 1st group pill should have the correct width (1 to 3 dec)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(1)')), "calc(1600% + 15px - 0px)",
|
|
"the 2nd group pill should have the correct width (4 to 19 dec)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(2)')), "calc(50% - 0px)",
|
|
"the 3rd group pill should have the correct width (20 morning dec");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(3)')), "calc(1150% + 10px - 0px)",
|
|
"the 4th group pill should have the correct width (20 afternoon to 31 dec");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('many2many grouped gantt rendering', async function (assert) {
|
|
assert.expect(20);
|
|
|
|
this.data.tasks.fields.user_ids = { string: 'Assignees', type: 'many2many', relation: 'users' };
|
|
this.data.tasks.records[0].user_ids = [1, 2];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt string="Tasks" date_start="start" date_stop="stop"/>`,
|
|
viewOptions: { initialDate },
|
|
groupBy: ['user_ids'],
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.hasClass(gantt.$('.o_gantt_button_scale[data-value=month]'), 'active',
|
|
'month view should by default activated');
|
|
assert.notOk(gantt.$('.o_gantt_button_expand_rows').is(':visible'),
|
|
"the expand button should be invisible (only displayed if useful)");
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .o_gantt_row_sidebar').text().trim(), 'Tasks',
|
|
'should contain "Tasks" in header sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
'should contain "December 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have 31 slots for month view');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row', 3, 'should have 3 rows');
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_row_sidebar',
|
|
'should have a sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_row_title').text().trim(), 'User 1', 'should contain "User 1" in sidebar title');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_pill_wrapper', 'should have a single pill in first row');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth-child(2) .o_gantt_pill_wrapper').text().trim(), 'Task 1', 'pills should have those names in first row');
|
|
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(3) .o_gantt_row_sidebar',
|
|
'should have a sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth-child(3) .o_gantt_row_title').text().trim(), 'User 2',
|
|
'should contain "User 2" in sidebar title');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:nth-child(3) .o_gantt_pill_wrapper',
|
|
'should have a single pill in second row');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth-child(3) .o_gantt_pill_wrapper').text().trim(), 'Task 1',
|
|
'pills should have those names in second row');
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:last-child .o_gantt_row_sidebar',
|
|
'should have a sidebar');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:last-child .o_gantt_row_title').text().trim(), 'Undefined Assignees', 'should contain "Undefined Assignees" in sidebar title');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row:last-child .o_gantt_pill_wrapper', 5, 'should have 5 pills in last row');
|
|
assert.deepEqual(gantt.$('.o_gantt_row_container .o_gantt_row:last-child .o_gantt_pill_wrapper').text().match(/(\w+\s\d)/g), [
|
|
"Task 5",
|
|
"Task 2",
|
|
"Task 4",
|
|
"Task 7",
|
|
"Task 3"
|
|
], 'pills should have those names in last row');
|
|
|
|
assert.deepEqual([...gantt.el.querySelectorAll(".o_gantt_pill")].map((el) => el.dataset.id), ["1","1","5","2","4","7","3"],
|
|
"two pills should represent same record id");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('multi-level grouped with many2many field in gantt view', async function (assert) {
|
|
assert.expect(20);
|
|
|
|
this.data.tasks.fields.user_ids = { string: 'Assignees', type: 'many2many', relation: 'users' };
|
|
this.data.tasks.records[0].user_ids = [1, 2];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: "tasks",
|
|
data: this.data,
|
|
arch: `<gantt string="Tasks" date_start="start" date_stop="stop"/>`,
|
|
viewOptions: { initialDate },
|
|
groupBy: ["user_ids", "project_id"],
|
|
});
|
|
|
|
// Header
|
|
assert.containsOnce(gantt, ".o_gantt_header_container", "should have a header");
|
|
assert.hasClass(gantt.el.querySelector(".o_gantt_button_scale[data-value=month]"), "active");
|
|
assert.containsOnce(gantt, ".btn-group:not(.d-none) > .o_gantt_button_expand_rows", "there should be an expand button");
|
|
assert.strictEqual(
|
|
gantt.el.querySelector(".o_gantt_header_container > .o_gantt_row_sidebar").innerText,
|
|
"Tasks",
|
|
"should contain 'Tasks' in header sidebar"
|
|
);
|
|
assert.strictEqual(
|
|
gantt.el.querySelector(".o_gantt_header_slots > .row:first-child").innerText,
|
|
"December 2018",
|
|
"should contain 'December 2018' in header"
|
|
);
|
|
assert.containsN(gantt, ".o_gantt_header_scale .o_gantt_header_cell", 31, "should have a 31 slots for month view");
|
|
|
|
// Body
|
|
assert.containsN(gantt, ".o_gantt_row_container .o_gantt_row", 7, "should have 7 rows");
|
|
assert.containsN(gantt, ".o_gantt_row_container .o_gantt_row .o_gantt_row_sidebar", 7, "each rowshould have a sidebar");
|
|
assert.containsN(gantt, ".o_gantt_row_container .o_gantt_row_group.open", 3, "should have 3 opened groups");
|
|
assert.containsN(gantt, ".o_gantt_row_container .o_gantt_row_nogroup", 4, "should have 4 'nogroup' rows");
|
|
|
|
// Check grouped rows
|
|
const rows = gantt.el.querySelectorAll(".o_gantt_row_container .o_gantt_row");
|
|
const rowGroupClasses = [...rows].map((el) => {
|
|
return [...el.classList].filter((c) => c.startsWith("o_gantt_row_"))[0].substring(12);
|
|
});
|
|
assert.deepEqual(rowGroupClasses, ["group", "nogroup", "group", "nogroup", "group", "nogroup", "nogroup"], "rows should be '(no)group' in this order");
|
|
|
|
const rowTitles = [...rows].map((el) => el.querySelector(".o_gantt_row_title").innerText.trim());
|
|
assert.deepEqual(rowTitles, ["User 1", "Project 1", "User 2", "Project 1", "Undefined Assignees", "Project 1", "Project 2"], "rows should have those titles");
|
|
|
|
// group row count and greyscale
|
|
const groupCounts = [...rows].filter((el) => [...el.classList].includes("o_gantt_row_group")).map((el) => [...el.querySelectorAll(".o_gantt_consolidated_pill_title")].map((x) => x.innerText).join());
|
|
assert.deepEqual(groupCounts, ["1", "1", "1,1,2,1,1"], "group consolidated counts should be correctly computed");
|
|
|
|
// consolidated pills should have correct width
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(0)')), "calc(3100% + 30px - 0px)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(1) .o_gantt_pill_wrapper:eq(0)')), "calc(3100% + 30px - 0px)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(2) .o_gantt_pill_wrapper:eq(0)')), "calc(300% + 2px - 0px)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(2) .o_gantt_pill_wrapper:eq(1)')), "calc(250% + 1px - 0px)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(2) .o_gantt_pill_wrapper:eq(2)')), "calc(100% - 0px)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(2) .o_gantt_pill_wrapper:eq(3)')), "calc(150% - 0px)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(2) .o_gantt_pill_wrapper:eq(4)')), "calc(500% + 4px - 0px)");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('full precision gantt rendering', async function(assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" default_scale="week" date_stop="stop" '
|
|
+ 'precision="{\'day\': \'hour:full\', \'week\':'
|
|
+ ' \'day:full\', \'month\': \'day:full\'}" />',
|
|
viewOptions: {
|
|
initialDate: new Date(2018, 10, 15, 8, 0, 0),
|
|
},
|
|
groupBy: ['user_id', 'project_id']
|
|
});
|
|
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(0)')), "calc(700% + 6px - 0px)",
|
|
"the group pill should have the correct width (7 days)");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('gantt rendering, thumbnails', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" thumbnails="{\'user_id\': \'image\'}" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id'],
|
|
mockRPC: function (route, args) {
|
|
console.log(route)
|
|
if (route.endsWith('search_read')) {
|
|
return Promise.resolve({
|
|
records: [
|
|
{
|
|
display_name: "Task 1",
|
|
id: 1,
|
|
start: "2018-11-30 18:30:00",
|
|
stop: "2018-12-31 18:29:59",
|
|
user_id: [1, "User 2"],
|
|
},{
|
|
display_name: "FALSE",
|
|
id: 1,
|
|
start: "2018-12-01 18:30:00",
|
|
stop: "2018-12-02 18:29:59",
|
|
user_id: false,
|
|
}
|
|
]
|
|
})
|
|
}
|
|
if(route.endsWith('read_group')) {
|
|
return Promise.resolve([
|
|
{
|
|
user_id: [1, "User 1"],
|
|
user_id_count: 3,
|
|
__domain: [
|
|
["user_id", "=", 1],
|
|
["start", "<=", "2018-12-31 23:59:59"],
|
|
["stop", ">=", "2018-12-01 00:00:00"],
|
|
]
|
|
},{
|
|
user_id: false,
|
|
user_id_count: 3,
|
|
__domain: [
|
|
["user_id", "=", false],
|
|
["start", "<=", "2018-12-31 23:59:59"],
|
|
["stop", ">=", "2018-12-01 00:00:00"],
|
|
]
|
|
}
|
|
])
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
}
|
|
});
|
|
|
|
|
|
assert.containsN(gantt, '.o_gantt_row_thumbnail', 1, 'There should be a thumbnail per row where user_id is defined');
|
|
|
|
assert.ok(gantt.$('.o_gantt_row_thumbnail:nth(0)')[0].dataset.src.endsWith('web/image?model=users&id=1&field=image'));
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('scale switching', async function (assert) {
|
|
assert.expect(17);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
// default (month)
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'), 'active',
|
|
'month view should be activated by default');
|
|
|
|
// switch to day view
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=day]'));
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=day]'), 'active',
|
|
'day view should be activated');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'Thursday, December 20, 2018',
|
|
'should contain "Thursday, December 20, 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 24,
|
|
'should have a 24 slots for day view');
|
|
assert.containsN(gantt, '.o_gantt_pill_wrapper', 4,
|
|
'should have a 4 pills');
|
|
|
|
// switch to week view
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=week]'));
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=week]'), 'active',
|
|
'week view should be activated');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '16 December 2018 - 22 December 2018',
|
|
'should contain "16 December 2018 - 22 December 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 7,
|
|
'should have a 7 slots for week view');
|
|
assert.containsN(gantt, '.o_gantt_pill_wrapper', 4,
|
|
'should have a 4 pills');
|
|
|
|
// switch to month view
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'));
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'), 'active',
|
|
'month view should be activated');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
'should contain "December 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 31,
|
|
'should have a 31 slots for month view');
|
|
assert.containsN(gantt, '.o_gantt_pill_wrapper', 6,
|
|
'should have a 6 pills');
|
|
|
|
// switch to year view
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=year]'));
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=year]'), 'active',
|
|
'year view should be activated');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '2018',
|
|
'should contain "2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 12,
|
|
'should have a 12 slots for year view');
|
|
assert.containsN(gantt, '.o_gantt_pill_wrapper', 7,
|
|
'should have a 7 pills');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('today is highlighted', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
});
|
|
|
|
var dayOfMonth = moment().date();
|
|
assert.containsOnce(gantt, '.o_gantt_header_cell.o_gantt_today',
|
|
"there should be an highlighted day");
|
|
assert.strictEqual(parseInt(gantt.$('.o_gantt_header_cell.o_gantt_today').text(), 10), dayOfMonth,
|
|
'the highlighted day should be today');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
// GANTT WITH SAMPLE="1"
|
|
|
|
QUnit.test('empty grouped gantt with sample="1"', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: new Date(),
|
|
},
|
|
groupBy: ['project_id'],
|
|
domain: Domain.FALSE_DOMAIN,
|
|
});
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
const content = gantt.$el.text();
|
|
|
|
await gantt.reload();
|
|
assert.strictEqual(gantt.$el.text(), content);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('empty gantt with sample data and default_group_by', async function (assert) {
|
|
assert.expect(5);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1" default_group_by="project_id"/>',
|
|
viewOptions: {
|
|
initialDate: new Date(),
|
|
},
|
|
domain: Domain.FALSE_DOMAIN,
|
|
});
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
const content = gantt.$el.text();
|
|
|
|
// trigger a reload via the search bar
|
|
await testUtils.controlPanel.validateSearch(gantt);
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
assert.strictEqual(gantt.$el.text(), content);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('empty gantt with sample data and default_group_by (switch view)', async function (assert) {
|
|
assert.expect(7);
|
|
|
|
const views = {
|
|
'tasks,false,gantt': '<gantt date_start="start" date_stop="stop" sample="1" default_group_by="project_id"/>',
|
|
'tasks,false,list': '<list/>',
|
|
'tasks,false,search': '<search/>',
|
|
};
|
|
const serverData = {models: this.data, views};
|
|
|
|
const target = getFixture();
|
|
const webClient = await createWebClient({ serverData });
|
|
|
|
await doAction(webClient, {
|
|
name: 'Gantt',
|
|
res_model: 'tasks',
|
|
type: 'ir.actions.act_window',
|
|
views: [[false, 'gantt'], [false, 'list']],
|
|
});
|
|
|
|
// the gantt view should be in sample mode
|
|
assert.hasClass($(target).find('.o_view_controller'), 'o_legacy_view_sample_data');
|
|
assert.ok($(target).find('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
const content = $(target).find('.o_view_controller').text();
|
|
|
|
// switch to list view
|
|
await testUtils.dom.click($(target).find('.o_cp_bottom_right .o_switch_view.o_list'));
|
|
await nextTick();
|
|
|
|
assert.containsOnce(target, '.o_list_view');
|
|
|
|
// go back to gantt view
|
|
await testUtils.dom.click($(target).find('.o_cp_bottom_right .o_switch_view.o_gantt'));
|
|
await nextTick();
|
|
|
|
assert.containsOnce(target, '.o_gantt_view');
|
|
|
|
// the gantt view should be still in sample mode
|
|
assert.hasClass($(target).find('.o_view_controller'), 'o_legacy_view_sample_data');
|
|
assert.ok($(target).find('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
assert.strictEqual($(target).find('.o_view_controller').text(), content);
|
|
});
|
|
|
|
QUnit.test('empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: new Date(),
|
|
},
|
|
domain: Domain.FALSE_DOMAIN,
|
|
});
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
const content = gantt.$el.text();
|
|
|
|
await gantt.reload();
|
|
assert.strictEqual(gantt.$el.text(), content);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('toggle filter on empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: new Date(),
|
|
},
|
|
domain: Domain.FALSE_DOMAIN,
|
|
});
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
await gantt.reload({ domain: [['id', '<', 0]] });
|
|
|
|
assert.doesNotHaveClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container');
|
|
assert.containsNone(gantt, '.o_gantt_pill_wrapper');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('non empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_scale="year" sample="1"/>',
|
|
viewOptions: { initialDate },
|
|
});
|
|
|
|
assert.doesNotHaveClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container');
|
|
assert.containsN(gantt, '.o_gantt_pill_wrapper', 7);
|
|
|
|
await gantt.reload({ domain: Domain.FALSE_DOMAIN });
|
|
assert.doesNotHaveClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsNone(gantt, '.o_gantt_pill_wrapper');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('non empty grouped gantt with sample="1"', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_scale="year" sample="1"/>',
|
|
viewOptions: { initialDate },
|
|
groupBy: ['project_id'],
|
|
});
|
|
|
|
assert.doesNotHaveClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row', 2);
|
|
assert.containsN(gantt, '.o_gantt_pill_wrapper', 7);
|
|
|
|
await gantt.reload({ domain: Domain.FALSE_DOMAIN });
|
|
assert.doesNotHaveClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container');
|
|
assert.containsNone(gantt, '.o_gantt_pill_wrapper');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('add record in empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(5);
|
|
|
|
const today = new Date();
|
|
this.data.tasks.records = [];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: today,
|
|
},
|
|
groupBy: ['project_id'],
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
await testUtils.dom.click(gantt.$('.o_gantt_button_add'));
|
|
await testUtils.modal.clickButton('Save & Close');
|
|
|
|
assert.doesNotHaveClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsOnce(gantt, '.o_gantt_row');
|
|
assert.containsOnce(gantt, '.o_gantt_pill');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('click add and discard in empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
const today = new Date();
|
|
this.data.tasks.records = [];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: today,
|
|
},
|
|
groupBy: ['project_id'],
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
const content = gantt.$el.text();
|
|
await testUtils.dom.click(gantt.$('.o_gantt_button_add'));
|
|
await testUtils.modal.clickButton('Discard');
|
|
|
|
assert.strictEqual(gantt.$el.text(), content,
|
|
"discarding should not modify the sample records previously displayed");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('click button scale in empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(11);
|
|
|
|
const today = new Date();
|
|
this.data.tasks.records = [];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: today,
|
|
}
|
|
});
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
const content = gantt.$el.text();
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=day]'));
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=week]'));
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=month]'));
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
assert.strictEqual(gantt.$el.text(), content,
|
|
"when we return to the default scale, the content should be the same as before");
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=year]'));
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('click today button in empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(5);
|
|
|
|
const today = new Date();
|
|
this.data.tasks.records = [];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: today,
|
|
}
|
|
});
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
|
|
const content = gantt.$el.text();
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_today'));
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
assert.strictEqual(gantt.$el.text(), content,
|
|
"when we return to the default scale, the content should be the same as before");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('click prev/next button in empty gantt with sample="1"', async function (assert) {
|
|
assert.expect(7);
|
|
|
|
const today = new Date();
|
|
this.data.tasks.records = [];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: today,
|
|
}
|
|
});
|
|
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
const content = gantt.$el.text();
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container');
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
assert.hasClass(gantt, 'o_legacy_view_sample_data');
|
|
assert.ok(gantt.$('.o_gantt_pill_wrapper').length > 0, "sample records should be displayed");
|
|
assert.strictEqual(gantt.$el.text(), content);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('empty grouped gantt with sample data: keyboard navigation', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const gantt = await createView({
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
data: this.data,
|
|
domain: Domain.FALSE_DOMAIN,
|
|
groupBy: ['project_id'],
|
|
model: 'tasks',
|
|
View: GanttView,
|
|
viewOptions: {
|
|
initialDate: new Date(),
|
|
},
|
|
});
|
|
|
|
// Check keynav is disabled
|
|
assert.hasClass(
|
|
gantt.el.querySelector('.o_gantt_row:not([data-group-id=empty])'),
|
|
'o_sample_data_disabled'
|
|
);
|
|
assert.containsNone(gantt.renderer, '[tabindex]:not([tabindex="-1"])');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('no content helper when no data and sample mode', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
const records = this.data.tasks.records;
|
|
|
|
this.data.tasks.records = [];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" sample="1"/>',
|
|
viewOptions: {
|
|
initialDate: new Date(2018, 10, 15, 8, 0, 0),
|
|
action: {
|
|
help: markup('<p class="hello">click to add a partner</p>'),
|
|
}
|
|
},
|
|
});
|
|
|
|
await testUtils.nextTick();
|
|
assert.containsOnce(gantt, '.o_view_nocontent',
|
|
"should display the no content helper");
|
|
|
|
assert.strictEqual(gantt.$('.o_view_nocontent p.hello:contains(add a partner)').length, 1,
|
|
"should have rendered no content helper from action");
|
|
|
|
this.data.tasks.records = records;
|
|
await gantt.reload();
|
|
|
|
assert.containsNone(gantt, '.o_view_nocontent',
|
|
"should not display the no content helper");
|
|
gantt.destroy();
|
|
});
|
|
|
|
// BEHAVIORAL TESTS
|
|
|
|
QUnit.test('date navigation with timezone (1h)', async function (assert) {
|
|
assert.expect(32);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (route === '/web/dataset/search_read') {
|
|
assert.step(args.domain.toString());
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
getTZOffset: function () {
|
|
return 60;
|
|
},
|
|
},
|
|
});
|
|
assert.verifySteps(["start,<=,2018-12-31 22:59:59,stop,>=,2018-11-30 23:00:00"]);
|
|
|
|
// month navigation
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'November 2018',
|
|
'should contain "November 2018" in header');
|
|
assert.verifySteps(["start,<=,2018-11-30 22:59:59,stop,>=,2018-10-31 23:00:00"]);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
'should contain "December 2018" in header');
|
|
assert.verifySteps(["start,<=,2018-12-31 22:59:59,stop,>=,2018-11-30 23:00:00"]);
|
|
|
|
// switch to day view and check day navigation
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=day]'));
|
|
assert.verifySteps(["start,<=,2018-12-20 22:59:59,stop,>=,2018-12-19 23:00:00"]);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'Wednesday, December 19, 2018',
|
|
'should contain "Wednesday, December 19, 2018" in header');
|
|
assert.verifySteps(["start,<=,2018-12-19 22:59:59,stop,>=,2018-12-18 23:00:00"]);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'Thursday, December 20, 2018',
|
|
'should contain "Thursday, December 20, 2018" in header');
|
|
assert.verifySteps(["start,<=,2018-12-20 22:59:59,stop,>=,2018-12-19 23:00:00"]);
|
|
|
|
// switch to week view and check week navigation
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=week]'));
|
|
assert.verifySteps(["start,<=,2018-12-22 22:59:59,stop,>=,2018-12-15 23:00:00"]);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '09 December 2018 - 15 December 2018',
|
|
'should contain "09 December 2018 - 15 December 2018" in header');
|
|
assert.verifySteps(["start,<=,2018-12-15 22:59:59,stop,>=,2018-12-08 23:00:00"]);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '16 December 2018 - 22 December 2018',
|
|
'should contain "16 December 2018 - 22 December 2018" in header');
|
|
assert.verifySteps(["start,<=,2018-12-22 22:59:59,stop,>=,2018-12-15 23:00:00"]);
|
|
|
|
// switch to year view and check year navigation
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_scale[data-value=year]'));
|
|
assert.verifySteps(["start,<=,2018-12-31 22:59:59,stop,>=,2017-12-31 23:00:00"]);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '2017',
|
|
'should contain "2017" in header');
|
|
assert.verifySteps(["start,<=,2017-12-31 22:59:59,stop,>=,2016-12-31 23:00:00"]);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '2018',
|
|
'should contain "2018" in header');
|
|
assert.verifySteps(["start,<=,2018-12-31 22:59:59,stop,>=,2017-12-31 23:00:00"]);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('if a on_create is specified, execute the action rather than opening a dialog. And reloads after the action', async function (assert) {
|
|
assert.expect(3);
|
|
var reloadCount = 0;
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" on_create="this_is_create_action" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (route === '/web/dataset/search_read') {
|
|
reloadCount++;
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
testUtils.mock.intercept(gantt, 'do_action', function (event) {
|
|
assert.strictEqual(event.data.action, 'this_is_create_action');
|
|
event.data.options.on_close();
|
|
});
|
|
|
|
assert.strictEqual(reloadCount, 1);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_add'));
|
|
await testUtils.nextTick();
|
|
|
|
assert.strictEqual(reloadCount, 2);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('if a cell_create is specified to false then do not show + icon', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" cell_create="false"/>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt.$buttons, '.o_gantt_button_add',
|
|
"there should be 'Add' button");
|
|
assert.containsNone(gantt, '.o_gantt_cell_add',
|
|
'should not have + icon on cell');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('open a dialog to add a new task', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_add'));
|
|
|
|
// check that the dialog is opened with prefilled fields
|
|
var $modal = $('.modal');
|
|
assert.strictEqual($modal.length, 1, 'There should be one modal opened');
|
|
assert.strictEqual($modal.find('.o_field_widget[name=start] .o_input').val(), '12/01/2018 00:00:00',
|
|
'the start date should be the start of the focus month');
|
|
assert.strictEqual($modal.find('.o_field_widget[name=stop] .o_input').val(), '12/31/2018 23:59:59',
|
|
'the end date should be the end of the focus month');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('open a dialog to create/edit a task', async function (assert) {
|
|
assert.expect(12);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// open dialog to create a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
// check that the dialog is opened with prefilled fields
|
|
var $modal = $('.modal');
|
|
assert.strictEqual($modal.length, 1, 'There should be one modal opened');
|
|
assert.strictEqual($modal.find('.modal-title').text(), "Create");
|
|
await testUtils.fields.editInput($modal.find('[name=name] input'), 'Task 8');
|
|
var $modalFieldStart = $modal.find('.o_field_widget[name=start]');
|
|
assert.strictEqual($modalFieldStart.find('.o_input').val(), '12/10/2018 00:00:00',
|
|
'The start field should have a value "12/10/2018 00:00:00"');
|
|
var $modalFieldStop = $modal.find('.o_field_widget[name=stop]');
|
|
assert.strictEqual($modalFieldStop.find('.o_input').val(), '12/10/2018 23:59:59',
|
|
'The stop field should have a value "12/10/2018 23:59:59"');
|
|
var $modalFieldProject = $modal.find('.o_field_widget.o_field_many2one[name=project_id]');
|
|
assert.strictEqual($modalFieldProject.find('.o_input').val(), 'Project 1',
|
|
'The project field should have a value "Project 1"');
|
|
var $modalFieldUser = $modal.find('.o_field_widget.o_field_many2one[name=user_id]');
|
|
assert.strictEqual($modalFieldUser.find('.o_input').val(), 'User 1',
|
|
'The user field should have a value "User 1"');
|
|
var $modalFieldStage = $modal.find('.o_field_widget[name=stage] select');
|
|
assert.strictEqual($modalFieldStage.val(), '"in_progress"',
|
|
'The stage field should have a value "In Progress"');
|
|
|
|
// create the task
|
|
await testUtils.modal.clickButton('Save & Close');
|
|
assert.strictEqual($('.modal-lg').length, 0, 'Modal should be closed');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_pill').text().trim(), 'Task 8',
|
|
'Task should be created with name "Task 8"');
|
|
|
|
// open dialog to view a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_pill'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
$modal = $('.modal-lg');
|
|
assert.strictEqual($modal.find('.modal-title').text(), "Open");
|
|
assert.strictEqual($modal.length, 1, 'There should be one modal opened');
|
|
assert.strictEqual($modal.find('[name=name] input').val(), 'Task 8',
|
|
'should open dialog for "Task 8"');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test("open a dialog to create a task when grouped by many2many field", async function (assert) {
|
|
assert.expect(22);
|
|
|
|
patchWithCleanup(browser, {
|
|
setTimeout: (fn) => fn(),
|
|
});
|
|
|
|
this.data.tasks.fields.user_ids = { string: 'Assignees', type: 'many2many', relation: 'users' };
|
|
this.data.tasks.records[0].user_ids = [1, 2];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: "tasks",
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop" />`,
|
|
viewOptions: { initialDate },
|
|
groupBy: ["user_ids", "project_id"],
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
<field name="user_ids" widget="many2many_tags"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// Check grouped rows
|
|
let rows = gantt.el.querySelectorAll(".o_gantt_row_container .o_gantt_row");
|
|
const rowGroupClasses = [...rows].map((el) => {
|
|
return [...el.classList].filter((c) => c.startsWith("o_gantt_row_"))[0].substring(12);
|
|
});
|
|
assert.deepEqual(rowGroupClasses, ["group", "nogroup", "group", "nogroup", "group", "nogroup", "nogroup"], "rows should be '(no)group' in this order");
|
|
document.createElement("a").classList.contains
|
|
// Sanity check: row titles and tasks
|
|
assert.deepEqual(
|
|
[...rows].map((row) => row.querySelector(".o_gantt_row_title").innerText.trim()),
|
|
["User 1", "Project 1", "User 2", "Project 1", "Undefined Assignees", "Project 1", "Project 2"],
|
|
"rows should have those titles");
|
|
assert.deepEqual(
|
|
[...rows]
|
|
.filter((row) => row.classList.contains("o_gantt_row_group"))
|
|
.map((row) => row.querySelector(".o_gantt_row_title").innerText.trim()),
|
|
["User 1", "User 2", "Undefined Assignees"],
|
|
"group rows should have those titles");
|
|
assert.deepEqual(
|
|
[...rows]
|
|
.filter((row) => row.classList.contains("o_gantt_row_nogroup"))
|
|
.map((row) => row.querySelector(".o_gantt_row_title").innerText.trim()),
|
|
["Project 1", "Project 1", "Project 1", "Project 2"],
|
|
"nogroup rows should have those titles");
|
|
assert.deepEqual(
|
|
[...rows]
|
|
.filter((row) => row.classList.contains("o_gantt_row_nogroup"))
|
|
.reduce((acc, row) => {
|
|
acc[row.dataset.rowId] = [...row.querySelectorAll(".o_gantt_pill_title")].map((pill) => pill.innerText);
|
|
return acc;
|
|
}, {}),
|
|
{
|
|
'[{"user_ids":[1,"User 1"]},{"project_id":[1,"Project 1"]}]':
|
|
["Task 1"],
|
|
'[{"user_ids":[2,"User 2"]},{"project_id":[1,"Project 1"]}]':
|
|
["Task 1"],
|
|
'[{"user_ids":false},{"project_id":[1,"Project 1"]}]':
|
|
["Task 2", "Task 4", "Task 3"],
|
|
'[{"user_ids":false},{"project_id":[2,"Project 2"]}]':
|
|
["Task 5", "Task 7"],
|
|
},
|
|
"nogroup rows should have those tasks");
|
|
|
|
// open dialog to create a task with two many2many values
|
|
let row = gantt.el.querySelector(`[data-row-id*="User 1"][data-row-id*="Project 1"]`);
|
|
await testUtils.dom.triggerMouseEvent(
|
|
row.querySelector(".o_gantt_cell[data-date='2018-12-10 00:00:00'] .o_gantt_cell_add"),
|
|
"click");
|
|
await testUtils.nextTick();
|
|
let $modal = $(".modal");
|
|
assert.strictEqual($modal.length, 1, "There should be one modal opened");
|
|
assert.strictEqual($modal.find(".modal-title").text(), "Create");
|
|
let $fieldDateStart = $modal.find(".o_field_widget.o_field_datetime[name=start]");
|
|
let $fieldDateStop = $modal.find(".o_field_widget.o_field_datetime[name=stop]");
|
|
let $fieldProject = $modal.find(".o_field_widget.o_field_many2one[name=project_id]");
|
|
let $fieldUser = $modal.find(".o_field_widget.o_field_many2many_tags[name=user_ids]");
|
|
assert.strictEqual($fieldDateStart.find(".o_input").val(), "12/10/2018 00:00:00", "The date start field should have a value '12/10/2018 00:00:00'");
|
|
assert.strictEqual($fieldDateStop.find(".o_input").val(), "12/10/2018 23:59:59", "The date stop field should have a value '12/10/2018 23:59:59'");
|
|
assert.strictEqual($fieldProject.find(".o_input").val(), "Project 1", "The project field should have a value 'Project 1'");
|
|
assert.containsOnce($fieldUser, ".badge", "The user field should contain a single badge");
|
|
assert.strictEqual($fieldUser.find(".badge .o_tag_badge_text").text().trim(), "User 1", "The user field should have a value 'User 1'");
|
|
|
|
await editInput(document.body, ".o_field_widget[name=user_ids] input", "User 2");
|
|
const autocompleteDropdown = document.body.querySelector(".o_dialog .o-autocomplete--dropdown-menu");
|
|
await click(autocompleteDropdown.querySelector("li a"));
|
|
await testUtils.fields.editInput($modal.find("[name=name] input"), "NEWTASK 0");
|
|
await testUtils.modal.clickButton("Save & Close");
|
|
assert.strictEqual($(".modal").length, 0, "Modal should be closed");
|
|
rows = gantt.el.querySelectorAll(".o_gantt_row_container .o_gantt_row");
|
|
assert.deepEqual(
|
|
[...rows]
|
|
.filter((row) => row.classList.contains("o_gantt_row_nogroup"))
|
|
.reduce((acc, row) => {
|
|
acc[row.dataset.rowId] = [...row.querySelectorAll(".o_gantt_pill_title")].map((pill) => pill.innerText);
|
|
return acc;
|
|
}, {}),
|
|
{
|
|
'[{"user_ids":[1,"User 1"]},{"project_id":[1,"Project 1"]}]':
|
|
["Task 1", "NEWTASK 0"],
|
|
'[{"user_ids":[2,"User 2"]},{"project_id":[1,"Project 1"]}]':
|
|
["Task 1", "NEWTASK 0"],
|
|
'[{"user_ids":false},{"project_id":[1,"Project 1"]}]':
|
|
["Task 2", "Task 4", "Task 3"],
|
|
'[{"user_ids":false},{"project_id":[2,"Project 2"]}]':
|
|
["Task 5", "Task 7"],
|
|
},
|
|
"nogroup rows should have those tasks");
|
|
|
|
// open dialog to create a task with no many2many values
|
|
row = gantt.el.querySelector(`[data-row-id*="Project 2"]:not([data-row-id*="User"])`);
|
|
await testUtils.dom.triggerMouseEvent(
|
|
row.querySelector(".o_gantt_cell[data-date='2018-12-24 00:00:00'] .o_gantt_cell_add"),
|
|
"click");
|
|
await testUtils.nextTick();
|
|
$modal = $(".modal");
|
|
assert.strictEqual($modal.length, 1, "There should be one modal opened");
|
|
assert.strictEqual($modal.find(".modal-title").text(), "Create");
|
|
$fieldDateStart = $modal.find(".o_field_widget.o_field_datetime[name=start]");
|
|
$fieldDateStop = $modal.find(".o_field_widget.o_field_datetime[name=stop]");
|
|
$fieldProject = $modal.find(".o_field_widget.o_field_many2one[name=project_id]");
|
|
$fieldUser = $modal.find(".o_field_widget.o_field_many2many_tags[name=user_ids]");
|
|
assert.strictEqual($fieldDateStart.find(".o_input").val(), "12/24/2018 00:00:00", "The date start field should have a value '12/24/2018 00:00:00'");
|
|
assert.strictEqual($fieldDateStop.find(".o_input").val(), "12/24/2018 23:59:59", "The date stop field should have a value '12/24/2018 23:59:59'");
|
|
assert.strictEqual($fieldProject.find(".o_input").val(), "Project 2", "The project field should have a value 'Project 2'");
|
|
assert.containsNone($fieldUser, ".badge", "The user field should not contain badges");
|
|
await testUtils.fields.editInput($modal.find("[name=name] input"), "NEWTASK 1");
|
|
await testUtils.modal.clickButton("Save & Close");
|
|
assert.strictEqual($(".modal").length, 0, "Modal should be closed");
|
|
rows = gantt.el.querySelectorAll(".o_gantt_row_container .o_gantt_row");
|
|
assert.deepEqual(
|
|
[...rows]
|
|
.filter((row) => row.classList.contains("o_gantt_row_nogroup"))
|
|
.reduce((acc, row) => {
|
|
acc[row.dataset.rowId] = [...row.querySelectorAll(".o_gantt_pill_title")].map((pill) => pill.innerText);
|
|
return acc;
|
|
}, {}),
|
|
{
|
|
'[{"user_ids":[1,"User 1"]},{"project_id":[1,"Project 1"]}]':
|
|
["Task 1", "NEWTASK 0"],
|
|
'[{"user_ids":[2,"User 2"]},{"project_id":[1,"Project 1"]}]':
|
|
["Task 1", "NEWTASK 0"],
|
|
'[{"user_ids":false},{"project_id":[1,"Project 1"]}]':
|
|
["Task 2", "Task 4", "Task 3"],
|
|
'[{"user_ids":false},{"project_id":[2,"Project 2"]}]':
|
|
["Task 5", "Task 7", "NEWTASK 1"],
|
|
},
|
|
"nogroup rows should have those tasks");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('open a dialog stops the resize/drag', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 2]],
|
|
});
|
|
const views = {
|
|
'tasks,false,form': '<form><field name="name"/></form>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// open dialog to create a task
|
|
// note that these 3 events need to be triggered for jQuery draggable
|
|
// to be activated
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), "mouseover");
|
|
await testUtils.nextTick();
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
assert.containsOnce($, '.modal', 'There should be one modal opened');
|
|
|
|
// close the modal without moving the mouse by pressing ESC
|
|
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
|
|
await testUtils.nextTick();
|
|
assert.containsNone($, '.modal', 'There should be no modal opened');
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_cell:first'), "mousemove");
|
|
await testUtils.nextTick();
|
|
assert.containsNone(gantt, '.o_gantt_dragging', "the pill should not be dragging");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('open a dialog to create a task, does not have a delete button', async function(assert){
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// open dialog to create a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
var $modal = $('.modal');
|
|
assert.containsNone($modal, '.o_btn_remove', 'There should be no delete button on create dialog');
|
|
|
|
gantt.destroy();
|
|
|
|
});
|
|
|
|
QUnit.test('open a dialog to edit a task, has a delete buttton', async function(assert){
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// open dialog to create a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
// create the task
|
|
await testUtils.modal.clickButton('Save & Close');
|
|
// open dialog to view the task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_pill'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
var $modal = $('.modal');
|
|
|
|
assert.strictEqual($modal.find('.o_form_button_remove').length, 1, 'There should be a delete button on edit dialog');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('clicking on delete button in edit dialog triggers a confirmation dialog, clicking discard does not call unlink on the model', async function(assert){
|
|
assert.expect(4);
|
|
|
|
var unlinkCallCount = 0;
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'unlink') {
|
|
unlinkCallCount++;
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
}
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// open dialog to create a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
// create the task
|
|
await testUtils.modal.clickButton('Save & Close');
|
|
// open dialog to view the task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_pill'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
// trigger the delete button
|
|
await testUtils.modal.clickButton('Remove');
|
|
await testUtils.nextTick();
|
|
|
|
var $dialog = $('.modal-dialog');
|
|
|
|
// there sould be one more dialog
|
|
assert.strictEqual($dialog.length, 2, 'Should have opened a new dialog');
|
|
assert.strictEqual(unlinkCallCount, 0, 'should not call unlink on the model if dialog is cancelled');
|
|
|
|
// trigger cancel
|
|
await testUtils.modal.clickButton('Cancel');
|
|
await testUtils.nextTick();
|
|
|
|
$dialog = $('.modal-dialog');
|
|
assert.strictEqual($dialog.length, 0, 'Should have closed all dialog');
|
|
assert.strictEqual(unlinkCallCount, 0, 'Unlink should not have been called');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('clicking on delete button in edit dialog triggers a confirmation dialog, clicking ok call unlink on the model', async function(assert){
|
|
assert.expect(4);
|
|
|
|
var unlinkCallCount = 0;
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'unlink') {
|
|
unlinkCallCount++;
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
}
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// open dialog to create a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
// create the task
|
|
await testUtils.modal.clickButton('Save & Close');
|
|
// open dialog to view the task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_pill'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
// trigger the delete button
|
|
await testUtils.modal.clickButton('Remove');
|
|
await testUtils.nextTick();
|
|
|
|
var $dialog = $('.modal-dialog');
|
|
|
|
// there sould be one more dialog
|
|
assert.strictEqual($dialog.length, 2, 'Should have opened a new dialog');
|
|
assert.strictEqual(unlinkCallCount, 0, 'should not call unlink on the model if dialog is cancelled');
|
|
|
|
// trigger ok
|
|
await testUtils.modal.clickButton('Ok');
|
|
await testUtils.nextTick();
|
|
|
|
$dialog = $('.modal-dialog');
|
|
assert.strictEqual($dialog.length, 0, 'Should have closed all dialog');
|
|
assert.strictEqual(unlinkCallCount, 1, 'Unlink should have been called');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('create dialog with timezone', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
patchTimeZone(60);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
session: {
|
|
getTZOffset: function () {
|
|
return 60;
|
|
},
|
|
},
|
|
});
|
|
const mockRPC = (route, args) => {
|
|
if (args.method === 'create') {
|
|
assert.deepEqual(args.args, [{
|
|
name: false,
|
|
project_id: false,
|
|
stage: false,
|
|
start: "2018-12-09 23:00:00",
|
|
stop: "2018-12-10 22:59:59",
|
|
user_id: false,
|
|
}], "the start/stop date should take timezone into account");
|
|
}
|
|
};
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>
|
|
`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views }, mockRPC);
|
|
|
|
// open dialog to create a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
assert.strictEqual($('.modal').length, 1, 'There should be one modal opened');
|
|
assert.strictEqual($('.modal .o_field_widget[name=start] .o_input').val(), '12/10/2018 00:00:00',
|
|
'The start field should have a value "12/10/2018 00:00:00"');
|
|
assert.strictEqual($('.modal .o_field_widget[name = stop] .o_input').val(), '12/10/2018 23:59:59',
|
|
'The stop field should have a value "12/10/2018 23:59:59"');
|
|
|
|
// create the task
|
|
await testUtils.modal.clickButton('Save & Close');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('plan button is not present if edit === false and plan is not specified', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" edit="false" />',
|
|
archs: {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
},
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_cell_plan').length, 0);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('plan button is not present if edit === false and plan is true', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" edit="false" plan="true" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_cell_plan').length, 0);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('plan button is not present if edit === true and plan === false', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" edit="true" plan="false" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_cell_plan').length, 0);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('plan button is present if edit === true and plan is not set', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" edit="true" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.notStrictEqual(gantt.$('.o_gantt_cell_plan').length, 0);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('plan button is present if edit === true and plan is true', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" edit="true" plan="true" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.notStrictEqual(gantt.$('.o_gantt_cell_plan').length, 0);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('open a dialog to plan a task', async function (assert) {
|
|
assert.expect(5);
|
|
|
|
this.data.tasks.records.push({ id: 41, name: 'Task 41' });
|
|
this.data.tasks.records.push({ id: 42, name: 'Task 42', stop: '2018-12-31 18:29:59' });
|
|
this.data.tasks.records.push({ id: 43, name: 'Task 43', start: '2018-11-30 18:30:00' });
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.strictEqual(args.model, 'tasks', "should write on the current model");
|
|
assert.deepEqual(args.args[0], [41, 42], "should write on the selected ids");
|
|
assert.deepEqual(args.args[1], { start: "2018-12-10 00:00:00", stop: "2018-12-10 23:59:59" },
|
|
"should write the correct values on the correct fields");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// click on the plan button
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_plan'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
assert.strictEqual($('.modal .o_list_view').length, 1,
|
|
"a list view dialog should be opened");
|
|
assert.strictEqual($('.modal .o_list_view tbody .o_data_cell').text().replace(/\s+/g, ''), "Task41Task42Task43",
|
|
"the 3 records without date set should be displayed");
|
|
|
|
await testUtils.dom.click($('.modal .o_list_view tbody tr:eq(0) input'));
|
|
await testUtils.dom.click($('.modal .o_list_view tbody tr:eq(1) input'));
|
|
await testUtils.dom.click($('.modal .o_select_button:contains(Select)'));
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('open a dialog to plan a task (with timezone)', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
patchTimeZone(60);
|
|
|
|
this.data.tasks.records.push({ id: 41, name: 'Task 41' });
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [41], "should write on the selected id");
|
|
assert.deepEqual(args.args[1], { start: "2018-12-09 23:00:00", stop: "2018-12-10 22:59:59" },
|
|
"should write the correct start/stop taking timezone into account");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
getTZOffset: function () {
|
|
return 60;
|
|
},
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// click on the plan button
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_plan'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
await testUtils.dom.click($('.modal .o_list_view tbody tr:eq(0) input'));
|
|
await testUtils.nextTick();
|
|
await testUtils.dom.click($('.modal .o_select_button:contains(Select)'));
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('open a dialog to plan a task (multi-level)', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
this.data.tasks.records.push({ id: 41, name: 'Task 41' });
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [41], "should write on the selected id");
|
|
assert.deepEqual(args.args[1], {
|
|
project_id: 1,
|
|
stage: "todo",
|
|
start: "2018-12-10 00:00:00",
|
|
stop: "2018-12-10 23:59:59",
|
|
user_id: 1,
|
|
}, "should write on all the correct fields");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
});
|
|
const views = {
|
|
'tasks,false,list': '<tree><field name="name"/></tree>',
|
|
'tasks,false,search': '<search><field name="name"/></search>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
// click on the plan button
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row:not(.o_gantt_row_group):first .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_plan'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
await testUtils.dom.click($('.modal .o_list_view tbody tr:eq(0) input'));
|
|
await testUtils.nextTick();
|
|
await testUtils.dom.click($('.modal .o_select_button:contains(Select)'));
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('expand/collapse rows', async function (assert) {
|
|
assert.expect(8);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row_group.open', 6,
|
|
"there should be 6 opened grouped (2 for the users + 2 projects by users = 6)");
|
|
assert.containsN(gantt, '.o_gantt_row_group:not(.open)', 0,
|
|
"all groups should be opened");
|
|
|
|
// collapse all groups
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_collapse_rows'));
|
|
assert.containsN(gantt, '.o_gantt_row_group:not(.open)', 2,
|
|
"there should be 2 closed groups");
|
|
assert.containsN(gantt, '.o_gantt_row_group.open', 0,
|
|
"all groups should now be closed");
|
|
|
|
// expand all groups
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_expand_rows'));
|
|
assert.containsN(gantt, '.o_gantt_row_group.open', 6,
|
|
"there should be 6 opened grouped");
|
|
assert.containsN(gantt, '.o_gantt_row_group:not(.open)', 0,
|
|
"all groups should be opened again");
|
|
|
|
// collapse the first group
|
|
await testUtils.dom.click(gantt.$('.o_gantt_row_group:first .o_gantt_row_sidebar'));
|
|
assert.containsN(gantt, '.o_gantt_row_group.open', 3,
|
|
"there should be three open groups");
|
|
assert.containsN(gantt, '.o_gantt_row_group:not(.open)', 1,
|
|
"there should be 1 closed group");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('collapsed rows remain collapsed at reload', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row_group.open', 6,
|
|
"there should be 6 opened grouped (2 for the users + 2 projects by users = 6)");
|
|
assert.containsN(gantt, '.o_gantt_row_group:not(.open)', 0,
|
|
"all groups should be opened");
|
|
|
|
// collapse the first group
|
|
await testUtils.dom.click(gantt.$('.o_gantt_row_group:first .o_gantt_row_sidebar'));
|
|
assert.containsN(gantt, '.o_gantt_row_group.open', 3,
|
|
"there should be three open groups");
|
|
assert.containsN(gantt, '.o_gantt_row_group:not(.open)', 1,
|
|
"there should be 1 closed group");
|
|
|
|
// reload
|
|
gantt.reload({});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row_group.open', 3,
|
|
"there should be three open groups");
|
|
assert.containsN(gantt, '.o_gantt_row_group:not(.open)', 1,
|
|
"there should be 1 closed group");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('resize a pill', async function (assert) {
|
|
assert.expect(13);
|
|
|
|
var nbWrite = 0;
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 1]],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [1]);
|
|
// initial dates -- start: '2018-11-30 18:30:00', stop: '2018-12-31 18:29:59'
|
|
if (nbWrite === 0) {
|
|
assert.deepEqual(args.args[1], { stop: "2018-12-30 18:29:59" });
|
|
} else {
|
|
assert.deepEqual(args.args[1], { start: "2018-11-29 18:30:00" });
|
|
}
|
|
nbWrite++;
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_pill',
|
|
"there should be one pill (Task 1)");
|
|
assert.containsNone(gantt, '.o_gantt_pill.ui-resizable',
|
|
"the pill should not be resizable after initial rendering");
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_pill.ui-resizable',
|
|
"the pill should be resizable after mouse enter");
|
|
|
|
assert.containsNone(gantt, '.ui-resizable-w',
|
|
"there should be no left resizer for task 1 (it starts before december)");
|
|
assert.containsOnce(gantt, '.ui-resizable-e',
|
|
"there should be one right resizer for task 1");
|
|
|
|
// resize to one cell smaller (-1 day)
|
|
var cellWidth = gantt.$('.o_gantt_cell:first').width() + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-e'),
|
|
gantt.$('.ui-resizable-e'),
|
|
{ position: { left: -cellWidth, top: 0 } }
|
|
);
|
|
|
|
// go to previous month (november)
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_pill',
|
|
"there should still be one pill (Task 1)");
|
|
assert.containsNone(gantt, '.ui-resizable-e',
|
|
"there should be no right resizer for task 1 (it stops after november)");
|
|
assert.containsOnce(gantt, '.ui-resizable-w',
|
|
"there should be one left resizer for task 1");
|
|
|
|
// resize to one cell smaller (-1 day)
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-w'),
|
|
gantt.$('.ui-resizable-w'),
|
|
{ position: { left: -cellWidth, top: 0 } }
|
|
);
|
|
|
|
assert.strictEqual(nbWrite, 2);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('resize pill in year mode', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_scale="year" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_pill.ui-resizable',
|
|
"in the year mode the pill should be resizable after mouse enter");
|
|
|
|
var pillWidth = gantt.$('.o_gantt_pill').width();
|
|
var cellWidth = gantt.$('.o_gantt_cell:first').width() + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-e'),
|
|
gantt.$('.ui-resizable-e'),
|
|
{ position: { left: 2 * cellWidth, top: 0 } }
|
|
);
|
|
|
|
assert.strictEqual(pillWidth , gantt.$('.o_gantt_pill').width(),
|
|
"the pill should have the same width as before the resize");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('resize a pill (2)', async function (assert) {
|
|
// This test checks a tricky situation where the user resizes a pill, and
|
|
// triggers the mouseup (i.e. release the mouse) over the pill. In this
|
|
// case, the click should not be considered as a click on the pill to
|
|
// edit it.
|
|
assert.expect(6);
|
|
|
|
const def = testUtils.makeTestPromise();
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
archs: {
|
|
'tasks,false,form': '<form/>',
|
|
},
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 2]],
|
|
mockRPC: async function (route, args) {
|
|
const result = this._super(...arguments);
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[1], { stop: "2018-12-23 06:29:59" });
|
|
await def;
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_pill',
|
|
"there should be one pill (Task 1)");
|
|
assert.containsNone(gantt, '.o_gantt_pill.ui-resizable',
|
|
"the pill should not be resizable after initial rendering");
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_pill.ui-resizable',
|
|
"the pill should be resizable after mouse enter");
|
|
assert.containsOnce(gantt, '.ui-resizable-e',
|
|
"there should be one right resizer for task 2");
|
|
|
|
// resize to one cell larger, but do the mouseup over the pill
|
|
const $resize = gantt.$('.ui-resizable-e');
|
|
const cellWidth = gantt.$('.o_gantt_cell:first').width() + 4;
|
|
const options = {
|
|
position: {
|
|
left: 0.9 * cellWidth, // do the mouseup over the pill
|
|
top: 10,
|
|
},
|
|
withTrailingClick: true,
|
|
mouseupTarget: gantt.$('.o_gantt_pill'),
|
|
};
|
|
await testUtils.dom.dragAndDrop($resize, $resize, options);
|
|
|
|
def.resolve();
|
|
assert.containsNone(document.body, '.modal',
|
|
'shoud not have opened the dialog to edit the pill');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('create a task maintains the domain', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop"></gantt>',
|
|
domain: [['user_id', '=', 2]], // I am an important line
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,form': '<form><field name="name"/></form>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.containsN(gantt, '.o_gantt_pill', 3, "the list view is filtered");
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_cell:first .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
await testUtils.fields.editInput($('.modal .modal-body [name=name] input'), 'new task');
|
|
await testUtils.modal.clickButton('Save & Close');
|
|
assert.containsN(gantt, '.o_gantt_pill', 3,
|
|
"the list view is still filtered after the save");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('pill is updated after failed resized', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var nbRead = 0;
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 7]],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.strictEqual(true, true, "should perform a write");
|
|
return Promise.reject();
|
|
}
|
|
if (route === '/web/dataset/search_read') {
|
|
nbRead++;
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var pillWidth = gantt.$('.o_gantt_pill').width();
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
// resize to one cell larger (1 day)
|
|
var cellWidth = gantt.$('.o_gantt_cell:first').width() + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-e'),
|
|
gantt.$('.ui-resizable-e'),
|
|
{ position: { left: cellWidth, top: 0 } }
|
|
);
|
|
|
|
assert.strictEqual(nbRead, 2);
|
|
|
|
assert.strictEqual(pillWidth, gantt.$('.o_gantt_pill').width(),
|
|
"the pill should have the same width as before the resize");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('move a pill in the same row', async function (assert) {
|
|
assert.expect(5);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 7]],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [7],
|
|
"should write on the correct record");
|
|
assert.deepEqual(args.args[1], {
|
|
start: "2018-12-21 10:30:12",
|
|
stop: "2018-12-21 18:29:59",
|
|
}, "both start and stop date should be correctly set (+1 day)");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
assert.containsOnce(gantt, '.o_gantt_pill',
|
|
"there should be one pill (Task 1)");
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_pill'), 'ui-draggable',
|
|
"the pill should not be draggable after initial rendering");
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_pill'), 'ui-draggable',
|
|
"the pill should be draggable after mouse enter");
|
|
|
|
// move a pill in the next cell (+1 day)
|
|
var cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill'),
|
|
gantt.$('.o_gantt_pill'),
|
|
{ position: { left: cellWidth, top: 0 } },
|
|
);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('move a pill in the same row (with timezone)', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 7]],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [7],
|
|
"should write on the correct record");
|
|
assert.deepEqual(args.args[1], {
|
|
start: "2018-12-21 10:30:12",
|
|
stop: "2018-12-21 18:29:59",
|
|
}, "both start and stop date should be correctly set (+1 day)");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
getTZOffset: function () {
|
|
return 60;
|
|
},
|
|
},
|
|
});
|
|
|
|
// move a pill in the next cell (+1 day)
|
|
var cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill'),
|
|
gantt.$('.o_gantt_pill'),
|
|
{ position: { left: cellWidth, top: 0 } },
|
|
);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('move a pill in the same row (with different timezone)', async function (assert) {
|
|
// find on which day daylight savings starts (or ends, it doesn't matter which) in the local timezone
|
|
// we need to make sure it's not a day at the end or beginning of a month
|
|
assert.expect(3);
|
|
var dstDate = null;
|
|
for(var d = moment('2019-01-01 12:00:00'); d.isBefore('2019-12-31'); ) {
|
|
var nextDay = d.clone().add(1, 'day');
|
|
if(nextDay.month() === d.month() && nextDay.utcOffset() !== d.utcOffset()) {
|
|
dstDate = d;
|
|
break;
|
|
}
|
|
d = nextDay;
|
|
}
|
|
if(!dstDate) {
|
|
// we can't really do the test if there is no DST :(
|
|
// unfortunately, the runbot tests are executed on UTC, so we need to dummy the test in that case...
|
|
// it would be ideal if we could pass a timezone to use for unit tests instead
|
|
// (it's possible with the moment-timezone library, but we don't want to add an external dependency
|
|
// as part of a bugfix...)
|
|
dstDate = moment('2020-03-28 12:00:00');
|
|
}
|
|
|
|
var initialDate = dstDate;
|
|
var taskStart = dstDate.clone().hour(10).minute(30).utc();
|
|
this.data.tasks.records[7].start = taskStart.format('YYYY-MM-DD HH:mm:ss');
|
|
this.data.tasks.records[7].stop = taskStart.clone().local().hour(16).utc().format('YYYY-MM-DD HH:mm:ss');
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 8]],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [8],
|
|
"should write on the correct record");
|
|
assert.equal(moment.utc(args.args[1].start).local().hour(), 10,
|
|
"start date should have the same local time as original")
|
|
assert.equal(moment.utc(args.args[1].stop).local().hour(), 16,
|
|
"stop date should have the same local time as original")
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
session: {
|
|
getTZOffset: function (d) {
|
|
return 60;
|
|
},
|
|
},
|
|
});
|
|
|
|
// we are going to move the pill for task 8 (10/24/2020 06:30:12) by 1 cell to the right (+1 day)
|
|
var cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill[data-id=8]'),
|
|
gantt.$('.o_gantt_pill[data-id=8]'),
|
|
{ position: { left: cellWidth + 1, top: 0 } },
|
|
);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('move a pill in another row', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
groupBy: ['project_id'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [7],
|
|
"should write on the correct record");
|
|
assert.deepEqual(args.args[1], {
|
|
project_id: 1,
|
|
start: "2018-12-21 10:30:12",
|
|
stop: "2018-12-21 18:29:59",
|
|
}, "all modified fields should be correctly set");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
domain: [['id', 'in', [1, 7]]],
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_pill', 2,
|
|
"there should be two pills (task 1 and task 7)");
|
|
assert.containsN(gantt, '.o_gantt_row', 2,
|
|
"there should be two rows (project 1 and project 2");
|
|
|
|
// move a pill (task 7) in the other row and in the the next cell (+1 day)
|
|
var cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
var cellHeight = gantt.$('.o_gantt_cell:first').height();
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill[data-id=7]'),
|
|
gantt.$('.o_gantt_pill[data-id=7]'),
|
|
{ position: { left: cellWidth + 4, top: -cellHeight } },
|
|
);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('copy a pill in another row', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
groupBy: ['project_id'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'copy') {
|
|
assert.deepEqual(args.args[0], 7,
|
|
"should copy the correct record");
|
|
assert.deepEqual(args.args[1], {
|
|
start: "2018-12-21 10:30:12",
|
|
stop: "2018-12-21 18:29:59",
|
|
project_id: 1
|
|
},
|
|
"should use the correct default values when copying");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
domain: [['id', 'in', [1, 7]]],
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_pill', 2,
|
|
"there should be two pills (task 1 and task 7)");
|
|
assert.containsN(gantt, '.o_gantt_row', 2,
|
|
"there should be two rows (project 1 and project 2");
|
|
|
|
// move a pill (task 7) in the other row and in the the next cell (+1 day)
|
|
var cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
var cellHeight = gantt.$('.o_gantt_cell:first').height() / 2;
|
|
await testUtils.dom.triggerEvent(gantt.$el, 'keydown',{ctrlKey: true}, true);
|
|
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill[data-id=7]'),
|
|
gantt.$('.o_gantt_pill[data-id=7]'),
|
|
{ position: { left: cellWidth, top: -cellHeight }, ctrlKey: true },
|
|
);
|
|
await testUtils.nextTick();
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('move a pill in another row in multi-level grouped', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [7],
|
|
"should write on the correct record");
|
|
assert.deepEqual(args.args[1], {
|
|
user_id: 2,
|
|
}, "we should only write on user_id");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
domain: [['id', 'in', [3, 7]]],
|
|
});
|
|
|
|
gantt.$('.o_gantt_pill').each(function () {
|
|
testUtils.dom.triggerMouseEvent($(this), 'mouseover');
|
|
});
|
|
await testUtils.nextTick();
|
|
|
|
assert.containsN(gantt, '.o_gantt_pill.ui-draggable:not(.o_fake_draggable)', 1,
|
|
"there should be only one draggable pill (Task 7)");
|
|
|
|
// move a pill (task 7) in the top-level group (User 2)
|
|
var $pill = gantt.$('.o_gantt_pill.ui-draggable:not(.o_fake_draggable)');
|
|
var groupHeaderHeight = gantt.$('.o_gantt_cell:first').height();
|
|
var cellHeight = $pill.closest('.o_gantt_cell').height();
|
|
await testUtils.dom.dragAndDrop(
|
|
$pill,
|
|
$pill,
|
|
{ position: { left: 4, top: -3 * groupHeaderHeight - cellHeight } },
|
|
);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('move a pill in another row in multi-level grouped (many2many case)', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
this.data.tasks.fields.user_ids = { string: 'Assignees', type: 'many2many', relation: 'users' };
|
|
this.data.tasks.records[1].user_ids = [1, 2];
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
groupBy: ['user_id', 'project_id', 'user_ids'],
|
|
viewOptions: { initialDate },
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [2],
|
|
"should write on the correct record");
|
|
assert.deepEqual(args.args[1], {
|
|
"project_id": 1,
|
|
"start": "2018-12-10 23:30:00",
|
|
"stop": "2018-12-15 18:29:59",
|
|
"user_id": 2,
|
|
"user_ids": false
|
|
}, "should write these changes");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
domain: [["user_id", "=", 2],["project_id", "=", 1]]
|
|
});
|
|
|
|
// initialize dragging feature...
|
|
gantt.$('.o_gantt_pill').each(function () {
|
|
testUtils.dom.triggerMouseEvent($(this), 'mouseover');
|
|
});
|
|
await testUtils.nextTick();
|
|
|
|
// sanity check
|
|
const draggable = gantt.el.querySelectorAll(".o_gantt_pill.ui-draggable:not(.o_fake_draggable)");
|
|
assert.deepEqual(
|
|
[...draggable].map((el) => el.innerText),
|
|
["Task 2", "Task 2"],
|
|
"there should be only 2 draggable pills (twice 'Task 2')"
|
|
);
|
|
|
|
// move a pill (first task 2) in last row group (Undefined Assignees)
|
|
await testUtils.dom.dragAndDrop(
|
|
draggable[0],
|
|
gantt.el.querySelector(".o_gantt_row_nogroup:last-child"),
|
|
);
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('display closest hook when pill being dragged', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', 'in', [3, 7]]],
|
|
});
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill:first'), 'mouseover');
|
|
|
|
const cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill:first'),
|
|
gantt.$('.o_gantt_pill:first'),
|
|
{ position: { left: cellWidth, top: 0 }, disableDrop: true },
|
|
);
|
|
assert.hasClass(gantt.$("div[data-date='2018-12-21 00:00:00']"), 'ui-drag-hover',
|
|
"the hook should be displayed with dotted border");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('closest hook should be removed on pill drop', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 7]],
|
|
});
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
const cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill'),
|
|
gantt.$('.o_gantt_pill'),
|
|
{ position: { left: cellWidth, top: 0 } },
|
|
);
|
|
assert.doesNotHaveClass(gantt.$("div[data-date='2018-12-21 00:00:00']"), 'ui-drag-hover',
|
|
"the hook should not be displayed with dotted border after drop");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('grey pills should not be resizable nor draggable', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" color="color" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id'],
|
|
domain: [['id', '=', 7]],
|
|
});
|
|
|
|
|
|
gantt.$('.o_gantt_pill').each(function () {
|
|
testUtils.dom.triggerMouseEvent($(this), 'mouseover');
|
|
});
|
|
await testUtils.nextTick();
|
|
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_row_group .o_gantt_pill'), 'ui-resizable',
|
|
'the group row pill should not be resizable');
|
|
assert.hasClass(gantt.$('.o_gantt_row_group .o_gantt_pill'), 'o_fake_draggable',
|
|
'the group row pill should not be draggable');
|
|
assert.hasClass(gantt.$('.o_gantt_row:not(.o_gantt_row_group) .o_gantt_pill'), 'ui-resizable',
|
|
'the pill should be resizable');
|
|
assert.hasClass(gantt.$('.o_gantt_row:not(.o_gantt_row_group) .o_gantt_pill'), 'ui-draggable',
|
|
'the pill should be draggable');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('should not be draggable when disable_drag_drop is set', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" color="color" disable_drag_drop="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id'],
|
|
domain: [['id', '=', 7]],
|
|
});
|
|
|
|
await testUtils.nextTick();
|
|
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_row_group .o_gantt_pill'), 'ui-draggable',
|
|
'the group row pill should not be draggable');
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_row:not(.o_gantt_row_group) .o_gantt_pill'), 'ui-draggable',
|
|
'the pill should not be draggable');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('gantt_unavailability reloads when the view\'s scale changes', async function(assert){
|
|
assert.expect(11);
|
|
|
|
var unavailabilityCallCount = 0;
|
|
var unavailabilityScaleArg = 'none';
|
|
var reloadCount = 0;
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" display_unavailability="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
var result;
|
|
if (route === '/web/dataset/search_read') {
|
|
reloadCount++;
|
|
result = this._super.apply(this, arguments);
|
|
}
|
|
else if (args.method === 'gantt_unavailability') {
|
|
unavailabilityCallCount++;
|
|
unavailabilityScaleArg = args.args[2];
|
|
result = args.args[4];
|
|
}
|
|
return Promise.resolve(result);
|
|
},
|
|
});
|
|
|
|
assert.strictEqual(reloadCount, 1, 'view should have loaded')
|
|
assert.strictEqual(unavailabilityCallCount, 1, 'view should have loaded unavailability');
|
|
|
|
await testUtils.dom.click(gantt.$('.o_gantt_button_scale[data-value=week]'));
|
|
assert.strictEqual(reloadCount, 2, 'view should have reloaded when switching scale to week')
|
|
assert.strictEqual(unavailabilityCallCount, 2, 'view should have reloaded when switching scale to week');
|
|
assert.strictEqual(unavailabilityScaleArg, 'week', 'unavailability should have been called with the week scale');
|
|
|
|
await testUtils.dom.click(gantt.$('.o_gantt_button_scale[data-value=month]'));
|
|
assert.strictEqual(reloadCount, 3, 'view should have reloaded when switching scale to month')
|
|
assert.strictEqual(unavailabilityCallCount, 3, 'view should have reloaded when switching scale to month');
|
|
assert.strictEqual(unavailabilityScaleArg, 'month', 'unavailability should have been called with the month scale');
|
|
|
|
await testUtils.dom.click(gantt.$('.o_gantt_button_scale[data-value=year]'));
|
|
assert.strictEqual(reloadCount, 4, 'view should have reloaded when switching scale to year')
|
|
assert.strictEqual(unavailabilityCallCount, 4, 'view should have reloaded when switching scale to year');
|
|
assert.strictEqual(unavailabilityScaleArg, 'year', 'unavailability should have been called with the year scale');
|
|
|
|
gantt.destroy();
|
|
|
|
});
|
|
|
|
QUnit.test('gantt_unavailability reload when period changes', async function(assert){
|
|
assert.expect(6);
|
|
|
|
var unavailabilityCallCount = 0;
|
|
var reloadCount = 0;
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" display_unavailability="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
var result;
|
|
if (route === '/web/dataset/search_read') {
|
|
reloadCount++;
|
|
result = this._super.apply(this, arguments);
|
|
}
|
|
else if (args.method === 'gantt_unavailability') {
|
|
unavailabilityCallCount++;
|
|
result = args.args[4];
|
|
}
|
|
return Promise.resolve(result);
|
|
},
|
|
});
|
|
|
|
assert.strictEqual(reloadCount, 1, 'view should have loaded')
|
|
assert.strictEqual(unavailabilityCallCount, 1, 'view should have loaded unavailability');
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
assert.strictEqual(reloadCount, 2, 'view should have reloaded when clicking next')
|
|
assert.strictEqual(unavailabilityCallCount, 2, 'view should have reloaded unavailability when clicking next');
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
assert.strictEqual(reloadCount, 3, 'view should have reloaded when clicking prev')
|
|
assert.strictEqual(unavailabilityCallCount, 3, 'view should have reloaded unavailability when clicking prev');
|
|
|
|
gantt.destroy();
|
|
|
|
});
|
|
|
|
QUnit.test('gantt_unavailability should not reload when period changes if display_unavailability is not set', async function(assert){
|
|
assert.expect(6);
|
|
|
|
var unavailabilityCallCount = 0;
|
|
var reloadCount = 0;
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
var result;
|
|
if (route === '/web/dataset/search_read') {
|
|
reloadCount++;
|
|
result = this._super.apply(this, arguments);
|
|
}
|
|
else if (args.method === 'gantt_unavailability') {
|
|
unavailabilityCallCount++;
|
|
result = {};
|
|
}
|
|
return Promise.resolve(result);
|
|
},
|
|
});
|
|
|
|
assert.strictEqual(reloadCount, 1, 'view should have loaded')
|
|
assert.strictEqual(unavailabilityCallCount, 0, 'view should not have loaded unavailability');
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
assert.strictEqual(reloadCount, 2, 'view should have reloaded when clicking next')
|
|
assert.strictEqual(unavailabilityCallCount, 0, 'view should not have reloaded unavailability when clicking next');
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_prev'));
|
|
assert.strictEqual(reloadCount, 3, 'view should have reloaded when clicking prev')
|
|
assert.strictEqual(unavailabilityCallCount, 0, 'view should not have reloaded unavailability when clicking prev');
|
|
|
|
gantt.destroy();
|
|
|
|
});
|
|
|
|
QUnit.test('cancelled drag and tooltip', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
var POPOVER_DELAY = GanttRow.prototype.POPOVER_DELAY;
|
|
GanttRow.prototype.POPOVER_DELAY = 0;
|
|
|
|
this.data.tasks.records[1].start = '2018-12-16 03:00:00';
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt default_scale="week" date_start="start" date_stop="stop" />',
|
|
archs: {
|
|
'tasks,false,form': '<form/>',
|
|
},
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
throw new Error('Should not do a write RPC');
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,form': '<form/>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.containsN(gantt, '.o_gantt_pill', 4);
|
|
const $secondPill = gantt.$('.o_gantt_pill:nth(1)');
|
|
|
|
// enable the drag feature
|
|
await testUtils.dom.triggerMouseEvent($secondPill, 'mouseover');
|
|
assert.hasClass($secondPill, 'ui-draggable', "the pill should be draggable after mouse enter");
|
|
assert.containsOnce(document.body, 'div.popover');
|
|
|
|
// move the pill of a few px (not enough for it to actually move to another cell)
|
|
await testUtils.dom.dragAndDrop($secondPill, $secondPill, {
|
|
position: { left: 4, top: 4 },
|
|
withTrailingClick: true,
|
|
});
|
|
|
|
// check popover
|
|
await testUtils.dom.triggerEvents($secondPill, ['mouseover']);
|
|
assert.containsOnce(document.body, 'div.popover');
|
|
|
|
// edit pill
|
|
await testUtils.dom.triggerEvents($secondPill, ['click']);
|
|
assert.containsOnce(document.body, '.modal .o_form_view');
|
|
|
|
gantt.destroy();
|
|
assert.containsNone(gantt, 'div.popover', 'should not have a popover anymore');
|
|
GanttRow.prototype.POPOVER_DELAY = POPOVER_DELAY;
|
|
});
|
|
|
|
QUnit.test('drag&drop on other pill in grouped view', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
var POPOVER_DELAY = GanttRow.prototype.POPOVER_DELAY;
|
|
GanttRow.prototype.POPOVER_DELAY = 0;
|
|
|
|
this.data.tasks.records[0].start = '2018-12-16 05:00:00';
|
|
this.data.tasks.records[0].stop = '2018-12-16 07:00:00';
|
|
this.data.tasks.records[1].stop = '2018-12-17 13:00:00';
|
|
|
|
let resolve;
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt default_scale="week" date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
mockRPC: async function (route, args) {
|
|
const promise = this._super.apply(this, arguments);
|
|
if (args.method === 'write') {
|
|
await new Promise(r => {
|
|
resolve = r;
|
|
});
|
|
}
|
|
return promise;
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,form': '<form/>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
const $firstPill = gantt.$('.o_gantt_pill:nth(0)');
|
|
let $secondPill = gantt.$('.o_gantt_pill:nth(1)');
|
|
|
|
// enable the drag feature
|
|
await testUtils.dom.triggerMouseEvent($secondPill, 'mouseover');
|
|
assert.hasClass($secondPill, 'ui-draggable', "the pill should be draggable after mouse enter");
|
|
assert.containsOnce(document.body, 'div.popover h3:contains(Task 2)', 'the pill should be display the popover');
|
|
|
|
// move the pill on hover the other pill
|
|
await testUtils.dom.dragAndDrop($secondPill, $firstPill, {
|
|
disableDrop: true,
|
|
});
|
|
|
|
// drop the pill on the other pill, the mouse stay at the same place
|
|
await testUtils.dom.triggerMouseEvent($secondPill, 'mouseup');
|
|
await testUtils.dom.triggerMouseEvent($firstPill, 'mouseover');
|
|
|
|
// wait popover is shown
|
|
await testUtils.nextTick();
|
|
await testUtils.returnAfterNextAnimationFrame();
|
|
|
|
assert.containsOnce(document.body, 'div.popover h3:contains(Task 1)', 'when drag&drop the other pills should be display the popover');
|
|
|
|
// force a long transition duration (avoid intermittent error raising)
|
|
$('div.popover:has(h3:contains(Task 1))').css('transition-duration', '1s');
|
|
|
|
// wait the redrawing and popover is removed
|
|
resolve();
|
|
await testUtils.nextTick();
|
|
await testUtils.returnAfterNextAnimationFrame();
|
|
|
|
assert.containsNone(document.body, 'div.popover', 'should not have a popover anymore');
|
|
|
|
gantt.destroy();
|
|
|
|
GanttRow.prototype.POPOVER_DELAY = POPOVER_DELAY;
|
|
});
|
|
|
|
// ATTRIBUTES TESTS
|
|
|
|
QUnit.test('create attribute', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" create="false" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
// the "Add" should not appear
|
|
assert.containsNone(gantt.$buttons.find('.o_gantt_button_add'),
|
|
"there should be no 'Add' button");
|
|
|
|
await testUtils.dom.click(gantt.$('.o_gantt_cell:first'));
|
|
|
|
assert.strictEqual($('.modal').length, 0,
|
|
"there should be no opened modal");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('edit attribute', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" edit="false" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
const views = {
|
|
'tasks,false,form': '<form><field name="name"/></form>',
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.containsNone(gantt, '.o_gantt_pill.ui-resizable',
|
|
"the pills should not be resizable");
|
|
|
|
assert.containsNone(gantt, '.o_gantt_pill.ui-draggable',
|
|
"the pills should not be draggable");
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill:first'), 'click');
|
|
await testUtils.nextTick();
|
|
|
|
assert.strictEqual($('.modal').length, 1,
|
|
"there should be a opened modal");
|
|
assert.strictEqual($('.modal .o_form_view .o_form_readonly').length, 1,
|
|
"the form view should be in readonly");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('total_row attribute', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" total_row="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row',
|
|
'should have 1 row');
|
|
assert.containsOnce(gantt, '.o_gantt_total_row_container .o_gantt_row_total',
|
|
'should have 1 total row');
|
|
assert.containsNone(gantt, '.o_gantt_row_container .o_gantt_row_sidebar',
|
|
'container should not have a sidebar');
|
|
assert.containsNone(gantt, '.o_gantt_total_row_container .o_gantt_row_sidebar',
|
|
'total container should not have a sidebar');
|
|
assert.containsN(gantt, '.o_gantt_row_total .o_gantt_pill ', 7,
|
|
'should have a 7 pills in the total row');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_total .o_gantt_consolidated_pill_title').text().replace(/\s+/g, ''), "2123212",
|
|
"the total row should be correctly computed");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('default_scale attribute', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_scale="day" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.hasClass(gantt.$buttons.find('.o_gantt_button_scale[data-value=day]'), 'active',
|
|
'day view should be activated');
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'Thursday, December 20, 2018',
|
|
'should contain "Thursday, December 20, 2018" in header');
|
|
assert.containsN(gantt, '.o_gantt_header_container .o_gantt_header_scale .o_gantt_header_cell', 24,
|
|
'should have a 24 slots for day view');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('scales attribute', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" scales="month,day,trololo" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsN(gantt.$buttons, '.o_gantt_button_scale', 2,
|
|
'only 2 scales should be available');
|
|
assert.strictEqual(gantt.$buttons.find('.o_gantt_button_scale').first().text().trim(), 'Month',
|
|
'Month scale should be the first option');
|
|
assert.strictEqual(gantt.$buttons.find('.o_gantt_button_scale').last().text().trim(), 'Day',
|
|
'Day scale should be the second option');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('precision attribute', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" precision=\'{"day": "hour:quarter", "week": "day:half", "month": "day", "year": "month:quarter"}\' default_scale="day" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 7]],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[1], { stop: "2018-12-20 18:44:59" });
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var cellWidth = gantt.$('.o_gantt_cell:first').width() + 4;
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
// resize of a quarter
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-e'),
|
|
gantt.$('.ui-resizable-e'),
|
|
{ disableDrop: true, position: { left: cellWidth / 4, top: 0 } }
|
|
);
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_pill_resize_badge').text().trim(), "+15 minutes",
|
|
"the resize should be by 15min step");
|
|
|
|
// manually trigger the drop to trigger a write
|
|
var toOffset = gantt.$('.ui-resizable-e').offset();
|
|
await gantt.$('.ui-resizable-e').trigger($.Event("mouseup", {
|
|
which: 1,
|
|
pageX: toOffset.left + cellWidth / 4,
|
|
pageY: toOffset.top
|
|
}));
|
|
await testUtils.nextTick();
|
|
|
|
assert.containsNone(gantt, '.o_gantt_pill_resize_badge',
|
|
"the badge should disappear after drop");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('progress attribute', async function (assert) {
|
|
assert.expect(7);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" progress="progress" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_pill .o_gantt_progress', 6,
|
|
'should have 6 rows with o_gantt_progress class');
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_pill:contains("Task 1") .o_gantt_progress').prop('style')['width'], '0%',
|
|
'first pill should have 0% progress');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_pill:contains("Task 2") .o_gantt_progress').prop('style')['width'], '30%',
|
|
'second pill should have 30% progress');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_pill:contains("Task 3") .o_gantt_progress').prop('style')['width'], '60%',
|
|
'third pill should have 60% progress');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_pill:contains("Task 4") .o_gantt_progress').prop('style')['width'], '0%',
|
|
'fourth pill should have 0% progress');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_pill:contains("Task 5") .o_gantt_progress').prop('style')['width'], '100%',
|
|
'fifth pill should have 100% progress');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_pill:contains("Task 7") .o_gantt_progress').prop('style')['width'], '80%',
|
|
'seventh task should have 80% progress');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
|
|
QUnit.test('form_view_id attribute', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" form_view_id="42"/>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
});
|
|
|
|
const views = {
|
|
'tasks,42,form': '<form><field name="name"/></form>',
|
|
};
|
|
const mockRPC = (route, args) => {
|
|
if (args.method === "get_views") {
|
|
assert.deepEqual(args.kwargs.views, [[42, "form"]]);
|
|
}
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views }, mockRPC);
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_add'));
|
|
await testUtils.nextTick();
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
|
|
QUnit.test('decoration attribute', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" decoration-info="stage == \'todo\'">' +
|
|
'<field name="stage"/>' +
|
|
'</gantt>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_pill[data-id=1]'), 'decoration-info',
|
|
'should have a "decoration-info" class on task 1');
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_pill[data-id=2]'), 'decoration-info',
|
|
'should not have a "decoration-info" class on task 2');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('decoration attribute with date', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 19, 12, 0, 0);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" decoration-danger="start < today">' +
|
|
'</gantt>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_pill[data-id=1]'), 'decoration-danger',
|
|
'should have a "decoration-danger" class on task 1');
|
|
assert.hasClass(gantt.$('.o_gantt_pill[data-id=2]'), 'decoration-danger',
|
|
'should have a "decoration-danger" class on task 2');
|
|
assert.hasClass(gantt.$('.o_gantt_pill[data-id=5]'), 'decoration-danger',
|
|
'should have a "decoration-danger" class on task 5');
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_pill[data-id=3]'), 'decoration-danger',
|
|
'should not have a "decoration-danger" class on task 3');
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_pill[data-id=4]'), 'decoration-danger',
|
|
'should not have a "decoration-danger" class on task 4');
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_pill[data-id=7]'), 'decoration-danger',
|
|
'should not have a "decoration-danger" class on task 7');
|
|
|
|
gantt.destroy();
|
|
unpatchDate();
|
|
});
|
|
|
|
QUnit.test('consolidation feature', async function (assert) {
|
|
assert.expect(25);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" consolidation="progress" consolidation_max=\'{"user_id": 100}\' consolidation_exclude="exclude" progress="progress"/>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id', 'stage'],
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row', 18,
|
|
'should have a 18 rows');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row_group.open', 12,
|
|
'should have a 12 opened groups as consolidation implies collapse_first_level');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row:not(.o_gantt_row_group)', 6,
|
|
'should have a 6 rows');
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_row:first .o_gantt_row_sidebar',
|
|
'should have a sidebar');
|
|
|
|
// Check grouped rows
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:first'), 'o_gantt_row_group',
|
|
'1st row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:first .o_gantt_row_title').text().trim(), 'User 1',
|
|
'1st row title should be "User 1"');
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_row_container .o_gantt_row:nth(9)'), 'o_gantt_row_group',
|
|
'7th row should be a group');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(9) .o_gantt_row_title').text().trim(), 'User 2',
|
|
'7th row title should be "User 2"');
|
|
|
|
// Consolidation
|
|
// 0 over the size of Task 5 (Task 5 is 100 but is excluded !) then 0 over the rest of Task 1, cut by Task 4 which has progress 0
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_consolidated_pill_title ').text().replace(/\s+/g, ''), "0000",
|
|
"the consolidation should be correctly computed");
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill:eq(0)').css('background-color'), "rgb(40, 167, 69)",
|
|
"the 1st group pill should have the correct color)");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill:eq(1)').css('background-color'), "rgb(40, 167, 69)",
|
|
"the 2nd group pill should have the correct color)");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill:eq(2)').css('background-color'), "rgb(40, 167, 69)",
|
|
"the 3rd group pill should have the correct color");
|
|
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(0)')), "calc(300% + 2px - 0px)",
|
|
"the 1st group pill should have the correct width (1 to 3 dec)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(1)')), "calc(1600% + 15px - 0px)",
|
|
"the 2nd group pill should have the correct width (4 to 19 dec)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(2)')), "calc(50% - 0px)",
|
|
"the 3rd group pill should have the correct width (20 morning dec");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_pill_wrapper:eq(3)')), "calc(1150% + 10px - 0px)",
|
|
"the 4th group pill should have the correct width (20 afternoon to 31 dec");
|
|
|
|
// 30 over Task 2 until Task 7 then 110 (Task 2 (30) + Task 7 (80)) then 30 again until end of task 2 then 60 over Task 3
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_consolidated_pill_title').text().replace(/\s+/g, ''), "301103060",
|
|
"the consolidation should be correctly computed");
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill:eq(0)').css('background-color'), "rgb(40, 167, 69)",
|
|
"the 1st group pill should have the correct color)");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill:eq(1)').css('background-color'), "rgb(220, 53, 69)",
|
|
"the 2nd group pill should have the correct color)");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill:eq(2)').css('background-color'), "rgb(40, 167, 69)",
|
|
"the 3rd group pill should have the correct color");
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill:eq(3)').css('background-color'), "rgb(40, 167, 69)",
|
|
"the 4th group pill should have the correct color");
|
|
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill_wrapper:eq(0)')), "calc(300% + 2px - 0px)",
|
|
"the 1st group pill should have the correct width (17 afternoon to 20 dec morning)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill_wrapper:eq(1)')), "calc(50% - 0px)",
|
|
"the 2nd group pill should have the correct width (20 dec afternoon)");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill_wrapper:eq(2)')), "calc(150% - 0px)",
|
|
"the 3rd group pill should have the correct width (21 to 22 dec morning dec");
|
|
assert.strictEqual(getPillItemWidth(gantt.$('.o_gantt_row_group:eq(6) .o_gantt_pill_wrapper:eq(3)')), "calc(500% + 4px - 0px)",
|
|
"the 4th group pill should have the correct width (27 afternoon to 31 dec");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('color attribute', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" color="color" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_pill[data-id=1]'), 'o_gantt_color_0',
|
|
'should have a color_0 class on task 1');
|
|
assert.hasClass(gantt.$('.o_gantt_pill[data-id=2]'), 'o_gantt_color_2',
|
|
'should have a color_0 class on task 2');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('color attribute in multi-level grouped', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" color="color" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id'],
|
|
domain: [['id', '=', 1]],
|
|
});
|
|
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_row_group .o_gantt_pill'), 'o_gantt_color_0',
|
|
"the group row pill should not be colored");
|
|
assert.hasClass(gantt.$('.o_gantt_row:not(.o_gantt_row_group) .o_gantt_pill'), 'o_gantt_color_0',
|
|
'the pill should be colored');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('color attribute on a many2one', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" color="project_id" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.hasClass(gantt.$('.o_gantt_pill[data-id=1]'), 'o_gantt_color_1',
|
|
'should have a color_1 class on task 1');
|
|
assert.containsN(gantt, '.o_gantt_pill.o_gantt_color_1', 4,
|
|
"there should be 4 pills with color 1");
|
|
assert.containsN(gantt, '.o_gantt_pill.o_gantt_color_2', 2,
|
|
"there should be 2 pills with color 2");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Today style with unavailabilities ("week": "day:half")', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 19, 2, 0, 0);
|
|
|
|
const unavailabilities = [{
|
|
start: '2018-12-16 10:00:00',
|
|
stop: '2018-12-18 14:00:00'
|
|
}].map(convertUnavailability);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop" display_unavailability="1"
|
|
default_scale="week" scales="week" precision="{'week': 'day:half'}"/>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_unavailability') {
|
|
const rows = args.args[4];
|
|
rows.forEach(function(r) {
|
|
r.unavailabilities = unavailabilities;
|
|
});
|
|
return Promise.resolve(rows);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const cell4 = gantt.el.querySelectorAll('.o_gantt_row_container .o_gantt_cell')[3];
|
|
assert.hasClass(cell4, 'o_gantt_today');
|
|
assert.hasAttrValue(cell4, 'style', 'height: 105px;');
|
|
|
|
unpatchDate();
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Today style of group rows', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 19, 2, 0, 0);
|
|
|
|
this.data.tasks.records = this.data.tasks.records.filter(r => r.id === 4);
|
|
|
|
const unavailabilities = [{
|
|
start: '2018-12-18 09:00:00',
|
|
stop: '2018-12-19 13:00:00'
|
|
}].map(convertUnavailability);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop" display_unavailability="1"
|
|
default_scale="week" scales="week" precision="{'week': 'day:half'}"/>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'project_id'],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_unavailability') {
|
|
const rows = args.args[4];
|
|
rows.forEach(function(r) {
|
|
r.unavailabilities = unavailabilities;
|
|
r.rows.forEach(sr => {
|
|
sr.unavailabilities = unavailabilities;
|
|
})
|
|
});
|
|
return Promise.resolve(rows);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
let todayCells = gantt.el.querySelectorAll('div.o_gantt_today');
|
|
assert.deepEqual([...todayCells].map(c => c.getAttribute('style')), [
|
|
null,
|
|
"height: 0px;", // a css rule fix a minimal height
|
|
"height: 35px; background: linear-gradient(90deg, var(--Gant__DayOff-background-color) 49%, var(--Gant__DayOffToday-background-color) 50%);"
|
|
]);
|
|
assert.strictEqual(window.getComputedStyle(todayCells[1]).getPropertyValue('background-color'), "rgba(0, 0, 0, 0)");
|
|
|
|
await testUtils.dom.click(todayCells[1]);
|
|
|
|
todayCells = gantt.el.querySelectorAll('div.o_gantt_today');
|
|
assert.deepEqual([...todayCells].map(c => c.getAttribute('style')), [
|
|
null,
|
|
"height: 0px;"
|
|
]);
|
|
assert.strictEqual(window.getComputedStyle(todayCells[1]).getPropertyValue('background-color'), "rgb(251, 249, 243)");
|
|
|
|
unpatchDate();
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('style without unavailabilities', async function (assert) {
|
|
assert.expect(3);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 5, 2, 0, 0);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" display_unavailability="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_unavailability') {
|
|
return Promise.resolve(args.args[4]);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const cells = gantt.el.querySelectorAll('.o_gantt_row_container .o_gantt_cell');
|
|
|
|
const cell5 = cells[4]
|
|
assert.hasClass(cell5, 'o_gantt_today');
|
|
assert.hasAttrValue(cell5, 'style', 'height: 105px;');
|
|
const cell6 = cells[5]
|
|
assert.hasAttrValue(cell6, 'style', 'height: 105px;');
|
|
|
|
unpatchDate();
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Unavailabilities ("month": "day:half")', async function (assert) {
|
|
assert.expect(10);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 5, 2, 0, 0);
|
|
|
|
const unavailabilities = [{
|
|
start: '2018-12-05 09:30:00',
|
|
stop: '2018-12-07 08:00:00'
|
|
}, {
|
|
start: '2018-12-16 09:00:00',
|
|
stop: '2018-12-18 13:00:00'
|
|
}].map(convertUnavailability);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" display_unavailability="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_unavailability') {
|
|
assert.strictEqual(args.model, 'tasks',
|
|
"the availability should be fetched on the correct model");
|
|
assert.strictEqual(args.args[0], '2018-12-01 00:00:00',
|
|
"the start_date argument should be in the server format");
|
|
assert.strictEqual(args.args[1], '2018-12-31 23:59:59',
|
|
"the end_date argument should be in the server format");
|
|
const rows = args.args[4];
|
|
rows.forEach(function(r) {
|
|
r.unavailabilities = unavailabilities;
|
|
});
|
|
return Promise.resolve(rows);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const cells = gantt.el.querySelectorAll('.o_gantt_row_container .o_gantt_cell');
|
|
|
|
const cell5 = cells[4];
|
|
assert.hasClass(cell5, 'o_gantt_today');
|
|
assert.hasAttrValue(cell5, 'style', 'height: 105px; background: linear-gradient(90deg, var(--Gant__DayOffToday-background-color) 49%, var(--Gant__DayOff-background-color) 50%);');
|
|
const cell6 = cells[5];
|
|
assert.hasAttrValue(cell6, 'style', 'height: 105px; background: var(--Gant__DayOff-background-color)');
|
|
const cell7 = cells[6];
|
|
assert.hasAttrValue(cell7, 'style', 'height: 105px;');
|
|
const cell16 = cells[15];
|
|
assert.hasAttrValue(cell16,'style', 'height: 105px; background: linear-gradient(90deg, var(--Gant__Day-background-color) 49%, var(--Gant__DayOff-background-color) 50%);');
|
|
const cell17 = cells[16];
|
|
assert.hasAttrValue(cell17,'style', 'height: 105px; background: var(--Gant__DayOff-background-color)');
|
|
const cell18 = cells[17];
|
|
assert.hasAttrValue(cell18,'style', 'height: 105px; background: linear-gradient(90deg, var(--Gant__DayOff-background-color) 49%, var(--Gant__Day-background-color) 50%);');
|
|
|
|
unpatchDate();
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Unavailabilities ("day": "hours:quarter")', async function (assert) {
|
|
assert.expect(5);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 20, 8, 0, 0);
|
|
|
|
this.data.tasks.records = [];
|
|
|
|
const unavailabilities = [
|
|
{
|
|
start: '2018-12-20 08:15:00',
|
|
stop: '2018-12-20 08:30:00',
|
|
}, {
|
|
start: '2018-12-20 10:35:00',
|
|
stop: '2018-12-20 12:29:00'
|
|
}, {
|
|
start: '2018-12-20 20:15:00',
|
|
stop: '2018-12-20 20:50:00',
|
|
}
|
|
].map(convertUnavailability);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop" display_unavailability="1"
|
|
default_scale="day" scales="day" precision="{'day': 'hours:quarter'}"/>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_unavailability') {
|
|
const rows = args.args[4];
|
|
rows.forEach(function(r) {
|
|
r.unavailabilities = unavailabilities;
|
|
});
|
|
return Promise.resolve(rows);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const cells = gantt.el.querySelectorAll('.o_gantt_row_container .o_gantt_cell');
|
|
|
|
const cell9 = cells[8];
|
|
assert.hasAttrValue(cell9, 'style', 'height: 0px; background: linear-gradient(90deg, var(--Gant__Day-background-color) 24%, var(--Gant__DayOff-background-color) 25%, var(--Gant__DayOff-background-color) 49%, var(--Gant__Day-background-color) 50%, var(--Gant__Day-background-color) 74%, var(--Gant__Day-background-color) 75%);');
|
|
const cell11 = cells[10];
|
|
assert.hasAttrValue(cell11, 'style', 'height: 0px; background: linear-gradient(90deg, var(--Gant__Day-background-color) 24%, var(--Gant__Day-background-color) 25%, var(--Gant__Day-background-color) 49%, var(--Gant__Day-background-color) 50%, var(--Gant__Day-background-color) 74%, var(--Gant__DayOff-background-color) 75%);');
|
|
const cell12 = cells[11];
|
|
assert.hasAttrValue(cell12, 'style', 'height: 0px; background: var(--Gant__DayOff-background-color)');
|
|
const cell13 = cells[12];
|
|
assert.hasAttrValue(cell13, 'style', 'height: 0px; background: linear-gradient(90deg, var(--Gant__DayOff-background-color) 24%, var(--Gant__Day-background-color) 25%, var(--Gant__Day-background-color) 49%, var(--Gant__Day-background-color) 50%, var(--Gant__Day-background-color) 74%, var(--Gant__Day-background-color) 75%);');
|
|
const cell21 = cells[20];
|
|
assert.hasAttrValue(cell21, 'style', 'height: 0px; background: linear-gradient(90deg, var(--Gant__Day-background-color) 24%, var(--Gant__DayOff-background-color) 25%, var(--Gant__DayOff-background-color) 49%, var(--Gant__DayOff-background-color) 50%, var(--Gant__DayOff-background-color) 74%, var(--Gant__Day-background-color) 75%);');
|
|
|
|
unpatchDate();
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Unavailabilities ("month": "day:half")', async function (assert) {
|
|
assert.expect(10);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 5, 2, 0, 0);
|
|
|
|
const unavailabilities = [{
|
|
start: '2018-12-05 09:30:00',
|
|
stop: '2018-12-07 08:00:00'
|
|
}, {
|
|
start: '2018-12-16 09:00:00',
|
|
stop: '2018-12-18 13:00:00'
|
|
}].map(convertUnavailability);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" display_unavailability="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_unavailability') {
|
|
assert.strictEqual(args.model, 'tasks',
|
|
"the availability should be fetched on the correct model");
|
|
assert.strictEqual(args.args[0], '2018-12-01 00:00:00',
|
|
"the start_date argument should be in the server format");
|
|
assert.strictEqual(args.args[1], '2018-12-31 23:59:59',
|
|
"the end_date argument should be in the server format");
|
|
const rows = args.args[4];
|
|
rows.forEach(function(r) {
|
|
r.unavailabilities = unavailabilities;
|
|
});
|
|
return Promise.resolve(rows);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const cells = gantt.el.querySelectorAll('.o_gantt_row_container .o_gantt_cell');
|
|
|
|
const cell5 = cells[4];
|
|
assert.hasClass(cell5, 'o_gantt_today');
|
|
assert.hasAttrValue(cell5, 'style', 'height: 105px; background: linear-gradient(90deg, var(--Gant__DayOffToday-background-color) 49%, var(--Gant__DayOff-background-color) 50%);');
|
|
const cell6 = cells[5];
|
|
assert.hasAttrValue(cell6, 'style', 'height: 105px; background: var(--Gant__DayOff-background-color)');
|
|
const cell7 = cells[6];
|
|
assert.hasAttrValue(cell7, 'style', 'height: 105px;');
|
|
const cell16 = cells[15];;
|
|
assert.hasAttrValue(cell16,'style', 'height: 105px; background: linear-gradient(90deg, var(--Gant__Day-background-color) 49%, var(--Gant__DayOff-background-color) 50%);');
|
|
const cell17 = cells[16];;
|
|
assert.hasAttrValue(cell17,'style', 'height: 105px; background: var(--Gant__DayOff-background-color)');
|
|
const cell18 = cells[17];;
|
|
assert.hasAttrValue(cell18,'style', 'height: 105px; background: linear-gradient(90deg, var(--Gant__DayOff-background-color) 49%, var(--Gant__Day-background-color) 50%);');
|
|
|
|
unpatchDate();
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('offset attribute', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" offset="-4" default_scale="day"/>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'Sunday, December 16, 2018',
|
|
'gantt view should be set to 4 days before initial date');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('default_group_by attribute', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_group_by="user_id" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row', 2,
|
|
"there should be 2 rows");
|
|
assert.strictEqual(gantt.$('.o_gantt_row:last .o_gantt_row_title').text().trim(), 'User 2',
|
|
'should be grouped by user');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('permanent_group_by attribute', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" permanent_group_by="user_id" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row', 2,
|
|
"there should be 2 rows");
|
|
assert.strictEqual(gantt.$('.o_gantt_row:last .o_gantt_row_title').text().trim(), 'User 2',
|
|
'should be grouped by user');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('default_group_by attribute with 2 fields', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_group_by="user_id,project_id"/>',
|
|
viewOptions: {
|
|
initialDate,
|
|
},
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_row_group', 2, 'there should be 2 rows.');
|
|
assert.containsN(gantt, '.o_gantt_row_nogroup', 4, 'there should be 4 sub rows.');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('dynamic_range attribute', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_group_by="user_id" dynamic_range="1" default_scale="month"/>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
var $headerScale = gantt.$el.find('.o_gantt_header_scale');
|
|
var $headerCells = $headerScale.find('.o_gantt_header_cell');
|
|
|
|
assert.strictEqual($headerCells[0].innerText.trim(), String(initialDate.getDate()),
|
|
'should start at the first record, not at the beginning of the month');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('collapse_first_level attribute with single-level grouped', async function (assert) {
|
|
assert.expect(13);
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt string="Tasks" date_start="start" date_stop="stop" collapse_first_level="1" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
});
|
|
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="project_id"/>
|
|
</form>`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_header_container',
|
|
'should have a header');
|
|
assert.ok(gantt.$buttons.find('.o_gantt_button_expand_rows').is(':visible'),
|
|
"the expand button should be visible");
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row', 4,
|
|
'should have a 4 rows');
|
|
assert.containsN(gantt, '.o_gantt_row_container .o_gantt_row.o_gantt_row_group', 2,
|
|
'should have 2 group rows');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(0) .o_gantt_row_title').text().trim(), 'Project 1',
|
|
'should contain "Project 1" in sidebar title');
|
|
assert.containsN(gantt, '.o_gantt_row:eq(1) .o_gantt_pill', 4,
|
|
'should have a 4 pills in first row');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_group:eq(1) .o_gantt_row_title').text().trim(), 'Project 2',
|
|
'should contain "Project 2" in sidebar title');
|
|
assert.containsN(gantt, '.o_gantt_row:eq(3) .o_gantt_pill', 2,
|
|
'should have a 2 pills in second row');
|
|
|
|
|
|
// open dialog to create a task
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row:nth(3) .o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
assert.strictEqual($('.modal').length, 1, 'There should be one modal opened');
|
|
assert.strictEqual($('.modal .modal-title').text(), "Create");
|
|
assert.strictEqual($('.modal .o_field_widget[name=project_id] .o_input').val(), 'Project 2',
|
|
'project_id should be set');
|
|
assert.strictEqual($('.modal .o_field_widget[name=start] .o_input').val(), '12/10/2018 00:00:00',
|
|
'start should be set');
|
|
assert.strictEqual($('.modal .o_field_widget[name = stop] .o_input').val(), '12/10/2018 23:59:59',
|
|
'stop should be set');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
// CONCURRENCY TESTS
|
|
QUnit.test('concurrent scale switches return in inverse order', async function (assert) {
|
|
assert.expect(11);
|
|
|
|
testUtils.mock.patch(GanttRenderer, {
|
|
_render: function () {
|
|
assert.step('render');
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var firstReloadProm = null;
|
|
var reloadProm = firstReloadProm;
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route) {
|
|
var result = this._super.apply(this, arguments);
|
|
if (route === '/web/dataset/search_read') {
|
|
return Promise.resolve(reloadProm).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
"should be in 'month' scale");
|
|
assert.strictEqual(gantt.model.get().records.length, 6,
|
|
"should have 6 records in the state");
|
|
|
|
// switch to 'week' scale (this rpc will be delayed)
|
|
firstReloadProm = testUtils.makeTestPromise();
|
|
reloadProm = firstReloadProm;
|
|
await testUtils.dom.click(gantt.$('.o_gantt_button_scale[data-value=week]'));
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), 'December 2018',
|
|
"should still be in 'month' scale");
|
|
assert.strictEqual(gantt.model.get().records.length, 6,
|
|
"should still have 6 records in the state");
|
|
|
|
// switch to 'year' scale
|
|
reloadProm = null;
|
|
await testUtils.dom.click(gantt.$('.o_gantt_button_scale[data-value=year]'));
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '2018',
|
|
"should be in 'year' scale");
|
|
assert.strictEqual(gantt.model.get().records.length, 7,
|
|
"should have 7 records in the state");
|
|
|
|
firstReloadProm.resolve();
|
|
|
|
assert.strictEqual(gantt.$('.o_gantt_header_container > .col > .row:first-child').text().trim(), '2018',
|
|
"should still be in 'year' scale");
|
|
assert.strictEqual(gantt.model.get().records.length, 7,
|
|
"should still have 7 records in the state");
|
|
|
|
assert.verifySteps(['render', 'render']); // should only re-render once
|
|
|
|
gantt.destroy();
|
|
testUtils.mock.unpatch(GanttRenderer);
|
|
});
|
|
|
|
QUnit.test('concurrent pill resizes return in inverse order', async function (assert) {
|
|
assert.expect(7);
|
|
|
|
testUtils.mock.patch(GanttRenderer, {
|
|
_render: function () {
|
|
assert.step('render');
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
var writeProm = testUtils.makeTestPromise();
|
|
var firstWriteProm = writeProm;
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 2]],
|
|
mockRPC: function (route, args) {
|
|
var result = this._super.apply(this, arguments);
|
|
assert.step(args.method || route);
|
|
if (args.method === 'write') {
|
|
return Promise.resolve(writeProm).then(_.constant(result));
|
|
}
|
|
return result;
|
|
},
|
|
});
|
|
|
|
var cellWidth = gantt.$('.o_gantt_cell:first').width() + 4;
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
// resize to 1 cell smaller (-1 day) ; this RPC will be delayed
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-e'),
|
|
gantt.$('.ui-resizable-e'),
|
|
{ position: { left: -cellWidth, top: 0 } }
|
|
);
|
|
|
|
// resize to two cells larger (+2 days)
|
|
writeProm = null;
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-e'),
|
|
gantt.$('.ui-resizable-e'),
|
|
{ position: { left: 2 * cellWidth, top: 0 } }
|
|
);
|
|
|
|
firstWriteProm.resolve();
|
|
|
|
await testUtils.nextTick();
|
|
|
|
assert.verifySteps([
|
|
'/web/dataset/search_read',
|
|
'render',
|
|
'write',
|
|
'write',
|
|
'/web/dataset/search_read', // should only reload once
|
|
'render', // should only re-render once
|
|
]);
|
|
|
|
gantt.destroy();
|
|
testUtils.mock.unpatch(GanttRenderer);
|
|
});
|
|
|
|
QUnit.test('concurrent pill resizes and open, dialog show updated number', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
var def = testUtils.makeTestPromise();
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [['id', '=', 2]],
|
|
mockRPC: function (route, args) {
|
|
var self = this;
|
|
if (args.method === 'write') {
|
|
var super_self = this._super
|
|
return def.then(() => {
|
|
return super_self.apply(self, arguments);
|
|
});
|
|
}
|
|
return this._super.apply(this, arguments);;
|
|
},
|
|
});
|
|
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
</form>`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
var cellWidth = gantt.$('.o_gantt_cell:first').width() + 4;
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), 'mouseover');
|
|
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.ui-resizable-e'),
|
|
gantt.$('.ui-resizable-e'),
|
|
{ position: { left: 2 * cellWidth, top: 0 } }
|
|
);
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_pill'), "click");
|
|
def.resolve();
|
|
await testUtils.nextTick();
|
|
assert.strictEqual($('.modal').find('[name=stop] input').val(), '12/24/2018 06:29:59');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('dst spring forward', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
// This is one of the few tests which have dynamic assertions, see
|
|
// our justification for it in the comment at the top of this file.
|
|
|
|
var firstStartDateUTCString = '2019-03-30 03:00:00';
|
|
var firstStartDateUTC = moment.utc(firstStartDateUTCString);
|
|
var firstStartDateLocalString = firstStartDateUTC.local().format('YYYY-MM-DD hh:mm:ss');
|
|
this.data.tasks.records.push({
|
|
id: 99,
|
|
name: 'DST Task 1',
|
|
start: firstStartDateUTCString,
|
|
stop: '2019-03-30 03:30:00',
|
|
});
|
|
|
|
var secondStartDateUTCString = '2019-03-31 03:00:00';
|
|
var secondStartDateUTC = moment.utc(secondStartDateUTCString);
|
|
var secondStartDateLocalString = secondStartDateUTC.local().format('YYYY-MM-DD hh:mm:ss');
|
|
this.data.tasks.records.push({
|
|
id: 100,
|
|
name: 'DST Task 2',
|
|
start: secondStartDateUTCString,
|
|
stop: '2019-03-31 03:30:00',
|
|
});
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_scale="day"/>',
|
|
viewOptions: {
|
|
initialDate: new Date(2019, 2, 30, 8, 0, 0),
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_cell[data-date="' + firstStartDateLocalString + '"] .o_gantt_pill_wrapper:contains(DST Task 1)',
|
|
'should be in the right cell');
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_cell[data-date="' + secondStartDateLocalString + '"] .o_gantt_pill_wrapper:contains(DST Task 2)',
|
|
'should be in the right cell');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('dst fall back', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
// This is one of the few tests which have dynamic assertions, see
|
|
// our justification for it in the comment at the top of this file.
|
|
|
|
var firstStartDateUTCString = '2019-10-26 03:00:00';
|
|
var firstStartDateUTC = moment.utc(firstStartDateUTCString);
|
|
var firstStartDateLocalString = firstStartDateUTC.local().format('YYYY-MM-DD hh:mm:ss');
|
|
this.data.tasks.records.push({
|
|
id: 99,
|
|
name: 'DST Task 1',
|
|
start: firstStartDateUTCString,
|
|
stop: '2019-10-26 03:30:00',
|
|
});
|
|
|
|
var secondStartDateUTCString = '2019-10-27 03:00:00';
|
|
var secondStartDateUTC = moment.utc(secondStartDateUTCString);
|
|
var secondStartDateLocalString = secondStartDateUTC.local().format('YYYY-MM-DD hh:mm:ss');
|
|
this.data.tasks.records.push({
|
|
id: 100,
|
|
name: 'DST Task 2',
|
|
start: secondStartDateUTCString,
|
|
stop: '2019-10-27 03:30:00',
|
|
});
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" default_scale="day"/>',
|
|
viewOptions: {
|
|
initialDate: new Date(2019, 9, 26, 8, 0, 0),
|
|
},
|
|
});
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_cell[data-date="' + firstStartDateLocalString + '"] .o_gantt_pill_wrapper:contains(DST Task 1)',
|
|
'should be in the right cell');
|
|
|
|
await testUtils.dom.click(gantt.$buttons.find('.o_gantt_button_next'));
|
|
|
|
assert.containsOnce(gantt, '.o_gantt_row_container .o_gantt_cell[data-date="' + secondStartDateLocalString + '"] .o_gantt_pill_wrapper:contains(DST Task 2)',
|
|
'should be in the right cell');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
// OTHER TESTS
|
|
|
|
QUnit.skip('[for manual testing] scripting time of large amount of records (ungrouped)', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
this.data.tasks.records = [];
|
|
for (var i = 1; i <= 1000; i++) {
|
|
this.data.tasks.records.push({
|
|
id: i,
|
|
name: 'Task ' + i,
|
|
start: '2018-12-01 00:00:00',
|
|
stop: '2018-12-02 00:00:00',
|
|
});
|
|
}
|
|
|
|
createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
});
|
|
|
|
QUnit.skip('[for manual testing] scripting time of large amount of records (one level grouped)', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
this.data.tasks.records = [];
|
|
this.data.users.records = [];
|
|
|
|
var i;
|
|
for (i = 1; i <= 100; i++) {
|
|
this.data.users.records.push({
|
|
id: i,
|
|
name: i,
|
|
});
|
|
}
|
|
|
|
for (i = 1; i <= 10000; i++) {
|
|
var day1 = (i % 30) + 1;
|
|
var day2 = ((i % 30) + 2);
|
|
if (day1 < 10) {
|
|
day1 = '0' + day1;
|
|
}
|
|
if (day2 < 10) {
|
|
day2 = '0' + day2;
|
|
}
|
|
this.data.tasks.records.push({
|
|
id: i,
|
|
name: 'Task ' + i,
|
|
user_id: Math.floor(Math.random() * Math.floor(100)) + 1,
|
|
start: '2018-12-' + day1,
|
|
stop: '2018-12-' + day2,
|
|
});
|
|
}
|
|
|
|
createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id'],
|
|
});
|
|
});
|
|
|
|
QUnit.skip('[for manual testing] scripting time of large amount of records (two level grouped)', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
this.data.tasks.records = [];
|
|
this.data.users.records = [];
|
|
var stages = this.data.tasks.fields.stage.selection;
|
|
|
|
var i;
|
|
for (i = 1; i <= 100; i++) {
|
|
this.data.users.records.push({
|
|
id: i,
|
|
name: i,
|
|
});
|
|
}
|
|
|
|
for (i = 1; i <= 10000; i++) {
|
|
this.data.tasks.records.push({
|
|
id: i,
|
|
name: 'Task ' + i,
|
|
stage: stages[i % 2][0],
|
|
user_id: (i % 100) + 1,
|
|
start: '2018-12-01 00:00:00',
|
|
stop: '2018-12-02 00:00:00',
|
|
});
|
|
}
|
|
|
|
createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['user_id', 'stage'],
|
|
});
|
|
});
|
|
|
|
QUnit.test('delete attribute on dialog', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" delete="0"/>',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="name"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
<field name="stage"/>
|
|
<field name="project_id"/>
|
|
<field name="user_id"/>
|
|
</form>`,
|
|
}
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_row_container .o_gantt_row .o_gantt_cell[data-date="2018-12-17 00:00:00"] .o_gantt_pill'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
assert.containsOnce(document.body, '.modal-dialog',
|
|
'Should have opened a new dialog');
|
|
assert.containsNone($('.modal-dialog'), '.o_form_button_remove',
|
|
'should not have the "Remove" Button form dialog');
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('move a pill in multi-level grop row after collapse and expand grouped row', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
groupBy: ['project_id', 'stage'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'write') {
|
|
assert.deepEqual(args.args[0], [7],
|
|
"should write on the correct record");
|
|
assert.deepEqual(args.args[1], {
|
|
project_id: 1,
|
|
start: "2018-12-02 10:30:12",
|
|
stop: "2018-12-02 18:29:59",
|
|
}, "all modified fields should be correctly set");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
domain: [['id', 'in', [1, 7]]],
|
|
});
|
|
|
|
assert.containsN(gantt, '.o_gantt_pill', 4,
|
|
"there should be two pills (task 1 and task 7) and two pills for group header combined");
|
|
|
|
await testUtils.dom.click(gantt.$('.o_gantt_row_container > .o_gantt_row:first .o_gantt_row_title'));
|
|
assert.doesNotHaveClass(gantt.$('.o_gantt_row_container > .o_gantt_row:first'), 'open',
|
|
"'Project 1' group should be collapsed");
|
|
await testUtils.dom.click(gantt.$('.o_gantt_row_container > .o_gantt_row:first .o_gantt_row_title'));
|
|
assert.hasClass(gantt.$('.o_gantt_row_container > .o_gantt_row:first'), 'open',
|
|
"'Project 1' group should be expanded");
|
|
|
|
// move a pill (task 7) in the other row and in the the next cell (+1 day)
|
|
const cellWidth = gantt.$('.o_gantt_header_scale .o_gantt_header_cell:first')[0].getBoundingClientRect().width + 4;
|
|
const cellHeight = gantt.$('.o_gantt_cell:first').height();
|
|
await testUtils.dom.dragAndDrop(
|
|
gantt.$('.o_gantt_pill[data-id=7]'),
|
|
gantt.$('.o_gantt_pill[data-id=1]'),
|
|
{ position: { left: cellWidth, top: -cellHeight } },
|
|
);
|
|
assert.containsOnce(gantt, '.o_gantt_row_group', 1, "should have one group row");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test("plan dialog initial domain has the action domain as its only base",
|
|
async function (assert) {
|
|
assert.expect(14);
|
|
|
|
const unpatchDate = patchDate(2018, 11, 20, 8, 0, 0);
|
|
registerCleanup(unpatchDate);
|
|
|
|
patchWithCleanup(session, {
|
|
getTZOffset() {
|
|
return 0;
|
|
},
|
|
});
|
|
|
|
const views = {
|
|
"tasks,false,gantt": `<gantt date_start="start" date_stop="stop"/>`,
|
|
"tasks,false,list": `<tree><field name="name"/></tree>`,
|
|
"tasks,false,search": `
|
|
<search>
|
|
<filter name="project_one" string="Project 1" domain="[('project_id', '=', 1)]"/>
|
|
</search>
|
|
`,
|
|
};
|
|
const serverData = {
|
|
models: this.data,
|
|
views,
|
|
};
|
|
const target = getFixture();
|
|
const webClient = await createWebClient({
|
|
serverData,
|
|
mockRPC: function (route, args) {
|
|
if (args.method === "web_search_read") {
|
|
assert.step(args.kwargs.domain.toString());
|
|
}
|
|
if (route === "/web/dataset/search_read") {
|
|
assert.step(args.domain.toString());
|
|
}
|
|
},
|
|
});
|
|
|
|
const ganttAction = {
|
|
name: "Tasks Gantt",
|
|
res_model: "tasks",
|
|
type: "ir.actions.act_window",
|
|
views: [[false, "gantt"]],
|
|
};
|
|
|
|
// Load action without domain and open plan dialog
|
|
await doAction(webClient, ganttAction);
|
|
assert.verifySteps(["start,<=,2018-12-31 23:59:59,stop,>=,2018-12-01 00:00:00"]);
|
|
await testUtils.dom.triggerMouseEvent($(target).find(".o_gantt_cell_plan:first"), "click");
|
|
await nextTick();
|
|
assert.verifySteps(["|,start,=,false,stop,=,false"]);
|
|
|
|
// Load action WITH domain and open plan dialog
|
|
await doAction(webClient, {
|
|
...ganttAction,
|
|
domain: [["project_id", "=", 1]],
|
|
});
|
|
assert.verifySteps(["project_id,=,1,start,<=,2018-12-31 23:59:59,stop,>=,2018-12-01 00:00:00"]);
|
|
|
|
await testUtils.dom.triggerMouseEvent($(target).find(".o_gantt_cell_plan:first"), "click");
|
|
await nextTick();
|
|
assert.verifySteps(["&,project_id,=,1,|,start,=,false,stop,=,false"]);
|
|
|
|
// Load action without domain, activate a filter and then open plan dialog
|
|
await doAction(webClient, ganttAction);
|
|
assert.verifySteps(["start,<=,2018-12-31 23:59:59,stop,>=,2018-12-01 00:00:00"]);
|
|
|
|
await toggleFilterMenu(target);
|
|
await nextTick();
|
|
await toggleMenuItem(target, "Project 1");
|
|
await nextTick();
|
|
assert.verifySteps(["project_id,=,1,start,<=,2018-12-31 23:59:59,stop,>=,2018-12-01 00:00:00"]);
|
|
|
|
await testUtils.dom.triggerMouseEvent($(target).find(".o_gantt_cell_plan:first"), "click");
|
|
await nextTick();
|
|
assert.verifySteps(["|,start,=,false,stop,=,false"]);
|
|
}
|
|
);
|
|
|
|
QUnit.test('No progress bar when no option set.', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"/>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_progress_bar') {
|
|
// Should never assert this - will be reported in assert.expect count.
|
|
assert.strictEqual(args.method, "gantt_progress_bar");
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const progressbar = gantt.el.querySelectorAll('.o_gantt_row_sidebar .o_gantt_progressbar');
|
|
assert.strictEqual(progressbar.length, 0, "Gantt should not include any progressbar");
|
|
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Progress bar rpc is triggered when option set.', async function (assert) {
|
|
assert.expect(8);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"
|
|
default_group_by="user_id"
|
|
progress_bar="user_id">
|
|
<field name="user_id"/>
|
|
</gantt>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_progress_bar') {
|
|
assert.strictEqual(args.model, "tasks");
|
|
assert.deepEqual(args.args[0], ['user_id']);
|
|
assert.deepEqual(args.args[1], {user_id: [1, 2]});
|
|
return Promise.resolve({
|
|
user_id: {
|
|
1: {value: 50, max_value: 100},
|
|
2: {value: 25, max_value: 200},
|
|
}
|
|
});
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const progressbar = gantt.el.querySelectorAll('.o_gantt_row_sidebar .o_gantt_progressbar');
|
|
assert.strictEqual(progressbar.length, 2, "Gantt should include two progressbars");
|
|
assert.strictEqual(progressbar[0].style.width, "50%");
|
|
assert.strictEqual(progressbar[1].style.width, "12.5%");
|
|
assert.hasClass(progressbar[0], "o_gantt_group_success", "Progress bar should have the success class");
|
|
assert.hasClass(progressbar[1], "o_gantt_group_success", "Progress bar should have the success class");
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Progress bar warning when max_value is zero', async function (assert) {
|
|
assert.expect(5);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"
|
|
default_group_by="user_id"
|
|
progress_bar="user_id">
|
|
<field name="user_id"/>
|
|
</gantt>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_progress_bar') {
|
|
assert.strictEqual(args.model, "tasks");
|
|
assert.deepEqual(args.args[0], ['user_id']);
|
|
assert.deepEqual(args.args[1], {user_id: [1, 2]});
|
|
return Promise.resolve({
|
|
user_id: {
|
|
1: {value: 50, max_value: 0},
|
|
}
|
|
});
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const progressbar = gantt.el.querySelectorAll('.o_gantt_row_sidebar .o_gantt_progressbar');
|
|
assert.strictEqual(progressbar.length, 0, "Gantt should not include progressbar");
|
|
const warning = gantt.el.querySelectorAll('.o_gantt_row_sidebar .fa-exclamation-triangle');
|
|
assert.strictEqual(warning.length, 1, "Gantt should include a warning");
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Progress bar danger when ratio > 100', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"
|
|
default_group_by="user_id"
|
|
progress_bar="user_id">
|
|
<field name="user_id"/>
|
|
</gantt>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_progress_bar') {
|
|
assert.strictEqual(args.model, "tasks");
|
|
assert.deepEqual(args.args[0], ['user_id']);
|
|
assert.deepEqual(args.args[1], {user_id: [1, 2]});
|
|
return Promise.resolve({
|
|
user_id: {
|
|
1: {value: 150, max_value: 100},
|
|
}
|
|
});
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const progressbar = gantt.el.querySelectorAll('.o_gantt_row_sidebar .o_gantt_progressbar');
|
|
assert.strictEqual(progressbar.length, 1, "Gantt should include one progressbar");
|
|
assert.strictEqual(progressbar[0].style.width, "100%", "Progress bar should have the maximal width");
|
|
assert.hasClass(progressbar[0], "o_gantt_group_danger", "Progress bar should have the danger class");
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Falsy search field will return an empty rows', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"
|
|
progress_bar="user_id">
|
|
<field name="user_id"/>
|
|
</gantt>`,
|
|
groupBy: ['project_id', 'user_id'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [["id", "=", 5]],
|
|
});
|
|
|
|
const progressbar = gantt.el.querySelectorAll('.o_gantt_row_sidebar .o_gantt_progressbar');
|
|
assert.containsOnce(gantt, '.o_gantt_row_sidebar_empty', 'should have empty rows');
|
|
assert.strictEqual(progressbar.length, 0, "Gantt should not have any progressbars");
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Search field return rows with progressbar', async function (assert) {
|
|
assert.expect(6);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"
|
|
progress_bar="user_id">
|
|
<field name="user_id"/>
|
|
</gantt>`,
|
|
groupBy: ['project_id', 'user_id'],
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
domain: [["id", "=", 2]],
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'gantt_progress_bar') {
|
|
assert.strictEqual(args.model, "tasks");
|
|
assert.deepEqual(args.args[0], ['user_id']);
|
|
assert.deepEqual(args.args[1], {user_id: [2]});
|
|
return Promise.resolve({
|
|
user_id: {
|
|
2: {value: 25, max_value: 200},
|
|
}
|
|
});
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
|
|
const progressbar = gantt.el.querySelectorAll('.o_gantt_row_sidebar .o_gantt_progressbar');
|
|
assert.containsNone(gantt, '.o_gantt_row_sidebar_empty', 'should have rows');
|
|
assert.strictEqual(gantt.$('.o_gantt_row_container .o_gantt_row:nth(1) .o_gantt_row_title').text().trim(), 'User 2',
|
|
'2nd row title should be "User 2"');
|
|
assert.strictEqual(progressbar.length, 1, "Gantt should have one progressbar");
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('add record in empty gantt', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
this.data.tasks.records = [];
|
|
this.data.tasks.fields.stage_id.domain = "[('id', '!=', False)]";
|
|
|
|
var gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: '<gantt date_start="start" date_stop="stop" />',
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
groupBy: ['project_id'],
|
|
});
|
|
|
|
const views = {
|
|
'tasks,false,form': `
|
|
<form>
|
|
<field name="stage_id" widget="statusbar"/>
|
|
<field name="project_id"/>
|
|
<field name="start"/>
|
|
<field name="stop"/>
|
|
</form>`,
|
|
};
|
|
await prepareWowlFormViewDialogs({ models: this.data, views });
|
|
|
|
await testUtils.dom.triggerMouseEvent(gantt.$('.o_gantt_cell[data-date="2018-12-10 00:00:00"] .o_gantt_cell_add'), "click");
|
|
await testUtils.nextTick();
|
|
|
|
assert.strictEqual($('.modal').length, 1, 'There should be one modal opened');
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('Only the task name appears in the pill title when the pill_label option is not set', async function (assert) {
|
|
assert.expect(1);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"/>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
const pill = gantt.el.querySelector('.o_gantt_pill_title');
|
|
assert.strictEqual(pill.textContent, 'Task 1', "The pill should not include DateTime in the title.");
|
|
gantt.destroy();
|
|
});
|
|
|
|
QUnit.test('The date and task name appears in the pill title when the pill_label option is set', async function (assert) {
|
|
assert.expect(2);
|
|
|
|
const gantt = await createView({
|
|
View: GanttView,
|
|
model: 'tasks',
|
|
data: this.data,
|
|
arch: `<gantt date_start="start" date_stop="stop"
|
|
default_scale="week" scales="week"
|
|
pill_label="True"/>`,
|
|
viewOptions: {
|
|
initialDate: initialDate,
|
|
},
|
|
});
|
|
|
|
const pills = gantt.el.querySelectorAll('.o_gantt_pill_title');
|
|
assert.strictEqual(pills[0].innerText, '11/30 - 12/31 - Task 1', "The task span across in week then DateTime should be displayed on the pill label.");
|
|
assert.strictEqual(pills[1].innerText, 'Task 2', "The task does not span across in week scale then DateTime shouldn't be displayed on the pill label.");
|
|
gantt.destroy();
|
|
});
|
|
});
|
|
});
|