odoo.define('web.graph_view_tests', function (require) {
"use strict";
var concurrency = require('web.concurrency');
var controlPanelViewParameters = require('web.controlPanelViewParameters');
var GraphView = require('web.GraphView');
var testUtils = require('web.test_utils');
var createActionManager = testUtils.createActionManager;
var createView = testUtils.createView;
var patchDate = testUtils.mock.patchDate;
var INTERVAL_OPTIONS = controlPanelViewParameters.INTERVAL_OPTIONS.map(o => o.optionId);
var TIME_RANGE_OPTIONS = controlPanelViewParameters.TIME_RANGE_OPTIONS.map(o => o.optionId);
var COMPARISON_TIME_RANGE_OPTIONS = controlPanelViewParameters.COMPARISON_TIME_RANGE_OPTIONS.map(o => o.optionId);
var f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e))));
var cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a);
var COMBINATIONS = cartesian(TIME_RANGE_OPTIONS, COMPARISON_TIME_RANGE_OPTIONS);
var COMBINATIONS_WITH_DATE = cartesian(TIME_RANGE_OPTIONS, COMPARISON_TIME_RANGE_OPTIONS, INTERVAL_OPTIONS);
QUnit.assert.checkDatasets = function (graph, keys, expectedDatasets) {
keys = keys instanceof Array ? keys : [keys];
expectedDatasets = expectedDatasets instanceof Array ?
expectedDatasets :
[expectedDatasets];
var datasets = graph.renderer.chart.data.datasets;
var actualValues = datasets.map(dataset => _.pick(dataset, keys));
this.pushResult({
result: _.isEqual(actualValues, expectedDatasets),
actual: actualValues,
expected: expectedDatasets,
});
};
QUnit.assert.checkLabels = function (graph, expectedLabels) {
var labels = graph.renderer.chart.data.labels;
this.pushResult({
result: _.isEqual(labels, expectedLabels),
actual: labels,
expected: expectedLabels,
});
};
QUnit.assert.checkLegend = function (graph, expectedLegendLabels) {
expectedLegendLabels = expectedLegendLabels instanceof Array ?
expectedLegendLabels :
[expectedLegendLabels];
var chart = graph.renderer.chart;
var actualLegendLabels = chart.config.options.legend.labels.generateLabels(chart).map(o => o.text);
this.pushResult({
result: _.isEqual(actualLegendLabels, expectedLegendLabels),
actual: actualLegendLabels,
expected: expectedLegendLabels,
});
};
QUnit.module('Views', {
beforeEach: function () {
this.data = {
foo: {
fields: {
foo: {string: "Foo", type: "integer", store: true},
bar: {string: "bar", type: "boolean"},
product_id: {string: "Product", type: "many2one", relation: 'product', store: true},
color_id: {string: "Color", type: "many2one", relation: 'color'},
date: {string: "Date", type: 'date', store: true, sortable: true},
},
records: [
{id: 1, foo: 3, bar: true, product_id: 37, date: "2016-01-01"},
{id: 2, foo: 53, bar: true, product_id: 37, color_id: 7, date: "2016-01-03"},
{id: 3, foo: 2, bar: true, product_id: 37, date: "2016-03-04"},
{id: 4, foo: 24, bar: false, product_id: 37, date: "2016-03-07"},
{id: 5, foo: 4, bar: false, product_id: 41, date: "2016-05-01"},
{id: 6, foo: 63, bar: false, product_id: 41},
{id: 7, foo: 42, bar: false, product_id: 41},
{id: 8, foo: 48, bar: false, product_id: 41, date: "2016-04-01"},
]
},
product: {
fields: {
name: {string: "Product Name", type: "char"}
},
records: [{
id: 37,
display_name: "xphone",
}, {
id: 41,
display_name: "xpad",
}]
},
color: {
fields: {
name: {string: "Color", type: "char"}
},
records: [{
id: 7,
display_name: "red",
}, {
id: 14,
display_name: "black",
}]
},
};
}
}, function () {
QUnit.module('GraphView');
QUnit.test('simple graph rendering', async function (assert) {
assert.expect(5);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.containsOnce(graph, 'div.o_graph_canvas_container canvas',
"should contain a div with a canvas element");
assert.strictEqual(graph.renderer.state.mode, "bar",
"should be in bar chart mode by default");
assert.checkLabels(graph, [[true], [false]]);
assert.checkDatasets(graph,
['backgroundColor', 'data', 'label', 'originIndex', 'stack'],
{
backgroundColor: "#1f77b4",
data: [3,5],
label: "Count",
originIndex: 0,
stack: "",
}
);
assert.checkLegend(graph, 'Count');
graph.destroy();
});
QUnit.test('default type attribute', async function (assert) {
assert.expect(1);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.strictEqual(graph.renderer.state.mode, "pie", "should be in pie chart mode by default");
graph.destroy();
});
QUnit.test('title attribute', async function (assert) {
assert.expect(1);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.strictEqual(graph.$('.o_graph_renderer label').text(), "Partners",
"should have 'Partners as title'");
graph.destroy();
});
QUnit.test('field id not in groupBy', async function (assert) {
assert.expect(1);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
mockRPC: function (route, args) {
if (args.method === 'read_group') {
assert.deepEqual(args.kwargs.groupby, [],
'groupby should not contain id field');
}
return this._super.apply(this, arguments);
}
});
graph.destroy();
});
QUnit.test('switching mode', async function (assert) {
assert.expect(6);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.strictEqual(graph.renderer.state.mode, "line", "should be in line chart mode by default");
assert.doesNotHaveClass(graph.$buttons.find('button[data-mode="bar"]'), 'active',
'bar type button should not be active');
assert.hasClass(graph.$buttons.find('button[data-mode="line"]'),'active',
'line type button should be active');
await testUtils.dom.click(graph.$buttons.find('button[data-mode="bar"]'));
assert.strictEqual(graph.renderer.state.mode, "bar", "should be in bar chart mode by default");
assert.doesNotHaveClass(graph.$buttons.find('button[data-mode="line"]'), 'active',
'line type button should not be active');
assert.hasClass(graph.$buttons.find('button[data-mode="bar"]'),'active',
'bar type button should be active');
graph.destroy();
});
QUnit.test('displaying line chart with only 1 data point', async function (assert) {
assert.expect(1);
// this test makes sure the line chart does not crash when only one data
// point is displayed.
this.data.foo.records = this.data.foo.records.slice(0,1);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.containsOnce(graph, 'canvas', "should have a canvas");
graph.destroy();
});
QUnit.test('displaying chart data with multiple groupbys', async function (assert) {
// this test makes sure the line chart shows all data labels (X axis) when
// it is grouped by several fields
assert.expect(6);
var graph = await createView({
View: GraphView,
model: 'foo',
data: this.data,
arch: '',
groupBy: ['product_id', 'bar', 'color_id'],
});
assert.checkLabels(graph, [['xphone'], ['xpad']]);
assert.checkLegend(graph, ['true/Undefined', 'true/red', 'false/Undefined']);
await testUtils.dom.click(graph.$buttons.find('button[data-mode="line"]'));
assert.checkLabels(graph, [['xphone'], ['xpad']]);
assert.checkLegend(graph, ['true/Undefined', 'true/red', 'false/Undefined']);
await testUtils.dom.click(graph.$buttons.find('button[data-mode="pie"]'));
assert.checkLabels(graph, [
["xphone", true, "Undefined"],
["xphone", true,"red"],
["xphone", false, "Undefined"],
["xpad", false, "Undefined"]
]);
assert.checkLegend(graph, [
'xphone/true/Undefined',
'xphone/true/red',
'xphone/false/Undefined',
'xpad/false/Undefined'
]);
graph.destroy();
});
QUnit.test('switching measures', async function (assert) {
assert.expect(2);
var rpcCount = 0;
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
mockRPC: function (route, args) {
rpcCount++;
return this._super(route, args);
},
});
testUtils.dom.click(graph.$buttons.find('.dropdown-toggle:contains(Measures)'));
await testUtils.dom.click(graph.$buttons.find('.dropdown-item[data-field="foo"]'));
assert.checkLegend(graph, 'Foo');
assert.strictEqual(rpcCount, 2, "should have done 2 rpcs (2 readgroups)");
graph.destroy();
});
QUnit.test('no content helper (bar chart)', async function (assert) {
assert.expect(2);
this.data.foo.records = [];
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.containsNone(graph, 'div.o_graph_canvas_container canvas',
"should not contain a div with a canvas element");
assert.containsOnce(graph, 'div.o_view_nocontent',
"should display the no content helper");
graph.destroy();
});
QUnit.test('no content helper (pie chart)', async function (assert) {
assert.expect(2);
this.data.foo.records = []
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.containsNone(graph, 'div.o_graph_canvas_container canvas',
"should not contain a div with a canvas element");
assert.containsOnce(graph, 'div.o_view_nocontent',
"should display the no content helper");
graph.destroy();
});
QUnit.test('render pie chart in comparison mode', async function (assert) {
assert.expect(2);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
context: {
timeRangeMenuData: {
//Q3 2018
timeRange: ['&', ["date", ">=", "2018-07-01"],["date", "<=", "2018-09-30"]],
timeRangeDescription: 'This Quarter',
//Q4 2018
comparisonTimeRange: ['&', ["date", ">=", "2018-10-01"],["date", "<=", "2018-12-31"]],
comparisonTimeRangeDescription: 'Previous Period',
},
},
arch: '' +
'' +
'',
});
assert.containsNone(graph, 'div.o_view_nocontent',
"should not display the no content helper");
assert.checkLegend(graph, 'No data');
graph.destroy();
});
QUnit.test('no content helper after update', async function (assert) {
assert.expect(4);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.containsOnce(graph, 'div.o_graph_canvas_container canvas',
"should contain a div with a canvas element");
assert.containsNone(graph, 'div.o_view_nocontent',
"should not display the no content helper");
await testUtils.graph.reload(graph, {domain: [['product_id', '=', 4]]});
assert.containsNone(graph, 'div.o_graph_canvas_container canvas',
"should not contain a div with a canvas element");
assert.containsOnce(graph, 'div.o_view_nocontent',
"should display the no content helper");
graph.destroy();
});
QUnit.test('can reload with other group by', async function (assert) {
assert.expect(2);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.checkLabels(graph, [['xphone'], ['xpad']]);
await testUtils.graph.reload(graph, {groupBy: ['color_id']});
assert.checkLabels(graph, [['Undefined'], ['red']]);
graph.destroy();
});
QUnit.test('getOwnedQueryParams correctly returns mode, measure, and groupbys', async function (assert) {
assert.expect(4);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.deepEqual(graph.getOwnedQueryParams(), {
context: {
graph_mode: 'bar',
graph_measure: '__count__',
graph_groupbys: ['product_id'],
}
}, "context should be correct");
testUtils.dom.click(graph.$buttons.find('.dropdown-toggle:contains(Measures)'));
await testUtils.dom.click(graph.$buttons.find('.dropdown-item[data-field="foo"]'));
assert.deepEqual(graph.getOwnedQueryParams(), {
context: {
graph_mode: 'bar',
graph_measure: 'foo',
graph_groupbys: ['product_id'],
},
}, "context should be correct");
await testUtils.dom.click(graph.$buttons.find('button[data-mode="line"]'));
assert.deepEqual(graph.getOwnedQueryParams(), {
context: {
graph_mode: 'line',
graph_measure: 'foo',
graph_groupbys: ['product_id'],
},
}, "context should be correct");
await testUtils.graph.reload(graph, {groupBy: ['product_id', 'color_id']}); // change groupbys
assert.deepEqual(graph.getOwnedQueryParams(), {
context: {
graph_mode: 'line',
graph_measure: 'foo',
graph_groupbys: ['product_id', 'color_id'],
},
}, "context should be correct");
graph.destroy();
});
QUnit.test('correctly uses graph_ keys from the context', async function (assert) {
assert.expect(5);
var lastOne = _.last(this.data.foo.records);
lastOne.color_id = 14;
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '',
viewOptions: {
context: {
graph_measure: 'foo',
graph_mode: 'line',
graph_groupbys: ['color_id'],
},
},
});
// check measure name is present in legend
assert.checkLegend(graph, 'Foo');
// check mode
assert.strictEqual(graph.renderer.state.mode, "line", "should be in line chart mode");
assert.doesNotHaveClass(graph.$buttons.find('button[data-mode="bar"]'), 'active',
'bar chart button should not be active');
assert.hasClass(graph.$buttons.find('button[data-mode="line"]'),'active',
'line chart button should be active');
// check groupby values ('Undefined' is rejected in line chart) are in labels
assert.checkLabels(graph, [['red'], ['black']]);
graph.destroy();
});
QUnit.test('correctly use group_by key from the context', async function (assert) {
assert.expect(1);
var lastOne = _.last(this.data.foo.records);
lastOne.color_id = 14;
var graph = await createView({
View: GraphView,
model: 'foo',
data: this.data,
arch: '',
groupBy: ['color_id'],
viewOptions: {
context: {
graph_measure: 'foo',
graph_mode: 'line',
},
},
});
// check groupby values ('Undefined' is rejected in line chart) are in labels
assert.checkLabels(graph, [['red'], ['black']]);
graph.destroy();
});
QUnit.test('correctly uses graph_ keys from the context (at reload)', async function (assert) {
assert.expect(7);
var lastOne = _.last(this.data.foo.records);
lastOne.color_id = 14;
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '',
});
assert.strictEqual(graph.renderer.state.mode, "bar", "should be in bar chart mode");
assert.hasClass(graph.$buttons.find('button[data-mode="bar"]'),'active',
'bar chart button should be active');
var reloadParams = {
context: {
graph_measure: 'foo',
graph_mode: 'line',
graph_groupbys: ['color_id'],
},
};
await testUtils.graph.reload(graph, reloadParams);
// check measure
assert.checkLegend(graph, 'Foo');
// check mode
assert.strictEqual(graph.renderer.state.mode, "line", "should be in line chart mode");
assert.doesNotHaveClass(graph.$buttons.find('button[data-mode="bar"]'), 'active',
'bar chart button should not be active');
assert.hasClass(graph.$buttons.find('button[data-mode="line"]'),'active',
'line chart button should be active');
// check groupby values ('Undefined' is rejected in line chart) are in labels
assert.checkLabels(graph, [['red'], ['black']])
graph.destroy();
});
QUnit.test('reload graph with correct fields', async function (assert) {
assert.expect(2);
var graph = await createView({
View: GraphView,
model: 'foo',
data: this.data,
arch: '' +
'' +
'' +
'',
mockRPC: function (route, args) {
if (args.method === 'read_group') {
assert.deepEqual(args.kwargs.fields, ['product_id', 'foo'],
"should read the correct fields");
}
return this._super.apply(this, arguments);
},
});
await testUtils.graph.reload(graph, {groupBy: []});
graph.destroy();
});
QUnit.test('initial groupby is kept when reloading', async function (assert) {
assert.expect(8);
var graph = await createView({
View: GraphView,
model: 'foo',
data: this.data,
arch: '' +
'' +
'' +
'',
mockRPC: function (route, args) {
if (args.method === 'read_group') {
assert.deepEqual(args.kwargs.groupby, ['product_id'],
"should group by the correct field");
}
return this._super.apply(this, arguments);
},
});
assert.checkLabels(graph, [['xphone'], ['xpad']]);
assert.checkLegend(graph, 'Foo');
assert.checkDatasets(graph, 'data', {data: [82, 157]});
await testUtils.graph.reload(graph, {groupBy: []});
assert.checkLabels(graph, [['xphone'], ['xpad']]);
assert.checkLegend(graph, 'Foo');
assert.checkDatasets(graph, 'data', {data: [82, 157]});
graph.destroy();
});
QUnit.test('use a many2one as a measure should work (without groupBy)', async function (assert) {
assert.expect(4);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.containsOnce(graph, 'div.o_graph_canvas_container canvas',
"should contain a div with a canvas element");
assert.checkLabels(graph, [[]]);
assert.checkLegend(graph, 'Product');
assert.checkDatasets(graph, 'data', {data: [2]});
graph.destroy();
});
QUnit.test('use a many2one as a measure should work (with groupBy)', async function (assert) {
assert.expect(5);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'' +
'',
});
assert.containsOnce(graph, 'div.o_graph_canvas_container canvas',
"should contain a div with a canvas element");
assert.strictEqual(graph.renderer.state.mode, "bar",
"should be in bar chart mode by default");
assert.checkLabels(graph, [[true], [false]]);
assert.checkLegend(graph, 'Product');
assert.checkDatasets(graph, 'data', {data: [1, 2]});
graph.destroy();
});
QUnit.test('use a many2one as a measure and as a groupby should work', async function (assert) {
assert.expect(3);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
viewOptions: {
additionalMeasures: ['product_id'],
},
});
// need to set the measure this way because it cannot be set in the
// arch.
await testUtils.dom.click(graph.$buttons.find('.dropdown-toggle:contains(Measures)'));
await testUtils.dom.click(graph.$buttons.find('.dropdown-item[data-field="product_id"]'));
assert.checkLabels(graph, [['xphone'], ['xpad']]);
assert.checkLegend(graph, 'Product');
assert.checkDatasets(graph, 'data', {data: [1, 1]});
graph.destroy();
});
QUnit.test('not use a many2one as a measure by default', async function (assert) {
assert.expect(1);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.notOk(graph.measures.product_id,
"should not have product_id as measure");
graph.destroy();
});
QUnit.test('use a many2one as a measure if set as additional fields', async function (assert) {
assert.expect(1);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
viewOptions: {
additionalMeasures: ['product_id'],
},
});
assert.ok(graph.measures.product_id,
"should have product_id as measure");
graph.destroy();
});
QUnit.test('graph view crash when moving from search view using Down key', async function (assert) {
assert.expect(1);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
graph.renderer.giveFocus();
assert.ok(true,"should not generate any error");
graph.destroy();
});
QUnit.test('graph measures should be alphabetically sorted', async function (assert) {
assert.expect(2);
var data = this.data;
data.foo.fields.bouh = {string: "bouh", type: "integer"};
var graph = await createView({
View: GraphView,
model: "foo",
data: data,
arch: '' +
'' +
'' +
'',
});
assert.strictEqual(graph.$buttons.find('.o_graph_measures_list .dropdown-item:first').data('field'), 'bouh',
"Bouh should be the first measure");
assert.strictEqual(graph.$buttons.find('.o_graph_measures_list .dropdown-item:last').data('field'), '__count__',
"Count should be the last measure");
graph.destroy();
});
QUnit.test('Undefined should appear in bar, pie graph but not in line graph', async function (assert) {
assert.expect(3);
var graph = await createView({
View: GraphView,
model: "foo",
groupBy:['date'],
data: this.data,
arch: '' +
'' +
'',
});
function _indexOf (label) {
return graph.renderer._indexOf(graph.renderer.chart.data.labels, label)
}
assert.strictEqual(_indexOf(['Undefined']), -1);
await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=bar]'));
assert.ok(_indexOf(['Undefined']) >= 0);
await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=pie]'));
assert.ok(_indexOf(['Undefined']) >= 0);
graph.destroy();
});
QUnit.test('Undefined should appear in bar, pie graph but not in line graph with multiple groupbys', async function (assert) {
assert.expect(4);
var graph = await createView({
View: GraphView,
model: "foo",
groupBy:['date', 'color_id'],
data: this.data,
arch: '' +
'' +
'',
});
function _indexOf (label) {
return graph.renderer._indexOf(graph.renderer.chart.data.labels, label)
}
assert.strictEqual(_indexOf(['Undefined']), -1);
await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=bar]'));
assert.ok(_indexOf(['Undefined']) >= 0);
await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=pie]'));
var labels = graph.renderer.chart.data.labels
assert.ok(labels.filter(label => /Undefined/.test(label.join(''))).length >= 1);
// Undefined should not appear after switching back to line chart
await testUtils.dom.click(graph.$buttons.find('.o_graph_button[data-mode=line]'));
assert.strictEqual(_indexOf(['Undefined']), -1);
graph.destroy();
});
QUnit.test('no comparison and no groupby', async function (assert) {
assert.expect(9);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
});
assert.checkLabels(graph, [[]]);
assert.checkLegend(graph, 'Foo');
assert.checkDatasets(graph, 'data', {data: [239]});
await testUtils.dom.click(graph.$('.o_graph_button[data-mode=line]'));
// the labels in line chart is translated in this case to avoid to have a single
// point at the left of the screen and chart to seem empty.
assert.checkLabels(graph, [[''], [], ['']]);
assert.checkLegend(graph, 'Foo');
assert.checkDatasets(graph, 'data', {data: [undefined, 239]});
await testUtils.dom.click(graph.$('.o_graph_button[data-mode=pie]'));
assert.checkLabels(graph, [[]]);
assert.checkLegend(graph, 'Total');
assert.checkDatasets(graph, 'data', {data: [239]});
graph.destroy();
});
QUnit.test('no comparison and one groupby', async function (assert) {
assert.expect(9);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'' +
'',
});
assert.checkLabels(graph, [[true], [false]]);
assert.checkLegend(graph, 'Foo');
assert.checkDatasets(graph, 'data', {data: [58, 181]});
await testUtils.dom.click(graph.$('.o_graph_button[data-mode=line]'));
assert.checkLabels(graph, [[true], [false]]);
assert.checkLegend(graph, 'Foo');
assert.checkDatasets(graph, 'data', {data: [58, 181]});
await testUtils.dom.click(graph.$('.o_graph_button[data-mode=pie]'));
assert.checkLabels(graph, [[true], [false]]);
assert.checkLegend(graph, ['true', 'false']);
assert.checkDatasets(graph, 'data', {data: [58, 181]});
graph.destroy();
});
QUnit.test('no comparison and two groupby', async function (assert) {
assert.expect(9);
var graph = await createView({
View: GraphView,
model: "foo",
data: this.data,
arch: '' +
'' +
'',
groupBy: ['product_id', 'color_id'],
});
assert.checkLabels(graph, [['xphone'], ['xpad']])
assert.checkLegend(graph, ['Undefined', 'red']);
assert.checkDatasets(graph, ['label', 'data'], [
{
label: 'Undefined',
data: [29, 157],
},
{
label: 'red',
data: [53, 0],
}
]);
await testUtils.dom.click(graph.$('.o_graph_button[data-mode=line]'));
assert.checkLabels(graph, [['xphone'], ['xpad']])
assert.checkLegend(graph, ['Undefined', 'red']);
assert.checkDatasets(graph, ['label', 'data'], [
{
label: 'Undefined',
data: [29, 157],
},
{
label: 'red',
data: [53, 0],
}
]);
await testUtils.dom.click(graph.$('.o_graph_button[data-mode=pie]'));
assert.checkLabels(graph, [['xphone', 'Undefined'], ['xphone', 'red'], ['xpad', 'Undefined']])
assert.checkLegend(graph, ['xphone/Undefined', 'xphone/red', 'xpad/Undefined']);
assert.checkDatasets(graph, ['label', 'data'], {
label: '',
data: [29, 53, 157],
});
graph.destroy();
});
QUnit.module('GraphView: comparison mode', {
beforeEach: async function () {
this.data.foo.records[0].date = '2016-12-15';
this.data.foo.records[1].date = '2016-12-17';
this.data.foo.records[2].date = '2016-11-22';
this.data.foo.records[3].date = '2016-11-03';
this.data.foo.records[4].date = '2016-12-20';
this.data.foo.records[5].date = '2016-12-19';
this.data.foo.records[6].date = '2016-12-15';
this.data.foo.records[7].date = undefined;
this.data.foo.records.push({id: 9, foo: 48, bar: false, product_id: 41, color_id: 7, date: "2016-12-01"});
this.data.foo.records.push({id: 10, foo: 17, bar: true, product_id: 41, color_id: 7, date: "2016-12-01"});
this.data.foo.records.push({id: 11, foo: 45, bar: true, product_id: 37, color_id: 14, date: "2016-12-01"});
this.data.foo.records.push({id: 12, foo: 48, bar: false, product_id: 37, color_id: 7, date: "2016-12-10"});
this.data.foo.records.push({id: 13, foo: 48, bar: false, product_id: undefined, color_id: 14, date: "2016-11-30"});
this.data.foo.records.push({id: 14, foo: -50, bar: true, product_id: 41, color_id: 14, date: "2016-12-15"});
this.data.foo.records.push({id: 15, foo: 53, bar: false, product_id: 41, color_id: 14, date: "2016-11-01"});
this.data.foo.records.push({id: 16, foo: 53, bar: true, product_id: undefined, color_id: 7, date: "2016-09-01"});
this.data.foo.records.push({id: 17, foo: 48, bar: false, product_id: 41, color_id: undefined, date: "2016-08-01"});
this.data.foo.records.push({id: 18, foo: -156, bar: false, product_id: 37, color_id: undefined, date: "2016-07-15"});
this.data.foo.records.push({id: 19, foo: 31, bar: false, product_id: 41, color_id: 14, date: "2016-12-15"});
this.data.foo.records.push({id: 20, foo: 109, bar: true, product_id: 41, color_id: 7, date: "2015-06-01"});
this.unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
var checkOnlyToCheck = true;
var exhaustiveTest = false || checkOnlyToCheck;
var self = this;
async function* graphGenerator(combinations) {
var i = 0;
while (i < combinations.length) {
var combination = combinations[i];
if (!checkOnlyToCheck || combination.toString() in self.combinationsToCheck) {
await self.setConfig(combination);
}
if (exhaustiveTest) {
i++;
} else {
i += Math.floor(1 + Math.random() * 20)
}
yield combination;
}
}
this.combinationsToCheck = {};
this.testCombinations = async function (combinations, assert) {
for await (var combination of graphGenerator(combinations)) {
// we can check particular combinations here
if (combination.toString() in self.combinationsToCheck) {
var graph = self.actionManager.getCurrentController().widget;
if (self.combinationsToCheck[combination].errorMessage) {
assert.strictEqual(
graph.$('.o_nocontent_help p').eq(1).text().trim(),
self.combinationsToCheck[combination].errorMessage
);
} else {
assert.checkLabels(graph, self.combinationsToCheck[combination].labels)
assert.checkLegend(graph, self.combinationsToCheck[combination].legend);
assert.checkDatasets(graph, ['label', 'data'], self.combinationsToCheck[combination].datasets);
}
}
}
};
// time range menu is assumed to be closed
this.selectTimeRanges = async function (timeRangeOption, comparisonTimeRangeOption) {
comparisonTimeRangeOption = comparisonTimeRangeOption || 'previous_period';
// open time range menu
await testUtils.dom.click($('.o_control_panel .o_time_range_menu_button'));
// select range
await testUtils.fields.editInput($('.o_control_panel .o_time_range_selector'), timeRangeOption);
// check checkbox 'Compare To'
if (!$('.o_control_panel .o_time_range_menu .o_comparison_checkbox').prop('checked')) {
await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_comparison_checkbox'));
}
// select 'Previous period' or 'Previous year' acording to comparisonTimeRangeOption
await testUtils.fields.editInput($('.o_control_panel .o_comparison_time_range_selector'), comparisonTimeRangeOption);
// Click on 'Apply' button
await testUtils.dom.click($('.o_control_panel .o_time_range_menu .o_apply_range'));
};
// groupby menu is assumed to be closed
this.selectDateIntervalOption = async function (intervalOption) {
const self = this;
intervalOption = intervalOption || 'month';
// open group by menu
await testUtils.dom.click($('.o_control_panel .o_dropdown span.fa-bars'));
let wasSelected = false;
if (this.keepFirst) {
if ($('.o_control_panel .o_group_by_menu .o_menu_item:contains(Product) a').hasClass('selected')) {
wasSelected = true;
await testUtils.dom.click($('.o_control_panel .o_group_by_menu .o_menu_item:contains(Product)'));
}
}
// open option submenu
await testUtils.dom.click($('.o_control_panel .o_group_by_menu .o_menu_item:contains("Date")'));
// check interval option if not already selected
if (!$('.o_control_panel .o_group_by_menu .o_item_option[data-option_id="' + intervalOption + '"] a').hasClass('selected')) {
await testUtils.dom.click($('.o_control_panel .o_group_by_menu .o_item_option[data-option_id="' + intervalOption + '"]'));
}
await INTERVAL_OPTIONS.filter(oId => oId !== intervalOption).forEach(async function(oId) {
if ($('.o_control_panel .o_group_by_menu .o_item_option[data-option_id="' + oId + '"] a').hasClass('selected')) {
await testUtils.dom.click($('.o_control_panel .o_group_by_menu .o_item_option[data-option_id="' + oId + '"]'));
}
});
if (this.keepFirst) {
if (wasSelected && !$('.o_control_panel .o_group_by_menu .o_menu_item:contains(Product) a').hasClass('selected')) {
await testUtils.dom.click($('.o_control_panel .o_group_by_menu .o_menu_item:contains(Product)'));
}
}
// close group by menu
await testUtils.dom.click($('.o_control_panel .o_dropdown span.fa-bars'));
};
// groupby menu is assumed to be closed
this.selectGroupBy = async function (groupByName) {
// open group by menu
await testUtils.dom.click($('.o_control_panel .o_dropdown span.fa-bars'));
// check groupBy if not already selected
if (!$('.o_control_panel .o_group_by_menu .o_menu_item:contains(' + groupByName + ') a').hasClass('selected')) {
await testUtils.dom.click($('.o_control_panel .o_group_by_menu .o_menu_item:contains(' + groupByName + ')'));
}
// close group by menu
await testUtils.dom.click($('.o_control_panel .o_dropdown span.fa-bars'));
};
// groupby menu is assumed to be closed
this.unselectGroupBy = async function (groupByName) {
// check groupBy if already selected
};
this.setConfig = async function (combination) {
await this.selectTimeRanges(combination[0], combination[1]);
if (combination.length === 3) {
await self.selectDateIntervalOption(combination[2]);
}
};
this.setMode = async function (mode) {
await testUtils.dom.click($('.o_control_panel .o_graph_button[data-mode=' + mode + ']'));
};
// // create an action manager to test the interactions with the search view
this.actionManager = await createActionManager({
data: this.data,
archs: {
'foo,false,graph': '' +
'' +
'',
'foo,false,search': '' +
'' +
'' +
'' +
'' +
'',
},
});
await this.actionManager.doAction({
res_model: 'foo',
type: 'ir.actions.act_window',
views: [[false, 'graph']],
flags: {
graph: {
additionalMeasures: ['product_id'],
}
}
});
},
afterEach: function () {
this.unpatchDate();
this.actionManager.destroy();
},
}, function () {
QUnit.test('comparison with one groupby equal to comparison date field', async function (assert) {
assert.expect(10);
this.combinationsToCheck = {
'last_30_days,previous_period,day': {
labels: [...Array(7).keys()].map(x => [x]),
legend: ["Last 30 Days", "Previous Period"],
datasets: [
{
data: [26, 53, 2, 63, 110, 48, 48],
label: "Last 30 Days",
},
{
data: [24, 53],
label: "Previous Period",
}
],
}
};
var combinations = COMBINATIONS_WITH_DATE;
await this.testCombinations(combinations, assert);
await this.setMode('line');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_30_days,previous_period,day'] = {
labels: [...Array(7).keys()].map(x => [x]),
legend: [
"2016-12-15,2016-11-03",
"2016-12-17,2016-11-01",
"2016-11-22",
"2016-12-19",
"2016-12-01",
"2016-12-10",
"2016-11-30",
],
datasets: [
{
data: [26, 53, 2, 63, 110, 48, 48],
label: "Last 30 Days",
},
{
data: [24, 53, 0, 0, 0, 0, 0],
label: "Previous Period",
}
],
};
await this.setMode('pie');
await this.testCombinations(combinations, assert);
assert.ok(true, "No combination causes a crash");
});
QUnit.test('comparison with no groupby', async function (assert) {
assert.expect(10);
this.combinationsToCheck = {
'last_30_days,previous_period': {
labels: [[]],
legend: ["Last 30 Days", "Previous Period"],
datasets: [
{
data: [350],
label: "Last 30 Days",
},
{
data: [77],
label: "Previous Period",
}
],
}
};
var combinations = COMBINATIONS;
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_30_days,previous_period'] = {
labels: [[''], [], ['']],
legend: ["Last 30 Days", "Previous Period"],
datasets: [
{
data: [undefined, 350],
label: "Last 30 Days",
},
{
data: [undefined, 77],
label: "Previous Period",
}
],
};
await this.setMode('line');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_30_days,previous_period'] = {
labels: [[]],
legend: ["Total"],
datasets: [
{
data: [350],
label: "Last 30 Days",
},
{
data: [77],
label: "Previous Period",
}
],
};
await this.setMode('pie');
await this.testCombinations(combinations, assert);
assert.ok(true, "No combination causes a crash");
});
QUnit.test('comparison with one groupby different from comparison date field', async function (assert) {
assert.expect(10);
this.combinationsToCheck = {
'last_30_days,previous_period': {
labels: [["xphone"],["xpad"],["Undefined"]],
legend: ["Last 30 Days", "Previous Period"],
datasets: [
{
data: [151, 151, 48],
label: "Last 30 Days",
},
{
data: [24, 53, 0],
label: "Previous Period",
}
],
}
};
var combinations = COMBINATIONS;
await this.selectGroupBy('Product');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_30_days,previous_period'] = {
labels: [["xphone"],["xpad"]],
legend: ["Last 30 Days", "Previous Period"],
datasets: [
{
data: [151, 151],
label: "Last 30 Days",
},
{
data: [24, 53],
label: "Previous Period",
}
],
};
await this.setMode('line');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_30_days,previous_period'] = {
labels: [["xphone"],["xpad"],["Undefined"]],
legend: ["xphone", "xpad", "Undefined"],
datasets: [
{
data: [151, 151, 48],
label: "Last 30 Days",
},
{
data: [24, 53, 0],
label: "Previous Period",
}
],
};
await this.setMode('pie');
await this.testCombinations(combinations, assert);
assert.ok(true, "No combination causes a crash");
});
QUnit.test('comparison with two groupby with first groupby equal to comparison date field', async function (assert) {
assert.expect(10);
this.keepFirst = true;
this.combinationsToCheck = {
'last_7_days,previous_period,day': {
labels: [...Array(3).keys()].map(x => [x]),
legend: [
"Last 7 Days/xphone",
"Last 7 Days/xpad",
"Previous Period/xphone"
],
datasets: [
{
data: [3, 53, 0],
label: "Last 7 Days/xphone",
},
{
data: [23, 0, 63],
label: "Last 7 Days/xpad",
},
{
data: [48],
label: "Previous Period/xphone",
}
],
}
};
var combinations = COMBINATIONS_WITH_DATE;
await this.selectGroupBy('Product');
await this.testCombinations(combinations, assert);
await this.setMode('line');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_7_days,previous_period,day'] = {
labels: [[0,"xphone"], [1,"xphone"], [2, "xpad"], [0, "xpad"]],
legend: [
"2016-12-15,2016-12-10/xphone",
"2016-12-17/xphone",
"2016-12-19/xpad",
"2016-12-15,2016-12-10/xpad"
],
datasets: [
{
data: [3, 53, 63, 23],
label: "Last 7 Days",
},
{
data: [48, 0, 0, 0],
label: "Previous Period",
}
],
};
await this.setMode('pie');
await this.testCombinations(combinations, assert);
assert.ok(true, "No combination causes a crash");
this.keepFirst = false;
});
QUnit.test('comparison with two groupby with second groupby equal to comparison date field', async function (assert) {
assert.expect(8);
this.combinationsToCheck = {
'this_year,previous_period,quarter': {
labels: [["xphone"], ["xpad"],["Undefined"]],
legend: [
"This Year/Q4 2016",
"This Year/Q3 2016",
"Previous Period/Q2 2015"
],
datasets: [
{
data: [175, 208, 48],
label: "This Year/Q4 2016",
},
{
data: [-156, 48, 53],
label: "This Year/Q3 2016",
},
{
data: [0, 109, 0],
label: "Previous Period/Q2 2015",
},
]
}
};
var combinations = COMBINATIONS_WITH_DATE;
await this.selectGroupBy('Product');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['this_year,previous_period,quarter'] = {
labels: [["xphone"], ["xpad"]],
legend: [
"This Year/Q4 2016",
"This Year/Q3 2016",
"Previous Period/Q2 2015"
],
datasets: [
{
data: [175, 208],
label: "This Year/Q4 2016",
},
{
data: [-156, 48],
label: "This Year/Q3 2016",
},
{
data: [0, 109],
label: "Previous Period/Q2 2015",
},
]
};
await this.setMode('line');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['this_year,previous_period,quarter'] = {
errorMessage: 'Pie chart cannot mix positive and negative numbers. ' +
'Try to change your domain to only display positive results'
};
await this.setMode('pie');
await this.testCombinations(combinations, assert);
assert.ok(true, "No combination causes a crash");
});
QUnit.test('comparison with two groupby with no groupby equal to comparison date field', async function (assert) {
assert.expect(10);
this.combinationsToCheck = {
'last_month,previous_year': {
labels: [["xphone"],["Undefined"], ["xpad"]],
legend: ["Last Month/true", "Last Month/false"],
datasets: [
{
data: [2, 0, 0],
label: "Last Month/true",
},
{
data: [24, 48, 53],
label: "Last Month/false",
}
],
}
};
var combinations = COMBINATIONS;
await this.selectGroupBy('Product');
await this.selectGroupBy('Bar');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_month,previous_year'] = {
labels: [["xphone"], ["xpad"]],
legend: ["Last Month/true", "Last Month/false"],
datasets: [
{
data: [2, 0],
label: "Last Month/true",
},
{
data: [24, 53],
label: "Last Month/false",
}
],
};
await this.setMode('line');
await this.testCombinations(combinations, assert);
this.combinationsToCheck['last_month,previous_year'] = {
labels: [
["xphone", true],
["xphone", false],
["Undefined", false],
["xpad", false],
["No data"]
],
legend: [
"xphone/true",
"xphone/false",
"Undefined/false",
"xpad/false",
"No data"
],
datasets: [
{
data: [2, 24, 48, 53],
label: "Last Month",
},
{
data: [undefined, undefined, undefined, undefined, 1],
label: "Previous Year",
}
],
};
await this.setMode('pie');
await this.testCombinations(combinations, assert);
assert.ok(true, "No combination causes a crash");
});
});
});
});