mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
8162a6d959
Summary: Forcing category xaxis type for bar chart when labels are not numerical. Test Plan: Added new and updated existing Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D4297
846 lines
32 KiB
TypeScript
846 lines
32 KiB
TypeScript
import {UserAPI} from 'app/common/UserAPI';
|
|
import {assert, driver, Key} from 'mocha-webdriver';
|
|
import {addYAxis, checkAxisConfig, checkAxisRange, findYAxis, getAxisTitle, getChartData,
|
|
removeYAxis, selectChartType, selectXAxis,
|
|
setSplitSeries} from 'test/nbrowser/chartViewTestUtils';
|
|
import * as gu from 'test/nbrowser/gristUtils';
|
|
import {setupTestSuite} from 'test/nbrowser/testUtils';
|
|
|
|
describe('ChartView1', function() {
|
|
this.timeout(20000);
|
|
const cleanup = setupTestSuite();
|
|
let api: UserAPI;
|
|
let doc: any;
|
|
|
|
before(async function() {
|
|
const session = await gu.session().teamSite.login();
|
|
doc = await session.tempDoc(cleanup, 'ChartData.grist');
|
|
api = session.createHomeApi();
|
|
});
|
|
|
|
gu.bigScreen();
|
|
afterEach(() => gu.checkForErrors());
|
|
|
|
it('should treat text as category', async function() {
|
|
const revert = await gu.begin();
|
|
await gu.sendActions([
|
|
['AddTable', 'Text', [
|
|
{id: 'X', type: 'Int'},
|
|
{id: 'Y', type: 'Int'}
|
|
]],
|
|
['AddRecord', 'Text', null, {X: 1, Y: 1}],
|
|
['AddRecord', 'Text', null, {X: 100, Y: 2}],
|
|
['AddRecord', 'Text', null, {X: 101, Y: 3}],
|
|
['AddRecord', 'Text', null, {X: 102, Y: 4}],
|
|
]);
|
|
await gu.openPage('Text');
|
|
await gu.addNewSection('Chart', 'Text');
|
|
|
|
const layout = () => getChartData().then(d => d.layout);
|
|
|
|
await gu.waitToPass(async () => {
|
|
// Check to make sure initial values are correct.
|
|
assert.deepEqual((await layout()).xaxis.type, 'linear');
|
|
});
|
|
|
|
// Now convert X to text.
|
|
await gu.sendActions([
|
|
['ModifyColumn', 'Text', 'X', {type: 'Text'}],
|
|
]);
|
|
assert.deepEqual((await layout()).xaxis.type, 'category');
|
|
|
|
// Invert the chart.
|
|
await gu.selectSectionByTitle('Text Chart');
|
|
await gu.toggleSidePanel('right', 'open');
|
|
await driver.find('.test-chart-orientation .test-select-open').click();
|
|
await driver.findContent('.test-select-menu', 'Horizontal').click();
|
|
await gu.waitForServer();
|
|
assert.deepEqual((await layout()).yaxis.type, 'category');
|
|
assert.deepEqual((await layout()).xaxis.type, 'linear');
|
|
|
|
await revert();
|
|
await gu.toggleSidePanel('right', 'close');
|
|
});
|
|
|
|
it('should allow adding and removing chart viewsections', async function() {
|
|
// Starting out with one section
|
|
assert.lengthOf(await driver.findAll('.test-gristdoc .view_leaf'), 1);
|
|
|
|
// Add a new chart section
|
|
await gu.addNewSection(/Chart/, /ChartData/);
|
|
|
|
// Check that there are now two sections
|
|
assert.lengthOf(await driver.findAll('.test-gristdoc .view_leaf'), 2);
|
|
|
|
// Delete the newly added one
|
|
await gu.openSectionMenu('viewLayout', 'CHARTDATA Chart');
|
|
await driver.find('.test-section-delete').click();
|
|
await gu.waitForServer();
|
|
|
|
// Check that there is now only one section
|
|
assert.lengthOf(await driver.findAll('.test-gristdoc .view_leaf'), 1);
|
|
});
|
|
|
|
it('should display a bar chart by default', async function() {
|
|
// Add a new chart section, and make sure it has focus
|
|
await gu.addNewSection(/Chart/, /ChartData/);
|
|
const section = await gu.getSection('CHARTDATA Chart');
|
|
assert.equal(await section.matches('.active_section'), true);
|
|
|
|
const chartDom = await section.find('.test-chart-container');
|
|
assert.equal(await chartDom.isDisplayed(), true);
|
|
|
|
const data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].x, [ 6, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 1, 2, 3, 4, 5, 6 ]);
|
|
});
|
|
|
|
it('should allow viewing raw data underlying chart', async function() {
|
|
// No raw data overlay at first
|
|
assert.isFalse(await driver.find('.test-raw-data-overlay').isPresent());
|
|
|
|
// Show raw data overlay
|
|
await gu.openSectionMenu('viewLayout');
|
|
await driver.find('.test-show-raw-data').click();
|
|
|
|
// Test that overlay is showed.
|
|
assert.isTrue(await driver.findWait('.test-raw-data-overlay', 100).isDisplayed());
|
|
|
|
// Test that the widget menu doesn't have the raw data option any more
|
|
await gu.openSectionMenu('viewLayout');
|
|
assert.isTrue(await driver.findContentWait('.grist-floating-menu li', 'Print widget', 100).isDisplayed());
|
|
assert.isFalse(await driver.findContent('.grist-floating-menu li', 'Show raw data').isPresent());
|
|
|
|
// Go back and confirm that the overlay is gone again
|
|
await driver.find('.test-raw-data-close-button').click();
|
|
assert.isFalse(await driver.find('.test-raw-data-overlay').isPresent());
|
|
|
|
// Open once again and close by escaping.
|
|
await gu.openSectionMenu('viewLayout');
|
|
await driver.find('.test-show-raw-data').click();
|
|
assert.isTrue(await driver.findWait('.test-raw-data-overlay', 100).isDisplayed());
|
|
await gu.sendKeys(Key.ESCAPE);
|
|
assert.isFalse(await driver.find('.test-raw-data-overlay').isPresent());
|
|
});
|
|
|
|
it('should update as the underlying data changes', async function() {
|
|
await gu.getCell({section: 'ChartData', col: 0, rowNum: 1}).click();
|
|
await driver.sendKeys(Key.ENTER, '1', Key.ENTER); // Change from 6 to 61
|
|
await gu.waitForServer();
|
|
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
let data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].x, [ 61, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 1, 2, 3, 4, 5, 6 ]);
|
|
|
|
await gu.getCell({section: 'ChartData', col: 1, rowNum: 1}).click();
|
|
await driver.sendKeys(Key.ENTER, '6', Key.ENTER); // Change from 1 to 16
|
|
await gu.waitForServer();
|
|
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].x, [ 61, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 16, 2, 3, 4, 5, 6 ]);
|
|
});
|
|
|
|
it('should skip empty points', async function() {
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
let data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].x, [ 61, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 16, 2, 3, 4, 5, 6 ]);
|
|
|
|
// Enter some blank values and a zero. The zero should be included in the plot, but blanks
|
|
// should not.
|
|
await gu.getCell({col: 1, rowNum: 1}).click();
|
|
await driver.sendKeys(Key.DELETE);
|
|
await gu.getCell({col: 1, rowNum: 4}).click();
|
|
await driver.sendKeys(Key.DELETE);
|
|
await gu.getCell({col: 1, rowNum: 6}).click();
|
|
await driver.sendKeys('0', Key.ENTER);
|
|
await gu.waitForServer();
|
|
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].x, [ 5, 4, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 2, 3, 5, 0 ]);
|
|
|
|
// Undo and verify that the range is restored.
|
|
await gu.undo(3);
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].x, [ 61, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 16, 2, 3, 4, 5, 6 ]);
|
|
});
|
|
|
|
it('should update chart when new columns are included', async function() {
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
// Check to make sure initial values are correct.
|
|
let data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].x, [ 61, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 16, 2, 3, 4, 5, 6 ]);
|
|
|
|
// Check that the intial scales are correct for the dataset.
|
|
checkAxisRange(await getChartData(chartDom), 0.5, 61.5, 0, 16.5);
|
|
|
|
// Open the view config pane for the Chart section.
|
|
await gu.getSection('ChartData chart').find('.viewsection_title').click();
|
|
await gu.toggleSidePanel('right', 'open');
|
|
await driver.find('.test-right-tab-pagewidget').click();
|
|
await driver.find('.test-config-widget').click();
|
|
|
|
// Check intial visible fields.
|
|
await checkAxisConfig({
|
|
xaxis: 'label',
|
|
yaxis: ['value']
|
|
});
|
|
|
|
// Adds 'largeValue'
|
|
await driver.find('.test-chart-add-y-axis').click();
|
|
await driver.findContent('.grist-floating-menu li', 'largeValue').click();
|
|
await gu.waitForServer();
|
|
|
|
// Check axis are correct
|
|
await checkAxisConfig({
|
|
xaxis: 'label',
|
|
yaxis: ['value', 'largeValue']
|
|
});
|
|
|
|
// Move 'largeValue' above 'value'. Scroll it into view first, since dragging is a bit messed
|
|
// up when it causes the pane to scroll.
|
|
await gu.scrollIntoView(findYAxis('largeValue'));
|
|
await driver.withActions((actions) => actions.dragAndDrop(findYAxis('largeValue'), findYAxis('value')));
|
|
await gu.waitForServer();
|
|
|
|
await checkAxisConfig({
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value']
|
|
});
|
|
|
|
// Make sure only y axis updates to the new column of data
|
|
await driver.sleep(50);
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].x, [ 61, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[0].y, [ 22, 33, 11, 44, 22, 55 ]);
|
|
assert.deepEqual(data[1].type, 'bar');
|
|
assert.deepEqual(data[1].x, [ 61, 5, 4, 3, 2, 1 ]);
|
|
assert.deepEqual(data[1].y, [ 16, 2, 3, 4, 5, 6 ]);
|
|
|
|
// Check that the scales are correct for the new y values.
|
|
checkAxisRange(await getChartData(chartDom), 0.5, 61.5, 0, 57);
|
|
|
|
// select 'largeValue' as x axis
|
|
await selectXAxis('largeValue');
|
|
|
|
// check x-axis is correct
|
|
await checkAxisConfig({
|
|
xaxis: 'largeValue',
|
|
yaxis: ['value'] // note: 'largeValue' was correctly removed from y-axis
|
|
});
|
|
|
|
// adds 'label' as y axis
|
|
await addYAxis('label');
|
|
|
|
// check axis are correct
|
|
await checkAxisConfig({
|
|
xaxis: 'largeValue',
|
|
yaxis: ['value', 'label']
|
|
});
|
|
|
|
// Reverse the order of the columns and make sure the data updates to reflect that.
|
|
await driver.sleep(50);
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].x, [ 22, 33, 11, 44, 55 ]);
|
|
assert.deepEqual(data[0].y, [ 16, 2, 3, 4, 6 ]);
|
|
assert.deepEqual(data[1].type, 'bar');
|
|
assert.deepEqual(data[1].x, [ 22, 33, 11, 44, 55 ]);
|
|
assert.deepEqual(data[1].y, [ 61, 5, 4, 3, 1 ]);
|
|
|
|
// Check that the scales are correct for the new values.
|
|
checkAxisRange(await getChartData(chartDom), 5.5, 60.5, 0, 61);
|
|
|
|
// select 'label' as x axis
|
|
await selectXAxis('label');
|
|
|
|
// adds 'largeValue' as y axis
|
|
await addYAxis('largeValue');
|
|
|
|
// moves 'largeValue' above 'value'
|
|
await driver.withActions((actions) => actions.dragAndDrop(findYAxis('largeValue'), findYAxis('value')));
|
|
await gu.waitForServer();
|
|
|
|
// check axis correctness
|
|
await checkAxisConfig({
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value']
|
|
});
|
|
});
|
|
|
|
it('should be able to render different types of charts', async function() {
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
|
|
await selectChartType('Pie Chart');
|
|
let data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'pie');
|
|
assert.equal(await driver.find('.test-chart-first-field-label').getText(), 'LABEL');
|
|
await selectChartType('Line Chart');
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'scatter');
|
|
// Make sure we are not grouping (which would produce names like "1 · value")
|
|
assert.equal(data[0].name, 'largeValue');
|
|
assert.equal(data[1].name, 'value');
|
|
assert.equal(await driver.find('.test-chart-first-field-label').getText(), 'X-AXIS');
|
|
|
|
await selectChartType('Area Chart');
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'scatter');
|
|
assert.deepEqual(data[0].line!.shape, 'spline');
|
|
assert.deepEqual(data[0].fill, 'tozeroy');
|
|
assert.equal(await driver.find('.test-chart-type').getText(), 'Area Chart');
|
|
|
|
// Make sure first field of scatter plot is marked label, not x-axis.
|
|
await selectChartType('Scatter Plot');
|
|
assert.equal(await driver.find('.test-chart-first-field-label').getText(), 'LABEL');
|
|
|
|
// Make sure first field of Kaplan-Meier plot is marked label, not x-axis.
|
|
await selectChartType('Kaplan-Meier Plot');
|
|
assert.equal(await driver.find('.test-chart-first-field-label').getText(), 'LABEL');
|
|
|
|
// Return to Area Chart.
|
|
await selectChartType('Area Chart');
|
|
});
|
|
|
|
it('should render pie charts with a single series, or counts', async function() {
|
|
await selectChartType('Pie Chart');
|
|
|
|
// select 'person' for x axis
|
|
await selectXAxis('person');
|
|
|
|
// adds 'label' and move to be first y axis
|
|
await addYAxis('label');
|
|
await driver.withActions((actions) => actions.dragAndDrop(findYAxis('label'), findYAxis('largeValue')));
|
|
await gu.waitForServer();
|
|
|
|
// check axis
|
|
await checkAxisConfig({
|
|
xaxis: 'person',
|
|
yaxis: ['label', 'largeValue', 'value']
|
|
});
|
|
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
let data = (await getChartData(chartDom)).data;
|
|
// Only the first series of values is included.
|
|
assert.deepEqual(data[0].values, [ 61, 4, 2, 5, 3, 1 ]);
|
|
assert.lengthOf(data, 1);
|
|
|
|
// When no series is included, just counts are used.
|
|
await removeYAxis('largeValue');
|
|
await removeYAxis('label');
|
|
await removeYAxis('value');
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].values, [1, 1, 1, 1, 1, 1]);
|
|
assert.lengthOf(data, 1);
|
|
|
|
await gu.undo(7);
|
|
|
|
// check axis
|
|
await checkAxisConfig({
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value']
|
|
});
|
|
|
|
// check chart type
|
|
assert.equal(await driver.find('.test-chart-type').getText(), 'Area Chart');
|
|
});
|
|
|
|
it('should support Y-axis options', async function() {
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
await selectChartType('Bar Chart');
|
|
checkAxisRange(await getChartData(chartDom), 0.5, 61.5, 0, 57);
|
|
|
|
await driver.findContent('label', /Invert Y-axis/).find('input').click();
|
|
await gu.waitForServer();
|
|
checkAxisRange(await getChartData(chartDom), 0.5, 61.5, 57, 0);
|
|
|
|
await driver.findContent('label', /Invert Y-axis/).find('input').click();
|
|
await driver.findContent('label', /Log scale Y-axis/).find('input').click();
|
|
await gu.waitForServer();
|
|
checkAxisRange(await getChartData(chartDom), 0.5, 61.5, 0.22, 1.82);
|
|
|
|
await gu.undo(4);
|
|
// check axis
|
|
await checkAxisConfig({
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value']
|
|
});
|
|
// check chart type
|
|
assert.equal(await driver.find('.test-chart-type').getText(), 'Area Chart');
|
|
});
|
|
|
|
it('should be able to render multiseries line charts', async function() {
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
|
|
// switch type to line chart
|
|
await selectChartType('Line Chart');
|
|
|
|
// pick 'largeValue' as the x axis
|
|
await selectXAxis('largeValue');
|
|
|
|
// set 'label' as the groupby column
|
|
await setSplitSeries('label');
|
|
|
|
let {data, layout} = await getChartData(chartDom);
|
|
assert.deepEqual(data[0].type, 'scatter');
|
|
assert.deepEqual(data.map(d => d.name), ['1', '2', '3', '4', '5', '61']);
|
|
assert.equal(getAxisTitle(layout.xaxis), 'largeValue');
|
|
assert.equal(getAxisTitle(layout.yaxis), 'value');
|
|
|
|
// Select person for grouping by column
|
|
await setSplitSeries('person');
|
|
|
|
await checkAxisConfig({
|
|
groupingByColumn: 'person',
|
|
xaxis: 'largeValue',
|
|
yaxis: ['value'],
|
|
});
|
|
|
|
({data, layout} = await getChartData(chartDom));
|
|
assert.deepEqual(data[0].type, 'scatter');
|
|
assert.deepEqual(data.map(d => d.name), ['Alice', 'Bob']);
|
|
assert.equal(getAxisTitle(layout.xaxis), 'largeValue');
|
|
assert.equal(getAxisTitle(layout.yaxis), 'value');
|
|
|
|
// Add a second series. If we have more than one, its name should be included into the series
|
|
// names rather than in the yaxis.title.
|
|
await addYAxis('label');
|
|
|
|
await checkAxisConfig({
|
|
groupingByColumn: 'person',
|
|
xaxis: 'largeValue',
|
|
yaxis: ['value', 'label'],
|
|
});
|
|
|
|
({data, layout} = await getChartData(chartDom));
|
|
assert.deepEqual(data[0].type, 'scatter');
|
|
assert.deepEqual(data.map(d => d.name), ['Alice • value', 'Alice • label', 'Bob • value', 'Bob • label']);
|
|
assert.equal(getAxisTitle(layout.xaxis), 'largeValue');
|
|
assert.equal(getAxisTitle(layout.yaxis), undefined);
|
|
|
|
await gu.undo(5);
|
|
await checkAxisConfig({
|
|
groupingByColumn: false,
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value'],
|
|
});
|
|
// check chart type
|
|
assert.equal(await driver.find('.test-chart-type').getText(), 'Area Chart');
|
|
});
|
|
|
|
it('should get options for SPLIT SERIES and X AXIS in sync when table changes', async function() {
|
|
|
|
// click change widget
|
|
await driver.findContent('button', 'Change Widget').click();
|
|
|
|
// click sum symbol
|
|
await driver.findContent('.test-wselect-table', 'People').click();
|
|
|
|
// click save
|
|
await driver.find('.test-wselect-addBtn').click();
|
|
await gu.waitForServer();
|
|
|
|
// click Split series
|
|
await driver.findContent('label', 'Split series').click();
|
|
|
|
// open split series options
|
|
await driver.find('.test-chart-group-by-column').click();
|
|
|
|
// check group-data options
|
|
assert.deepEqual(
|
|
await driver.findAll('.test-select-menu li', e => e.getText()),
|
|
['Pick a column', 'Name', 'B']
|
|
);
|
|
|
|
// send ESCAPE to close menu
|
|
await driver.sendKeys(Key.ESCAPE);
|
|
|
|
// open x axis options
|
|
await driver.find('.test-chart-x-axis').click();
|
|
|
|
// check x axis options
|
|
assert.deepEqual(
|
|
await driver.findAll('.test-select-menu li', e => e.getText()),
|
|
['Name', 'B']
|
|
);
|
|
|
|
// send ESCAPE to close menu
|
|
await driver.sendKeys(Key.ESCAPE);
|
|
|
|
// undo
|
|
await gu.undo(1);
|
|
});
|
|
|
|
it('should get series name right when grouped column has \'\' values', async function() {
|
|
// remove series 'value'
|
|
await removeYAxis('value');
|
|
|
|
// add a row with person left as blank
|
|
const {retValues} = await api.applyUserActions(doc.id, [
|
|
['AddRecord', 'ChartData', 7, {largeValue: 44}]
|
|
]);
|
|
await setSplitSeries('person');
|
|
|
|
// check that series name is correct
|
|
const data = (await getChartData()).data;
|
|
assert.deepEqual(data.map(d => d.name), ['[Blank]', 'Alice', 'Bob']);
|
|
|
|
// remove row
|
|
await api.applyUserActions(doc.id, [
|
|
['RemoveRecord', 'ChartData', retValues[0]]
|
|
]);
|
|
|
|
// undo
|
|
await gu.undo(2);
|
|
});
|
|
|
|
it('should disabled split series option for pie charts', async function() {
|
|
|
|
// start with line chart type
|
|
await selectChartType('Line Chart');
|
|
|
|
// check the split series option is present
|
|
assert.equal(await driver.findContent('label', /Split series/).isPresent(), true);
|
|
assert.equal(await driver.find('.test-chart-group-by-column').isPresent(), true);
|
|
|
|
// select 'person' as the split series column
|
|
await setSplitSeries('person');
|
|
|
|
// check split series option
|
|
assert.equal(await driver.findContent('label', /Split series/).isPresent(), true);
|
|
assert.equal(await driver.find('.test-chart-group-by-column').isPresent(), true);
|
|
|
|
// check axis
|
|
await checkAxisConfig({
|
|
groupingByColumn: 'person',
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value'],
|
|
});
|
|
|
|
// select pie chart type
|
|
await selectChartType('Pie Chart');
|
|
|
|
// check that the split series option is not present
|
|
assert.equal(await driver.findContent('label', /Split series/).isPresent(), false);
|
|
assert.equal(await driver.find('.test-chart-group-by-column').isPresent(), false);
|
|
|
|
// check axis
|
|
await checkAxisConfig({
|
|
groupingByColumn: false,
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value'],
|
|
});
|
|
assert.equal(await driver.find('.test-chart-type').getText(), 'Pie Chart');
|
|
|
|
// undo
|
|
await gu.undo(2);
|
|
await checkAxisConfig({
|
|
groupingByColumn: false,
|
|
xaxis: 'label',
|
|
yaxis: ['largeValue', 'value'],
|
|
});
|
|
assert.equal(await driver.find('.test-chart-type').getText(), 'Line Chart');
|
|
});
|
|
|
|
it('should render dates properly on X-axis', async function() {
|
|
await gu.getSection('ChartData').find('.viewsection_title').click();
|
|
|
|
// Add a new first column.
|
|
await gu.getCell({col: 0, rowNum: 1}).click();
|
|
// driver.sendKeys() doesn't support key combinations, but elem.sendKeys() does.
|
|
await driver.find('body').sendKeys(Key.chord(Key.ALT, Key.SHIFT, '='));
|
|
await gu.waitForServer();
|
|
await driver.find('.test-column-title-label').sendKeys('MyDate', Key.ENTER);
|
|
await gu.waitForServer();
|
|
|
|
// Convert it to Date
|
|
await gu.toggleSidePanel('right', 'open');
|
|
await driver.find('.test-right-tab-field').click();
|
|
await gu.setType(/Date/);
|
|
await gu.waitForServer();
|
|
|
|
// Enter some values.
|
|
await gu.enterGridRows({col: 0, rowNum: 1}, [
|
|
["2018-01-15"], ["2018-01-31"], ["2018-02-14"], ["2018-03-04"], ["2018-03-14"], ["2018-03-26"]
|
|
]);
|
|
|
|
// Open the view config pane for the Chart section.
|
|
await gu.getSection('ChartData chart').find('.viewsection_title').click();
|
|
await driver.find('.test-right-tab-pagewidget').click();
|
|
|
|
// select MyDate for x axis
|
|
await selectXAxis('MyDate');
|
|
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
const {data, layout} = await getChartData(chartDom);
|
|
// This check helps understand Plotly's actual interpretation of the dates. E.g. if the range
|
|
// endpoints are like '2018-03-25 20:00', plotly is misinterpreting the timezone.
|
|
assert.deepEqual(layout.xaxis.range, ['2018-01-15', '2018-03-26']);
|
|
assert.deepEqual(data[0].type, 'scatter');
|
|
assert.deepEqual(data[0].name, 'largeValue');
|
|
assert.deepEqual(data[0].x, [
|
|
"2018-01-15T00:00:00.000Z", "2018-01-31T00:00:00.000Z", "2018-02-14T00:00:00.000Z",
|
|
"2018-03-04T00:00:00.000Z", "2018-03-14T00:00:00.000Z", "2018-03-26T00:00:00.000Z"
|
|
]);
|
|
assert.deepEqual(data[0].y, [22, 33, 11, 44, 22, 55]);
|
|
assert.deepEqual(data[1].type, 'scatter');
|
|
assert.deepEqual(data[1].name, 'value');
|
|
assert.deepEqual(data[0].x, [
|
|
"2018-01-15T00:00:00.000Z", "2018-01-31T00:00:00.000Z", "2018-02-14T00:00:00.000Z",
|
|
"2018-03-04T00:00:00.000Z", "2018-03-14T00:00:00.000Z", "2018-03-26T00:00:00.000Z"
|
|
]);
|
|
assert.deepEqual(data[1].y, [16, 2, 3, 4, 5, 6]);
|
|
});
|
|
|
|
it('should support error bars', async function() {
|
|
// We start with a line chart with MyDate on X-axis, and two series: largeValue and value.
|
|
await selectChartType('Line Chart');
|
|
await checkAxisConfig({xaxis: 'MyDate', yaxis: ['largeValue', 'value']});
|
|
|
|
// Symmetric error bars should leave only the largeValue series, with 'value' for error bars.
|
|
await driver.find('.test-chart-error-bars .test-select-open').click();
|
|
await driver.findContent('.test-select-menu li', /Symmetric/).click();
|
|
await gu.waitForServer();
|
|
|
|
const chartDom = await driver.find('.test-chart-container');
|
|
let data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'scatter');
|
|
assert.deepEqual(data[0].name, 'largeValue');
|
|
assert.deepEqual(data[0].y, [22, 33, 11, 44, 22, 55]);
|
|
assert.deepEqual((data[0].error_y as any).array, [16, 2, 3, 4, 5, 6]);
|
|
assert.deepEqual(data[0].error_y!.symmetric, true);
|
|
assert.lengthOf(data, 1);
|
|
|
|
// Using separate error bars for above+below will leave just the "above" error bars.
|
|
await driver.find('.test-chart-error-bars .test-select-open').click();
|
|
await driver.findContent('.test-select-menu li', /Above.*Below/).click();
|
|
await gu.waitForServer();
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].y, [22, 33, 11, 44, 22, 55]);
|
|
assert.deepEqual((data[0].error_y as any).array, [16, 2, 3, 4, 5, 6]);
|
|
assert.deepEqual((data[0].error_y as any).arrayminus, null);
|
|
assert.deepEqual(data[0].error_y!.symmetric, false);
|
|
assert.lengthOf(data, 1);
|
|
|
|
// If we add another line, it'll be used for "below" error bars.
|
|
await addYAxis('label');
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].y, [22, 33, 11, 44, 22, 55]);
|
|
assert.deepEqual((data[0].error_y as any).array, [16, 2, 3, 4, 5, 6]);
|
|
assert.deepEqual((data[0].error_y as any).arrayminus, [61, 5, 4, 3, 2, 1]);
|
|
assert.deepEqual(data[0].error_y!.symmetric, false);
|
|
assert.lengthOf(data, 1);
|
|
|
|
// Should work also for bar charts
|
|
await selectChartType('Bar Chart');
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].y, [22, 33, 11, 44, 22, 55]);
|
|
assert.deepEqual((data[0].error_y as any).array, [16, 2, 3, 4, 5, 6]);
|
|
assert.deepEqual((data[0].error_y as any).arrayminus, [61, 5, 4, 3, 2, 1]);
|
|
assert.deepEqual(data[0].error_y!.symmetric, false);
|
|
assert.lengthOf(data, 1);
|
|
await gu.undo(1);
|
|
|
|
|
|
await gu.undo(3);
|
|
});
|
|
|
|
it('should fetch data for tables not yet loaded', async function() {
|
|
// Create a Page that only has a Chart, no other sections.
|
|
await gu.addNewPage(/Chart/, /ChartData/);
|
|
|
|
let chartDom = await driver.findWait('.test-chart-container', 1000);
|
|
assert.equal(await chartDom.isDisplayed(), true);
|
|
let data = (await getChartData(chartDom)).data;
|
|
assert.lengthOf(data, 1);
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].y, [ 61, 5, 4, 3, 2, 1 ]);
|
|
|
|
// Reload the page and test that the chart loaded.
|
|
await driver.navigate().refresh();
|
|
await gu.waitForDocToLoad();
|
|
|
|
await driver.sleep(1000);
|
|
chartDom = await driver.findWait('.test-chart-container', 1000);
|
|
assert.equal(await chartDom.isDisplayed(), true);
|
|
data = (await getChartData(chartDom)).data;
|
|
assert.lengthOf(data, 1);
|
|
assert.deepEqual(data[0].type, 'bar');
|
|
assert.deepEqual(data[0].y, [ 61, 5, 4, 3, 2, 1 ]);
|
|
});
|
|
|
|
it('should resize chart when side panels open or close', async function() {
|
|
// Open a document with some chart data.
|
|
const session = await gu.session().teamSite.login();
|
|
doc = await session.tempDoc(cleanup, 'ChartData.grist');
|
|
await gu.toggleSidePanel('right', 'close');
|
|
|
|
// Add a chart section.
|
|
await gu.addNewSection(/Chart/, /ChartData/);
|
|
const chart = await driver.findWait('.viewsection_content .svg-container', 1000);
|
|
const initialRect = await chart.getRect();
|
|
// We expect the left panel open initially.
|
|
assert.equal(await gu.isSidePanelOpen('left'), true);
|
|
|
|
// Open the RightPanel, check that chart's width was reduced.
|
|
await gu.toggleSidePanel('right', 'open');
|
|
await driver.wait(async () => (await chart.getRect()).width < initialRect.width, 1000);
|
|
|
|
// Close the panel and check the chart went back to initial size.
|
|
await gu.toggleSidePanel('right', 'close');
|
|
await driver.wait(async () => (await chart.getRect()).width === initialRect.width, 1000);
|
|
assert.deepEqual(await chart.getRect(), initialRect);
|
|
|
|
// Close the left panel, and check that chart width was increased.
|
|
await gu.toggleSidePanel('left', 'close');
|
|
await driver.wait(async () => (await chart.getRect()).width > initialRect.width, 1000);
|
|
|
|
// Reopen the left panel and check the chart went back to initial size.
|
|
await gu.toggleSidePanel('left', 'open');
|
|
await driver.wait(async () => (await chart.getRect()).width === initialRect.width, 1000);
|
|
assert.deepEqual(await chart.getRect(), initialRect);
|
|
});
|
|
|
|
// Tests a bug where js errors would be thrown when fewer than 2 series were visible
|
|
// and any chart settings were changed.
|
|
it('should not throw errors when no y-axis are set', async function() {
|
|
// Open the RightPanel and hide both series.
|
|
await gu.toggleSidePanel('right', 'open');
|
|
await removeYAxis('value');
|
|
|
|
// Invert the y-axis. (This is meant to trigger js errors if the bug is present)
|
|
await driver.findContent('label', /Invert Y-axis/).find('input').click();
|
|
await gu.waitForServer();
|
|
|
|
// Group by the first column. (This is meant to trigger js errors if the bug is present)
|
|
await setSplitSeries('value');
|
|
|
|
// Disable groupby column
|
|
await setSplitSeries(false);
|
|
|
|
// Revert changes.
|
|
await gu.undo(3);
|
|
});
|
|
|
|
// Tests a bug where hitting enter would try to edit a non-existent cell for summary charts.
|
|
it('should not throw errors when pressing enter on summary charts', async function() {
|
|
// Click the section and press 'Enter'.
|
|
await gu.getSection('ChartData chart').click();
|
|
await driver.sendKeys(Key.ENTER);
|
|
await gu.checkForErrors();
|
|
});
|
|
|
|
it('should not throw errors when switching to a chart page', async function() {
|
|
await gu.getPageItem('People').click();
|
|
await gu.waitForServer();
|
|
await gu.getPageItem('ChartData').click();
|
|
await gu.waitForServer();
|
|
const chartDom = await gu.getSection('ChartData chart').find('.test-chart-container');
|
|
assert.equal(await chartDom.isDisplayed(), true);
|
|
await gu.checkForErrors();
|
|
});
|
|
|
|
it('should not throw errors when summarizing or un-summarizing underlying table', async function() {
|
|
// activate the chart widget
|
|
await gu.getSection('ChartData chart').click();
|
|
|
|
// open widget option
|
|
await gu.openSectionMenu('viewLayout');
|
|
await driver.findContent('.grist-floating-menu li', 'Widget options').click();
|
|
|
|
// open the page widget picker
|
|
await driver.findContent('.test-right-panel button', 'Change Widget').click();
|
|
|
|
// click the summarize button
|
|
await driver.findContent('.test-wselect-table', 'ChartData').find('.test-wselect-pivot').click();
|
|
|
|
// click save
|
|
await driver.find('.test-wselect-addBtn').click();
|
|
|
|
// wait for server
|
|
await gu.waitForServer();
|
|
|
|
// wait for chart to be changed
|
|
await gu.waitToPass(async () => {
|
|
assert.equal(
|
|
await gu.getActiveSectionTitle(),
|
|
'CHARTDATA [Totals] Chart'
|
|
);
|
|
});
|
|
|
|
// check for error
|
|
await gu.checkForErrors();
|
|
|
|
// undo 1
|
|
await gu.undo(1);
|
|
});
|
|
|
|
it('should sort x-axis values', async function() {
|
|
// Import a small table of numbers to test this.
|
|
await gu.importFileDialog('uploads/ChartData-Sort_Test.csv');
|
|
|
|
await driver.find('.test-modal-confirm').click();
|
|
await gu.waitForServer();
|
|
|
|
// Add a chart of this data, and configure it first to just show X and Y1, Y2 series.
|
|
await gu.addNewSection(/Chart/, /ChartData-Sort_Test/);
|
|
await gu.toggleSidePanel('right', 'open');
|
|
await selectChartType('Line Chart');
|
|
|
|
// Show series X, Y1, Y2, grouped by Group.
|
|
await selectXAxis('X');
|
|
await setSplitSeries('Group');
|
|
await addYAxis('Y1');
|
|
await addYAxis('Y2');
|
|
|
|
const chartDom = await driver.findWait('.test-chart-container', 1000);
|
|
let {data} = await getChartData(chartDom);
|
|
assert.lengthOf(data, 4);
|
|
assert.deepInclude(data[0], {type: 'scatter', name: 'Bar • Y1'});
|
|
assert.deepInclude(data[1], {type: 'scatter', name: 'Bar • Y2'});
|
|
assert.deepInclude(data[2], {type: 'scatter', name: 'Foo • Y1'});
|
|
assert.deepInclude(data[3], {type: 'scatter', name: 'Foo • Y2'});
|
|
assert.deepEqual(data[0].x, [ 1.5, 2.5, 3.5, 4.5, 5.5 ]);
|
|
assert.deepEqual(data[0].y, [ 1.5, 1, 3.5, 2.5, 4 ]);
|
|
assert.deepEqual(data[1].x, [ 1.5, 2.5, 3.5, 4.5, 5.5 ]);
|
|
assert.deepEqual(data[1].y, [ 6.9, 6, 4.9, 5, 7 ]);
|
|
assert.deepEqual(data[2].x, [ 1, 2, 3, 4, 5 ]);
|
|
assert.deepEqual(data[2].y, [ 1.5, 1, 3.5, 2.5, 4 ]);
|
|
assert.deepEqual(data[3].x, [ 1, 2, 3, 4, 5 ]);
|
|
assert.deepEqual(data[3].y, [ 6.9, 6, 4.9, 5, 7 ]);
|
|
|
|
// Now show series ungrouped.
|
|
await setSplitSeries(false);
|
|
|
|
({data} = await getChartData(chartDom));
|
|
assert.lengthOf(data, 2);
|
|
assert.deepInclude(data[0], {type: 'scatter', name: 'Y1'});
|
|
assert.deepInclude(data[1], {type: 'scatter', name: 'Y2'});
|
|
assert.deepEqual(data[0].x, [ 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5 ]);
|
|
assert.deepEqual(data[0].y, [ 1.5, 1.5, 1, 1, 3.5, 3.5, 2.5, 2.5, 4, 4 ]);
|
|
assert.deepEqual(data[1].x, [ 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5 ]);
|
|
assert.deepEqual(data[1].y, [ 6.9, 6.9, 6, 6, 4.9, 4.9, 5, 5, 7, 7 ]);
|
|
});
|
|
|
|
it('should not throw when picking the grouping by column for the x-axis', async function() {
|
|
await checkAxisConfig({xaxis: 'X', yaxis: ['Y1', 'Y2']});
|
|
await setSplitSeries('Group');
|
|
await checkAxisConfig({xaxis: 'X', yaxis: ['Y1', 'Y2'], groupingByColumn: 'Group'});
|
|
await selectXAxis('Group');
|
|
await checkAxisConfig({xaxis: 'Group', yaxis: ['Y1', 'Y2']});
|
|
await gu.checkForErrors();
|
|
await gu.undo(2);
|
|
});
|
|
});
|