Files
odoo_source/addons/web/static/tests/views/graph_tests.js
T
Aaron Bohy 4f4e686fb2 [FIX] web: graph: don't lose initial groupby at reload
Before this rev., when a graph view was reloaded (e.g. by clicking
on the graph view switcher), the inital groupby was lost if it
didn't come from the searchview (i.e. if it came from the context
or from the arch). For instance, go to Sales > Reporting > Sales
(the view is grouped by 'date', and there are two groups), reload
by clicking on the graph view switcher: there is only one group
left.

The reload RPC was actually fine. What was wrong is the 'state'
object given to the renderer after the reload, as it didn't contain
the correct groupby, so the view wasn't correctly re-rendered.
2017-12-22 15:43:04 +01:00

482 lines
18 KiB
JavaScript

odoo.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: '<graph string="Partners">' +
'<field name="bar"/>' +
'</graph>',
});
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: '<graph string="Partners" type="pie">' +
'<field name="bar"/>' +
'</graph>',
});
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: '<graph string="Partners" type="line">' +
'<field name="bar"/>' +
'</graph>',
});
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: '<graph string="Partners" type="line">' +
'<field name="bar"/>' +
'</graph>',
});
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: '<graph string="Gloups">' +
'<field name="product_id"/>' +
'</graph>',
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: '<graph string="Gloups">' +
'<field name="product_id"/>' +
'</graph>',
});
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: '<graph string="Gloups">' +
'<field name="product_id"/>' +
'</graph>',
});
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: '<graph string="Gloups">' +
'<field name="product_id"/>' +
'</graph>',
});
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: '<graph><field name="product_id"/></graph>',
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: '<graph><field name="product_id" /></graph>',
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: '<graph><field name="product_id"/></graph>',
});
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: '<graph>' +
'<field name="product_id" type="row"/>' +
'<field name="foo" type="measure"/>' +
'</graph>',
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: '<graph>' +
'<field name="product_id" type="row"/>' +
'<field name="foo" type="measure"/>' +
'</graph>',
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();
});
});
});
});
});