flectra.define('web.graph_tests', function (require) { "use strict"; var concurrency = require('web.concurrency'); var GraphView = require('web.GraphView'); var testUtils = require('web.test_utils'); var createView = testUtils.createView; 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'}, color_id: {string: "Color", type: "many2one", relation: 'color'}, }, records: [ {id: 1, foo: 3, bar: true, product_id: 37}, {id: 2, foo: 53, bar: true, product_id: 37, color_id: 7}, {id: 3, foo: 2, bar: true, product_id: 37}, {id: 4, foo: 24, bar: false, product_id: 37}, {id: 5, foo: 4, bar: false, product_id: 41}, {id: 6, foo: 63, bar: false, product_id: 41}, {id: 7, foo: 42, bar: false, product_id: 41}, ] }, 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', function (assert) { assert.expect(4); var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '' + '' + '', }); var done = assert.async(); return concurrency.delay(0).then(function () { assert.strictEqual(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, 1, "should contain a div with a svg element"); assert.strictEqual(graph.renderer.state.mode, "bar", "should be in bar chart mode by default"); // here, we would like to test the svg in the dom. However, it is // not so easy, because there is an animation which means that we // don't really have a nice way to find the proper rect elements. // So, instead we will do some white box testing. assert.strictEqual(graph.model.chart.data[0].value, 3, "should have first datapoint with value 3"); assert.strictEqual(graph.model.chart.data[1].value, 4, "should have first datapoint with value 4"); graph.destroy(); done(); }); }); QUnit.test('default type attribute', function (assert) { assert.expect(1); var graph = 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('switching mode', function (assert) { assert.expect(6); var graph = 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.notOk(graph.$buttons.find('button[data-mode="bar"]').hasClass('active'), 'bar type button should not be active'); assert.ok(graph.$buttons.find('button[data-mode="line"]').hasClass('active'), 'line type button should be active'); graph.$buttons.find('button[data-mode="bar"]').click(); assert.strictEqual(graph.renderer.state.mode, "bar", "should be in bar chart mode by default"); assert.notOk(graph.$buttons.find('button[data-mode="line"]').hasClass('active'), 'line type button should not be active'); assert.ok(graph.$buttons.find('button[data-mode="bar"]').hasClass('active'), 'bar type button should be active'); graph.destroy(); }); QUnit.test('displaying line chart with only 1 data point', function (assert) { assert.expect(2); // this test makes sure the line chart does not crash when only one data // point is displayed. This was the case since a line cannot be drawn with // only one point of reference. var done = assert.async(); this.data.foo.records = this.data.foo.records.slice(0,1); var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '' + '' + '', }); return concurrency.delay(0).then(function () { assert.ok(!graph.$('svg').length, "should not have a svg"); assert.ok(graph.$('.oe_view_nocontent').length, "should have an error message"); graph.destroy(); done(); }); }); QUnit.test('switching measures', function (assert) { var done = assert.async(); assert.expect(4); var rpcCount = 0; var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '' + '' + '', mockRPC: function (route, args) { rpcCount++; return this._super(route, args); }, }); return concurrency.delay(0).then(function () { assert.ok(graph.$('text.nv-legend-text:contains(Count)').length, "should have used the correct measure"); assert.ok(graph.$buttons.find('li[data-field="foo"]').length, "should have foo in the list of measures"); graph.$buttons.find('li[data-field="foo"] a').click(); return concurrency.delay(0); }).then(function () { assert.ok(graph.$('text.nv-legend-text:contains(Foo)').length, "should now use the Foo measure"); assert.strictEqual(rpcCount, 2, "should have done 2 rpcs (2 readgroups)"); graph.destroy(); done(); }); }); QUnit.test('no content helper', function (assert) { var done = assert.async(); assert.expect(4); var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '' + '' + '', }); return concurrency.delay(0).then(function () { assert.ok(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, "should contain a div with a svg element"); assert.notOk(graph.$('div.oe_view_nocontent').length, "should not display the no content helper"); graph.update({domain: [['product_id', '=', 4]]}); assert.notOk(graph.$('div.o_graph_svg_container svg.nvd3-svg').length, "should not contain a div with a svg element"); assert.ok(graph.$('div.oe_view_nocontent').length, "should display the no content helper"); graph.destroy(); done(); }); }); QUnit.test('can reload with other group by', function (assert) { var done = assert.async(); assert.expect(4); var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '' + '' + '', }); return concurrency.delay(0).then(function () { assert.ok(graph.$('text:contains(xphone)').length, "should contain a text element with product in legend"); assert.notOk(graph.$('text:contains(red)').length, "should not contain a text element with color in legend"); graph.update({groupBy: ['color_id']}); return concurrency.delay(0); }).then(function () { assert.notOk(graph.$('text:contains(xphone)').length, "should not contain a text element with product in legend"); assert.ok(graph.$('text:contains(red)').length, "should contain a text element with color in legend"); graph.destroy(); done(); }); }); QUnit.test('getContext correctly returns mode, measure and groupbys', function (assert) { var done = assert.async(); assert.expect(4); var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '' + '' + '', }); return concurrency.delay(0).then(function () { assert.deepEqual(graph.getContext(), { graph_mode: 'bar', graph_measure: '__count__', graph_groupbys: ['product_id'], }, "context should be correct"); graph.$buttons.find('li[data-field="foo"] a').click(); // change measure return concurrency.delay(0); }).then(function () { assert.deepEqual(graph.getContext(), { graph_mode: 'bar', graph_measure: 'foo', graph_groupbys: ['product_id'], }, "context should be correct"); graph.$buttons.find('button[data-mode="line"]').click(); // change mode return concurrency.delay(0); }).then(function () { assert.deepEqual(graph.getContext(), { graph_mode: 'line', graph_measure: 'foo', graph_groupbys: ['product_id'], }, "context should be correct"); graph.update({groupBy: ['product_id', 'color_id']}); // change groupbys return concurrency.delay(0); }).then(function () { assert.deepEqual(graph.getContext(), { graph_mode: 'line', graph_measure: 'foo', graph_groupbys: ['product_id', 'color_id'], }, "context should be correct"); graph.destroy(); done(); }); }); QUnit.test('correctly uses graph_ keys from the context', function (assert) { var done = assert.async(); assert.expect(6); var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '', viewOptions: { context: { graph_measure: 'foo', graph_mode: 'line', graph_groupbys: ['color_id'], }, }, }); return concurrency.delay(0).then(function () { // check measure assert.strictEqual(graph.$('text.nv-legend-text:contains(Foo)').length, 1, "should now use the 'foo' measure"); // check mode assert.strictEqual(graph.renderer.state.mode, "line", "should be in line chart mode"); assert.notOk(graph.$buttons.find('button[data-mode="bar"]').hasClass('active'), 'bar chart button should not be active'); assert.ok(graph.$buttons.find('button[data-mode="line"]').hasClass('active'), 'line chart button should be active'); // check groupbys assert.strictEqual(graph.$('text:contains(xphone)').length, 0, "should not contain a text element with product in legend"); assert.strictEqual(graph.$('text:contains(red)').length, 1, "should contain a text element with color in legend"); graph.destroy(); done(); }); }); QUnit.test('correctly use group_by key from the context', function (assert) { var done = assert.async(); assert.expect(2); var graph = createView({ View: GraphView, model: 'foo', data: this.data, arch: '', groupBy: ['color_id'], viewOptions: { context: { graph_measure: 'foo', graph_mode: 'line', }, }, }); return concurrency.delay(0).then(function () { assert.strictEqual(graph.$('text:contains(xphone)').length, 0, 'should not contain a text element with product in legend'); assert.strictEqual(graph.$('text:contains(red)').length, 1, 'should contain a text element with color in legend'); graph.destroy(); done(); }); }); QUnit.test('correctly uses graph_ keys from the context (at reload)', function (assert) { var done = assert.async(); assert.expect(8); var graph = createView({ View: GraphView, model: "foo", data: this.data, arch: '', }); assert.strictEqual(graph.renderer.state.mode, "bar", "should be in bar chart mode"); assert.ok(graph.$buttons.find('button[data-mode="bar"]').hasClass('active'), 'bar chart button should be active'); var reloadParams = { context: { graph_measure: 'foo', graph_mode: 'line', graph_groupbys: ['color_id'], }, }; graph.reload(reloadParams); return concurrency.delay(0).then(function () { // check measure assert.strictEqual(graph.$('text.nv-legend-text:contains(Foo)').length, 1, "should now use the 'foo' measure"); // check mode assert.strictEqual(graph.renderer.state.mode, "line", "should be in line chart mode"); assert.notOk(graph.$buttons.find('button[data-mode="bar"]').hasClass('active'), 'bar chart button should not be active'); assert.ok(graph.$buttons.find('button[data-mode="line"]').hasClass('active'), 'line chart button should be active'); // check groupbys assert.strictEqual(graph.$('text:contains(xphone)').length, 0, "should not contain a text element with product in legend"); assert.strictEqual(graph.$('text:contains(red)').length, 1, "should contain a text element with color in legend"); graph.destroy(); done(); }); }); QUnit.test('reload graph with correct fields', function (assert) { assert.expect(2); var graph = 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); }, }); graph.reload({groupBy: []}); graph.destroy(); }); QUnit.test('initial groupby is kept when reloading', function (assert) { var done = assert.async(); assert.expect(4); var graph = 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); }, }); return concurrency.delay(0).then(function () { assert.strictEqual(graph.$('.nv-groups rect').length, 2, "should display two groups"); graph.reload({groupBy: []}); return concurrency.delay(0).then(function () { assert.strictEqual(graph.$('.nv-groups rect').length, 2, "should still display two groups"); graph.destroy(); done(); }); }); }); }); });