odoo.define('web.graph_view_tests', function (require) { "use strict"; const searchUtils = require('web.searchUtils'); const GraphView = require('web.GraphView'); const testUtils = require('web.test_utils'); const { sortBy } = require('web.utils'); const { createView } = testUtils; const cpHelpers = testUtils.controlPanel; const patchDate = testUtils.mock.patchDate; const { INTERVAL_OPTIONS, PERIOD_OPTIONS, COMPARISON_OPTIONS } = searchUtils; const INTERVAL_OPTION_IDS = Object.keys(INTERVAL_OPTIONS); const yearIds = []; const otherIds = []; for (const id of Object.keys(PERIOD_OPTIONS)) { const option = PERIOD_OPTIONS[id]; if (option.granularity === 'year') { yearIds.push(id); } else { otherIds.push(id); } } const BASIC_DOMAIN_IDS = []; for (const yearId of yearIds) { BASIC_DOMAIN_IDS.push(yearId); for (const id of otherIds) { BASIC_DOMAIN_IDS.push(`${yearId}__${id}`); } } const GENERATOR_INDEXES = {}; Object.keys(PERIOD_OPTIONS).forEach((id, index) => { GENERATOR_INDEXES[id] = index; }); const COMPARISON_OPTION_IDS = Object.keys(COMPARISON_OPTIONS); const COMPARISON_OPTION_INDEXES = {}; COMPARISON_OPTION_IDS.forEach((id, index) => { COMPARISON_OPTION_INDEXES[id] = index; }); const f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e)))); const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a); const COMBINATIONS = cartesian(COMPARISON_OPTION_IDS, BASIC_DOMAIN_IDS); const COMBINATIONS_WITH_DATE = cartesian(COMPARISON_OPTION_IDS, BASIC_DOMAIN_IDS, INTERVAL_OPTION_IDS); function checkDatasets(assert, graph, keys, expectedDatasets) { keys = keys instanceof Array ? keys : [keys]; expectedDatasets = expectedDatasets instanceof Array ? expectedDatasets : [expectedDatasets]; const datasets = graph.renderer.componentRef.comp.chart.data.datasets; const actualValues = datasets.map(dataset => _.pick(dataset, keys)); assert.deepEqual(actualValues, expectedDatasets); } function checkLabels(assert, graph, expectedLabels) { const labels = graph.renderer.componentRef.comp.chart.data.labels; assert.deepEqual(labels, expectedLabels); } function checkLegend(assert, graph, expectedLegendLabels) { expectedLegendLabels = expectedLegendLabels instanceof Array ? expectedLegendLabels : [expectedLegendLabels]; const chart = graph.renderer.componentRef.comp.chart; const actualLegendLabels = chart.config.options.legend.labels.generateLabels(chart).map(o => o.text); assert.deepEqual(actualLegendLabels, 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 }, revenue: { string: "Revenue", type: 'integer', store: true }, }, records: [ { id: 1, foo: 3, bar: true, product_id: 37, date: "2016-01-01", revenue: 1 }, { id: 2, foo: 53, bar: true, product_id: 37, color_id: 7, date: "2016-01-03", revenue: 2 }, { id: 3, foo: 2, bar: true, product_id: 37, date: "2016-03-04", revenue: 3 }, { id: 4, foo: 24, bar: false, product_id: 37, date: "2016-03-07", revenue: 4 }, { id: 5, foo: 4, bar: false, product_id: 41, date: "2016-05-01", revenue: 5 }, { 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", revenue: 8 }, ] }, 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); const 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.props.mode, "bar", "should be in bar chart mode by default"); checkLabels(assert, graph, [[true], [false]]); checkDatasets(assert, graph, ['backgroundColor', 'data', 'label', 'originIndex', 'stack'], { backgroundColor: "#1f77b4", data: [3, 5], label: "Count", originIndex: 0, stack: "", } ); checkLegend(assert, graph, 'Count'); graph.destroy(); }); QUnit.test('default type attribute', async function (assert) { assert.expect(1); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); assert.strictEqual(graph.renderer.props.mode, "pie", "should be in pie chart mode by default"); graph.destroy(); }); QUnit.test('title attribute', async function (assert) { assert.expect(1); const 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); const 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); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); assert.strictEqual(graph.renderer.props.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.props.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); const 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); const graph = await createView({ View: GraphView, model: 'foo', data: this.data, arch: ` `, groupBy: ['product_id', 'bar', 'color_id'], }); checkLabels(assert, graph, [['xphone'], ['xpad']]); checkLegend(assert, graph, ['true/Undefined', 'true/red', 'false/Undefined']); await testUtils.dom.click(graph.$buttons.find('button[data-mode="line"]')); checkLabels(assert, graph, [['xphone'], ['xpad']]); checkLegend(assert, graph, ['true/Undefined', 'true/red', 'false/Undefined']); await testUtils.dom.click(graph.$buttons.find('button[data-mode="pie"]')); checkLabels(assert, graph, [ ["xphone", true, "Undefined"], ["xphone", true, "red"], ["xphone", false, "Undefined"], ["xpad", false, "Undefined"] ]); checkLegend(assert, 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); let rpcCount = 0; const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, mockRPC: function (route, args) { rpcCount++; return this._super(route, args); }, }); await cpHelpers.toggleMenu(graph, "Measures"); await cpHelpers.toggleMenuItem(graph, "Foo"); checkLegend(assert, 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(3); this.data.foo.records = []; const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, viewOptions: { action: { help: '

This helper should not be displayed in graph views

' } }, }); assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); assert.containsNone(graph, 'div.o_view_nocontent'); assert.containsNone(graph, '.abc'); graph.destroy(); }); QUnit.test('no content helper (pie chart)', async function (assert) { assert.expect(3); this.data.foo.records = []; const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, viewOptions: { action: { help: '

This helper should not be displayed in graph views

' } }, }); assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); assert.containsNone(graph, 'div.o_view_nocontent'); assert.containsNone(graph, '.abc'); graph.destroy(); }); QUnit.test('render pie chart in comparison mode', async function (assert) { assert.expect(2); const unpatchDate = patchDate(2020, 4, 19, 1, 0, 0); const graph = await createView({ View: GraphView, model: "foo", data: this.data, context: { search_default_date_filter: 1, }, arch: ` `, archs: { 'foo,false,search': ` `, }, }); await cpHelpers.toggleComparisonMenu(graph); await cpHelpers.toggleMenuItem(graph, 'Date: Previous period'); assert.containsNone(graph, 'div.o_view_nocontent', "should not display the no content helper"); checkLegend(assert, graph, 'No data'); unpatchDate(); graph.destroy(); }); QUnit.test('no content helper after update', async function (assert) { assert.expect(6); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, viewOptions: { action: { help: '

This helper should not be displayed in graph views

' } }, }); assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); assert.containsNone(graph, 'div.o_view_nocontent'); assert.containsNone(graph, '.abc'); await testUtils.graph.reload(graph, { domain: [['product_id', '=', 4]] }); assert.containsOnce(graph, 'div.o_graph_canvas_container canvas'); assert.containsNone(graph, 'div.o_view_nocontent'); assert.containsNone(graph, '.abc'); graph.destroy(); }); QUnit.test('can reload with other group by', async function (assert) { assert.expect(2); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); checkLabels(assert, graph, [['xphone'], ['xpad']]); await testUtils.graph.reload(graph, { groupBy: ['color_id'] }); checkLabels(assert, graph, [['Undefined'], ['red']]); graph.destroy(); }); QUnit.test('getOwnedQueryParams correctly returns mode, measure, and groupbys', async function (assert) { assert.expect(4); const 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"); await cpHelpers.toggleMenu(graph, "Measures"); await cpHelpers.toggleMenuItem(graph, "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); const lastOne = this.data.foo.records[this.data.foo.records.length - 1]; lastOne.color_id = 14; const 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 checkLegend(assert, graph, 'Foo'); // check mode assert.strictEqual(graph.renderer.props.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 checkLabels(assert, graph, [['red'], ['black']]); graph.destroy(); }); QUnit.test('correctly use group_by key from the context', async function (assert) { assert.expect(1); const lastOne = this.data.foo.records[this.data.foo.records.length - 1]; lastOne.color_id = 14; const 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 checkLabels(assert, graph, [['red'], ['black']]); graph.destroy(); }); QUnit.test('correctly uses graph_ keys from the context (at reload)', async function (assert) { assert.expect(7); const lastOne = this.data.foo.records[this.data.foo.records.length - 1]; lastOne.color_id = 14; const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: '', }); assert.strictEqual(graph.renderer.props.mode, "bar", "should be in bar chart mode"); assert.hasClass(graph.$buttons.find('button[data-mode="bar"]'), 'active', 'bar chart button should be active'); const reloadParams = { context: { graph_measure: 'foo', graph_mode: 'line', graph_groupbys: ['color_id'], }, }; await testUtils.graph.reload(graph, reloadParams); // check measure checkLegend(assert, graph, 'Foo'); // check mode assert.strictEqual(graph.renderer.props.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 checkLabels(assert, graph, [['red'], ['black']]); graph.destroy(); }); QUnit.test('reload graph with correct fields', async function (assert) { assert.expect(2); const 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); const 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); }, }); checkLabels(assert, graph, [['xphone'], ['xpad']]); checkLegend(assert, graph, 'Foo'); checkDatasets(assert, graph, 'data', { data: [82, 157] }); await testUtils.graph.reload(graph, { groupBy: [] }); checkLabels(assert, graph, [['xphone'], ['xpad']]); checkLegend(assert, graph, 'Foo'); checkDatasets(assert, 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); const 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"); checkLabels(assert, graph, [[]]); checkLegend(assert, graph, 'Product'); checkDatasets(assert, graph, 'data', { data: [2] }); graph.destroy(); }); QUnit.test('use a many2one as a measure should work (with groupBy)', async function (assert) { assert.expect(5); const 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.props.mode, "bar", "should be in bar chart mode by default"); checkLabels(assert, graph, [[true], [false]]); checkLegend(assert, graph, 'Product'); checkDatasets(assert, 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); const 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 cpHelpers.toggleMenu(graph, "Measures"); await cpHelpers.toggleMenuItem(graph, "Product"); checkLabels(assert, graph, [['xphone'], ['xpad']]); checkLegend(assert, graph, 'Product'); checkDatasets(assert, graph, 'data', { data: [1, 1] }); graph.destroy(); }); QUnit.test('not use a many2one as a measure by default', async function (assert) { assert.expect(1); const 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); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: '', viewOptions: { additionalMeasures: ['product_id'], }, }); assert.ok(graph.measures.find(m => m.fieldName === 'product_id'), "should have product_id as measure"); graph.destroy(); }); QUnit.test('measure dropdown consistency', async function (assert) { assert.expect(2); const actionManager = await testUtils.createActionManager({ archs: { 'foo,false,graph': ` `, 'foo,false,search': ``, 'foo,false,kanban': `
`, }, data: this.data, }); await actionManager.doAction({ res_model: 'foo', type: 'ir.actions.act_window', views: [[false, 'graph'], [false, 'kanban']], flags: { graph: { additionalMeasures: ['product_id'], } }, }); assert.containsOnce(actionManager, '.o_control_panel .o_graph_measures_list', "Measures dropdown is present at init" ); await cpHelpers.switchView(actionManager, 'kanban'); await cpHelpers.switchView(actionManager, 'graph'); assert.containsOnce(actionManager, '.o_control_panel .o_graph_measures_list', "Measures dropdown is present after reload" ); actionManager.destroy(); }); QUnit.test('graph view crash when moving from search view using Down key', async function (assert) { assert.expect(1); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); graph._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); const data = this.data; data.foo.fields.bouh = { string: "bouh", type: "integer" }; const graph = await createView({ View: GraphView, model: "foo", data: data, arch: ` `, }); await cpHelpers.toggleMenu(graph, "Measures"); assert.strictEqual(graph.$buttons.find('.o_graph_measures_list .dropdown-item:first').text(), 'bouh', "Bouh should be the first measure"); assert.strictEqual(graph.$buttons.find('.o_graph_measures_list .dropdown-item:last').text(), '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); const graph = await createView({ View: GraphView, model: "foo", groupBy: ['date'], data: this.data, arch: ` `, }); function indexOf(label) { return graph.renderer.componentRef.comp.chart.data.labels.findIndex( (lab) => JSON.stringify(lab) === JSON.stringify(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); const graph = await createView({ View: GraphView, model: "foo", groupBy: ['date', 'color_id'], data: this.data, arch: ` `, }); function indexOf(label) { return graph.renderer.componentRef.comp.chart.data.labels.findIndex( (lab) => JSON.stringify(lab) === JSON.stringify(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]')); const labels = graph.renderer.componentRef.comp.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); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); checkLabels(assert, graph, [[]]); checkLegend(assert, graph, 'Foo'); checkDatasets(assert, 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. checkLabels(assert, graph, [[''], [], ['']]); checkLegend(assert, graph, 'Foo'); checkDatasets(assert, graph, 'data', { data: [undefined, 239] }); await testUtils.dom.click(graph.$('.o_graph_button[data-mode=pie]')); checkLabels(assert, graph, [[]]); checkLegend(assert, graph, 'Total'); checkDatasets(assert, graph, 'data', { data: [239] }); graph.destroy(); }); QUnit.test('no comparison and one groupby', async function (assert) { assert.expect(9); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); checkLabels(assert, graph, [[true], [false]]); checkLegend(assert, graph, 'Foo'); checkDatasets(assert, graph, 'data', { data: [58, 181] }); await testUtils.dom.click(graph.$('.o_graph_button[data-mode=line]')); checkLabels(assert, graph, [[true], [false]]); checkLegend(assert, graph, 'Foo'); checkDatasets(assert, graph, 'data', { data: [58, 181] }); await testUtils.dom.click(graph.$('.o_graph_button[data-mode=pie]')); checkLabels(assert, graph, [[true], [false]]); checkLegend(assert, graph, ['true', 'false']); checkDatasets(assert, graph, 'data', { data: [58, 181] }); graph.destroy(); }); QUnit.test('no comparison and two groupby', async function (assert) { assert.expect(9); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, groupBy: ['product_id', 'color_id'], }); checkLabels(assert, graph, [['xphone'], ['xpad']]); checkLegend(assert, graph, ['Undefined', 'red']); checkDatasets(assert, graph, ['label', 'data'], [ { label: 'Undefined', data: [29, 157], }, { label: 'red', data: [53, 0], } ]); await testUtils.dom.click(graph.$('.o_graph_button[data-mode=line]')); checkLabels(assert, graph, [['xphone'], ['xpad']]); checkLegend(assert, graph, ['Undefined', 'red']); checkDatasets(assert, graph, ['label', 'data'], [ { label: 'Undefined', data: [29, 157], }, { label: 'red', data: [53, 0], } ]); await testUtils.dom.click(graph.$('.o_graph_button[data-mode=pie]')); checkLabels(assert, graph, [['xphone', 'Undefined'], ['xphone', 'red'], ['xpad', 'Undefined']]); checkLegend(assert, graph, ['xphone/Undefined', 'xphone/red', 'xpad/Undefined']); checkDatasets(assert, graph, ['label', 'data'], { label: '', data: [29, 53, 157], }); graph.destroy(); }); QUnit.test('graph view only keeps finer groupby filter option for a given groupby', async function (assert) { assert.expect(3); const graph = await createView({ View: GraphView, model: "foo", groupBy: ['date:year', 'product_id', 'date', 'date:quarter'], data: this.data, arch: ` `, }); checkLabels(assert, graph, [["January 2016"], ["March 2016"], ["May 2016"], ["April 2016"]]); // mockReadGroup does not always sort groups -> May 2016 is before April 2016 for that reason. checkLegend(assert, graph, ["xphone", "xpad"]); checkDatasets(assert, graph, ['label', 'data'], [ { label: 'xphone', data: [2, 2, 0, 0], }, { label: 'xpad', data: [0, 0, 1, 1], } ]); graph.destroy(); }); QUnit.test('clicking on bar and pie charts triggers a do_action', async function (assert) { assert.expect(5); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, intercepts: { do_action: function (ev) { assert.deepEqual(ev.data.action, { context: {}, domain: [["bar", "=", true]], name: "Foo Analysis", res_model: "foo", target: 'current', type: 'ir.actions.act_window', view_mode: 'list', views: [[false, 'list'], [false, 'form']], }, "should trigger do_action with correct action parameter"); } }, }); // bar mode assert.strictEqual(graph.renderer.props.mode, "bar", "should be in bar chart mode"); checkDatasets(assert, graph, ['domain'], { domain: [[["bar", "=", true]], [["bar", "=", false]]], }); let myChart = graph.renderer.componentRef.comp.chart; let meta = myChart.getDatasetMeta(0); let rectangle = myChart.canvas.getBoundingClientRect(); let point = meta.data[0].getCenterPoint(); await testUtils.dom.triggerEvent(myChart.canvas, 'click', { pageX: rectangle.left + point.x, pageY: rectangle.top + point.y }); // pie mode await testUtils.dom.click(graph.$('.o_graph_button[data-mode=pie]')); assert.strictEqual(graph.renderer.props.mode, "pie", "should be in pie chart mode"); myChart = graph.renderer.componentRef.comp.chart; meta = myChart.getDatasetMeta(0); rectangle = myChart.canvas.getBoundingClientRect(); point = meta.data[0].getCenterPoint(); await testUtils.dom.triggerEvent(myChart.canvas, 'click', { pageX: rectangle.left + point.x, pageY: rectangle.top + point.y }); graph.destroy(); }); QUnit.test('clicking charts trigger a do_action with correct views', async function (assert) { assert.expect(3); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, intercepts: { do_action: function (ev) { assert.deepEqual(ev.data.action, { context: {}, domain: [["bar", "=", true]], name: "Foo Analysis", res_model: "foo", target: 'current', type: 'ir.actions.act_window', view_mode: 'list', views: [[364, 'list'], [29, 'form']], }, "should trigger do_action with correct action parameter"); } }, viewOptions: { actionViews: [{ type: 'list', viewID: 364, }, { type: 'form', viewID: 29, }], }, }); assert.strictEqual(graph.renderer.props.mode, "bar", "should be in bar chart mode"); checkDatasets(assert, graph, ['domain'], { domain: [[["bar", "=", true]], [["bar", "=", false]]], }); let myChart = graph.renderer.componentRef.comp.chart; let meta = myChart.getDatasetMeta(0); let rectangle = myChart.canvas.getBoundingClientRect(); let point = meta.data[0].getCenterPoint(); await testUtils.dom.triggerEvent(myChart.canvas, 'click', { pageX: rectangle.left + point.x, pageY: rectangle.top + point.y }); graph.destroy(); }); QUnit.test('graph view with attribute disable_linking="True"', async function (assert) { assert.expect(2); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, intercepts: { do_action: function () { throw new Error('Should not perform a do_action'); }, }, }); assert.strictEqual(graph.renderer.props.mode, "bar", "should be in bar chart mode"); checkDatasets(assert, graph, ['domain'], { domain: [[["bar", "=", true]], [["bar", "=", false]]], }); let myChart = graph.renderer.componentRef.comp.chart; let meta = myChart.getDatasetMeta(0); let rectangle = myChart.canvas.getBoundingClientRect(); let point = meta.data[0].getCenterPoint(); await testUtils.dom.triggerEvent(myChart.canvas, 'click', { pageX: rectangle.left + point.x, pageY: rectangle.top + point.y }); graph.destroy(); }); QUnit.test('graph view without invisible attribute on field', async function (assert) { assert.expect(4); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ``, }); await testUtils.dom.click(graph.$('.btn-group:first button')); assert.containsN(graph, 'li.o_menu_item', 3, "there should be three menu item in the measures dropdown (count, revenue and foo)"); assert.containsOnce(graph, 'li.o_menu_item a:contains("Revenue")'); assert.containsOnce(graph, 'li.o_menu_item a:contains("Foo")'); assert.containsOnce(graph, 'li.o_menu_item a:contains("Count")'); graph.destroy(); }); QUnit.test('graph view with invisible attribute on field', async function (assert) { assert.expect(2); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); await testUtils.dom.click(graph.$('.btn-group:first button')); assert.containsN(graph, 'li.o_menu_item', 2, "there should be only two menu item in the measures dropdown (count and foo)"); assert.containsNone(graph, 'li.o_menu_item a:contains("Revenue")'); graph.destroy(); }); QUnit.test('graph view sort by measure', async function (assert) { assert.expect(18); // change first record from foo as there are 4 records count for each product this.data.product.records.push({ id: 38, display_name: "zphone" }); this.data.foo.records[7].product_id = 38; const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); assert.containsN(graph, 'button[data-order]', 2, "there should be two order buttons for sorting axis labels in bar mode"); checkLegend(assert, graph, 'Count', 'measure should be by count'); assert.hasClass(graph.$('button[data-order="desc"]'), 'active', 'sorting should be applie on descending order by default when sorting="desc"'); checkDatasets(assert, graph, 'data', { data: [4, 3, 1] }); await testUtils.dom.click(graph.$buttons.find('button[data-order="asc"]')); assert.hasClass(graph.$('button[data-order="asc"]'), 'active', "ascending order should be applied"); checkDatasets(assert, graph, 'data', { data: [1, 3, 4] }); await testUtils.dom.click(graph.$buttons.find('button[data-order="desc"]')); assert.hasClass(graph.$('button[data-order="desc"]'), 'active', "descending order button should be active"); checkDatasets(assert, graph, 'data', { data: [4, 3, 1] }); // again click on descending button to deactivate order button await testUtils.dom.click(graph.$buttons.find('button[data-order="desc"]')); assert.doesNotHaveClass(graph.$('button[data-order="desc"]'), 'active', "descending order button should not be active"); checkDatasets(assert, graph, 'data', { data: [4, 3, 1] }); // set line mode await testUtils.dom.click(graph.$buttons.find('button[data-mode="line"]')); assert.containsN(graph, 'button[data-order]', 2, "there should be two order buttons for sorting axis labels in line mode"); checkLegend(assert, graph, 'Count', 'measure should be by count'); assert.doesNotHaveClass(graph.$('button[data-order="desc"]'), 'active', "descending order should be applied"); checkDatasets(assert, graph, 'data', { data: [4, 3, 1] }); await testUtils.dom.click(graph.$buttons.find('button[data-order="asc"]')); assert.hasClass(graph.$('button[data-order="asc"]'), 'active', "ascending order button should be active"); checkDatasets(assert, graph, 'data', { data: [1, 3, 4] }); await testUtils.dom.click(graph.$buttons.find('button[data-order="desc"]')); assert.hasClass(graph.$('button[data-order="desc"]'), 'active', "descending order button should be active"); checkDatasets(assert, graph, 'data', { data: [4, 3, 1] }); graph.destroy(); }); QUnit.test('graph view sort by measure for grouped data', async function (assert) { assert.expect(9); // change first record from foo as there are 4 records count for each product this.data.product.records.push({ id: 38, display_name: "zphone", }); this.data.foo.records[7].product_id = 38; const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, }); checkLegend(assert, graph, ["true", "false"], 'measure should be by count'); assert.containsN(graph, 'button[data-order]', 2, "there should be two order buttons for sorting axis labels"); checkDatasets(assert, graph, 'data', [{ data: [3, 0, 0] }, { data: [1, 3, 1] }]); await testUtils.dom.click(graph.$buttons.find('button[data-order="asc"]')); assert.hasClass(graph.$('button[data-order="asc"]'), 'active', "ascending order should be applied by default"); checkDatasets(assert, graph, 'data', [{ data: [1, 3, 1] }, { data: [0, 0, 3] }]); await testUtils.dom.click(graph.$buttons.find('button[data-order="desc"]')); assert.hasClass(graph.$('button[data-order="desc"]'), 'active', "ascending order button should be active"); checkDatasets(assert, graph, 'data', [{ data: [3, 0, 0] }, { data: [1, 3, 1] }]); // again click on descending button to deactivate order button await testUtils.dom.click(graph.$buttons.find('button[data-order="desc"]')); assert.doesNotHaveClass(graph.$('button[data-order="desc"]'), 'active', "descending order button should not be active"); checkDatasets(assert, graph, 'data', [{ data: [3, 0, 0] }, { data: [1, 3, 1] }]); graph.destroy(); }); QUnit.test('graph view sort by measure for multiple grouped data', async function (assert) { assert.expect(9); // change first record from foo as there are 4 records count for each product this.data.product.records.push({ id: 38, display_name: "zphone" }); this.data.foo.records[7].product_id = 38; // add few more records to data to have grouped data date wise const data = [ { id: 9, foo: 48, bar: false, product_id: 41, date: "2016-04-01" }, { id: 10, foo: 49, bar: false, product_id: 41, date: "2016-04-01" }, { id: 11, foo: 50, bar: true, product_id: 37, date: "2016-01-03" }, { id: 12, foo: 50, bar: true, product_id: 41, date: "2016-01-03" }, ]; Object.assign(this.data.foo.records, data); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, groupBy: ['date', 'product_id'] }); checkLegend(assert, graph, ["xpad", "xphone", "zphone"], 'measure should be by count'); assert.containsN(graph, 'button[data-order]', 2, "there should be two order buttons for sorting axis labels"); checkDatasets(assert, graph, 'data', [{ data: [2, 1, 1, 2] }, { data: [0, 1, 0, 0] }, { data: [1, 0, 0, 0] }]); await testUtils.dom.click(graph.$buttons.find('button[data-order="asc"]')); assert.hasClass(graph.$('button[data-order="asc"]'), 'active', "ascending order should be applied by default"); checkDatasets(assert, graph, 'data', [{ data: [1, 1, 2, 2] }, { data: [0, 1, 0, 0] }, { data: [0, 0, 0, 1] }]); await testUtils.dom.click(graph.$buttons.find('button[data-order="desc"]')); assert.hasClass(graph.$('button[data-order="desc"]'), 'active', "descending order button should be active"); checkDatasets(assert, graph, 'data', [{ data: [2, 1, 2, 1] }, { data: [1, 0, 0, 0] }, { data: [0, 1, 0, 0] }]); // again click on descending button to deactivate order button await testUtils.dom.click(graph.$buttons.find('button[data-order="desc"]')); assert.doesNotHaveClass(graph.$('button[data-order="desc"]'), 'active', "descending order button should not be active"); checkDatasets(assert, graph, 'data', [{ data: [2, 1, 1, 2] }, { data: [0, 1, 0, 0] }, { data: [1, 0, 0, 0] }]); graph.destroy(); }); QUnit.test('empty graph view with sample data', async function (assert) { assert.expect(8); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, domain: [['id', '<', 0]], viewOptions: { action: { help: '

click to add a foo

' } }, }); assert.hasClass(graph.el, 'o_view_sample_data'); assert.containsOnce(graph, '.o_view_nocontent'); assert.containsOnce(graph, '.o_graph_canvas_container canvas'); assert.hasClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); await graph.reload({ domain: [] }); assert.doesNotHaveClass(graph.el, 'o_view_sample_data'); assert.containsNone(graph, '.o_view_nocontent'); assert.containsOnce(graph, '.o_graph_canvas_container canvas'); assert.doesNotHaveClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); graph.destroy(); }); QUnit.test('non empty graph view with sample data', async function (assert) { assert.expect(8); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, viewOptions: { action: { help: '

click to add a foo

' } }, }) assert.doesNotHaveClass(graph.el, 'o_view_sample_data'); assert.containsNone(graph, '.o_view_nocontent'); assert.containsOnce(graph, '.o_graph_canvas_container canvas'); assert.doesNotHaveClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); await graph.reload({ domain: [['id', '<', 0]] }); assert.doesNotHaveClass(graph.el, 'o_view_sample_data'); assert.containsOnce(graph, '.o_graph_canvas_container canvas'); assert.doesNotHaveClass(graph.$('.o_graph_canvas_container'), 'o_sample_data_disabled'); assert.containsNone(graph, '.o_view_nocontent'); 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.data.foo.records = sortBy(this.data.foo.records, 'date'); this.unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); const graph = await createView({ View: GraphView, model: "foo", data: this.data, arch: ` `, archs: { 'foo,false,search': ` `, }, viewOptions: { additionalMeasures: ['product_id'], }, }); this.graph = graph; const checkOnlyToCheck = true; const exhaustiveTest = false || checkOnlyToCheck; const self = this; async function* graphGenerator(combinations) { let i = 0; while (i < combinations.length) { const 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 (combinations, assert) => { for await (const combination of graphGenerator(combinations)) { // we can check particular combinations here if (combination.toString() in this.combinationsToCheck) { if (this.combinationsToCheck[combination].errorMessage) { assert.strictEqual( graph.$('.o_nocontent_help p').eq(1).text().trim(), this.combinationsToCheck[combination].errorMessage ); } else { checkLabels(assert, graph, this.combinationsToCheck[combination].labels); checkLegend(assert, graph, this.combinationsToCheck[combination].legend); checkDatasets(assert, graph, ['label', 'data'], this.combinationsToCheck[combination].datasets); } } } }; this.selectTimeRanges = async (comparisonOptionId, basicDomainId) => { const facetEls = graph.el.querySelectorAll('.o_searchview_facet'); const facetIndex = [...facetEls].findIndex(el => !!el.querySelector('span.fa-filter')); if (facetIndex > -1) { await cpHelpers.removeFacet(graph, facetIndex); } const [yearId, otherId] = basicDomainId.split('__'); await cpHelpers.toggleFilterMenu(graph); await cpHelpers.toggleMenuItem(graph, 'Date Filter'); await cpHelpers.toggleMenuItemOption(graph, 'Date Filter', GENERATOR_INDEXES[yearId]); if (otherId) { await cpHelpers.toggleMenuItemOption(graph, 'Date Filter', GENERATOR_INDEXES[otherId]); } const itemIndex = COMPARISON_OPTION_INDEXES[comparisonOptionId]; await cpHelpers.toggleComparisonMenu(graph); await cpHelpers.toggleMenuItem(graph, itemIndex); }; // groupby menu is assumed to be closed this.selectDateIntervalOption = async (intervalOption) => { await cpHelpers.toggleGroupByMenu(graph); const productWasSelected = cpHelpers.isItemSelected(graph, 'Product'); const facetEls = graph.el.querySelectorAll('.o_searchview_facet'); const facetIndex = [...facetEls].findIndex(el => !!el.querySelector('span.fa-bars')); if (facetIndex > -1) { await cpHelpers.removeFacet(graph, facetIndex); await cpHelpers.toggleGroupByMenu(graph); } if (productWasSelected && !this.keepDateFirst) { await cpHelpers.toggleMenuItem(graph, 'Product'); } const optionIndex = INTERVAL_OPTION_IDS.indexOf(intervalOption); await cpHelpers.toggleMenuItem(graph, 'Date'); await cpHelpers.toggleMenuItemOption(graph, 'Date', optionIndex); if (productWasSelected && this.keepDateFirst) { await cpHelpers.toggleMenuItem(graph, 'Product'); } await cpHelpers.toggleGroupByMenu(graph); }; // groupby menu is assumed to be closed this.selectGroupBy = async (groupByName) => { await cpHelpers.toggleGroupByMenu(graph); if (!cpHelpers.isItemSelected(graph, groupByName)) { await cpHelpers.toggleMenuItem(graph, groupByName); } await cpHelpers.toggleGroupByMenu(graph); }; this.setConfig = async (combination) => { await this.selectTimeRanges(combination[0], combination[1]); if (combination.length === 3) { await this.selectDateIntervalOption(combination[2]); } }; this.setMode = async (mode) => { await testUtils.dom.click($(`.o_control_panel .o_graph_button[data-mode="${mode}"]`)); }; }, afterEach: function () { this.unpatchDate(); this.graph.destroy(); }, }, function () { QUnit.test('comparison with one groupby equal to comparison date field', async function (assert) { assert.expect(11); this.combinationsToCheck = { 'previous_period,this_year__this_month,day': { labels: [...Array(6).keys()].map(x => [x]), legend: ["December 2016", "November 2016"], datasets: [ { data: [110, 48, 26, 53, 63, 4], label: "December 2016", }, { data: [53, 24, 2, 48], label: "November 2016", } ], } }; const combinations = COMBINATIONS_WITH_DATE; await this.testCombinations(combinations, assert); await this.setMode('line'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year__this_month,day'] = { labels: [...Array(6).keys()].map(x => [x]), legend: [ "2016-12-01,2016-11-01", "2016-12-10,2016-11-03", "2016-12-15,2016-11-22", "2016-12-17,2016-11-30", "2016-12-19", "2016-12-20" ], datasets: [ { data: [110, 48, 26, 53, 63, 4], label: "December 2016", }, { data: [53, 24, 2, 48, 0, 0], label: "November 2016", } ], }; await this.setMode('pie'); await this.testCombinations(combinations, assert); // isNotVisible can not have two elements so checking visibility of first element assert.isNotVisible(this.graph.$('button[data-order]:first'), "there should not be order button in comparison mode"); assert.ok(true, "No combination causes a crash"); }); QUnit.test('comparison with no groupby', async function (assert) { assert.expect(10); this.combinationsToCheck = { 'previous_period,this_year__this_month': { labels: [[]], legend: ["December 2016", "November 2016"], datasets: [ { data: [304], label: "December 2016", }, { data: [127], label: "November 2016", } ], } }; const combinations = COMBINATIONS; await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year__this_month'] = { labels: [[''], [], ['']], legend: ["December 2016", "November 2016"], datasets: [ { data: [undefined, 304], label: "December 2016", }, { data: [undefined, 127], label: "November 2016", } ], }; await this.setMode('line'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year__this_month'] = { labels: [[]], legend: ["Total"], datasets: [ { data: [304], label: "December 2016", }, { data: [127], label: "November 2016", } ], }; 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 = { 'previous_period,this_year__this_month': { labels: [["xpad"], ["xphone"], ["Undefined"]], legend: ["December 2016", "November 2016"], datasets: [ { data: [155, 149, 0], label: "December 2016", }, { data: [53, 26, 48], label: "November 2016", } ], } }; const combinations = COMBINATIONS; await this.selectGroupBy('Product'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year__this_month'] = { labels: [["xpad"], ["xphone"]], legend: ["December 2016", "November 2016"], datasets: [ { data: [155, 149], label: "December 2016", }, { data: [53, 26], label: "November 2016", } ], }; await this.setMode('line'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year__this_month'] = { labels: [["xpad"], ["xphone"], ["Undefined"]], legend: ["xpad", "xphone", "Undefined"], datasets: [ { data: [155, 149, 0], label: "December 2016", }, { data: [53, 26, 48], label: "November 2016", } ], }; 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.keepDateFirst = true; this.combinationsToCheck = { 'previous_period,this_year__this_month,day': { labels: [...Array(6).keys()].map(x => [x]), legend: [ "December 2016/xpad", "December 2016/xphone", "November 2016/xpad", "November 2016/xphone", "November 2016/Undefined" ], datasets: [ { data: [65, 0, 23, 0, 63, 4], label: "December 2016/xpad" }, { data: [45, 48, 3, 53, 0, 0], label: "December 2016/xphone" }, { data: [53, 0, 0, 0], label: "November 2016/xpad" }, { data: [0, 24, 2, 0], label: "November 2016/xphone" }, { data: [0, 0, 0, 48], label: "November 2016/Undefined" } ] } }; const combinations = COMBINATIONS_WITH_DATE; await this.selectGroupBy('Product'); await this.testCombinations(combinations, assert); await this.setMode('line'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year__this_month,day'] = { labels: [[0, "xpad"], [0, "xphone"], [1, "xphone"], [2, "xphone"], [2, "xpad"], [3, "xphone"], [4, "xpad"], [5, "xpad"], [3, "Undefined"]], legend: [ "2016-12-01,2016-11-01/xpad", "2016-12-01,2016-11-01/xphone", "2016-12-10,2016-11-03/xphone", "2016-12-15,2016-11-22/xphone", "2016-12-15,2016-11-22/xpad", "2016-12-17,2016-11-30/xphone", "2016-12-19/xpad", "2016-12-20/xpad", "2016-12-17,2016-11-30/Undefine..." ], datasets: [ { "data": [65, 45, 48, 3, 23, 53, 63, 4, 0], "label": "December 2016" }, { "data": [53, 0, 24, 2, 0, 0, 0, 0, 48], "label": "November 2016" } ], }; await this.setMode('pie'); await this.testCombinations(combinations, assert); assert.ok(true, "No combination causes a crash"); this.keepDateFirst = false; }); QUnit.test('comparison with two groupby with second groupby equal to comparison date field', async function (assert) { assert.expect(8); this.combinationsToCheck = { 'previous_period,this_year,quarter': { labels: [["xphone"], ["xpad"], ["Undefined"]], legend: [ "2016/Q3 2016", "2016/Q4 2016", "2015/Q2 2015" ], datasets: [ { data: [-156, 48, 53], label: "2016/Q3 2016", }, { data: [175, 208, 48], label: "2016/Q4 2016", }, { data: [0, 109, 0], label: "2015/Q2 2015", }, ] } }; const combinations = COMBINATIONS_WITH_DATE; await this.selectGroupBy('Product'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year,quarter'] = { labels: [["xphone"], ["xpad"]], legend: [ "2016/Q3 2016", "2016/Q4 2016", "2015/Q2 2015" ], datasets: [ { data: [-156, 48], label: "2016/Q3 2016", }, { data: [175, 208], label: "2016/Q4 2016", }, { data: [0, 109], label: "2015/Q2 2015", }, ] }; await this.setMode('line'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_period,this_year,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 = { 'previous_year,this_year__last_month': { labels: [["xpad"], ["xphone"], ["Undefined"]], legend: ["November 2016/false", "November 2016/true"], datasets: [ { data: [53, 24, 48], label: "November 2016/false", }, { data: [0, 2, 0], label: "November 2016/true", } ], } }; const combinations = COMBINATIONS; await this.selectGroupBy('Product'); await this.selectGroupBy('Bar'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_year,this_year__last_month'] = { labels: [["xpad"], ["xphone"]], legend: ["November 2016/false", "November 2016/true"], datasets: [ { data: [53, 24], label: "November 2016/false", }, { data: [0, 2], label: "November 2016/true", } ], }; await this.setMode('line'); await this.testCombinations(combinations, assert); this.combinationsToCheck['previous_year,this_year__last_month'] = { labels: [["xpad", false], ["xphone", false], ["xphone", true], ["Undefined", false], ["No data"]], legend: [ "xpad/false", "xphone/false", "xphone/true", "Undefined/false", "No data" ], datasets: [ { "data": [53, 24, 2, 48], "label": "November 2016" }, { "data": [undefined, undefined, undefined, undefined, 1], "label": "November 2015" } ], }; await this.setMode('pie'); await this.testCombinations(combinations, assert); assert.ok(true, "No combination causes a crash"); }); }); }); });