Skip to content

Commit

Permalink
Add some tests for Choropleth visualization (#4358)
Browse files Browse the repository at this point in the history
  • Loading branch information
kravets-levko authored Nov 14, 2019
1 parent b44fa51 commit aa06b32
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 17 deletions.
1 change: 1 addition & 0 deletions client/app/components/ColorPicker/Input.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export default function Input({ color, presetColors, presetColumns, onChange, on
))}
<div className="color-picker-input">
<TextInput
data-test="ColorPicker.CustomColor"
addonBefore={<Typography.Text type="secondary">#</Typography.Text>}
value={inputValue}
onChange={e => handleInputChange(e.target.value)}
Expand Down
14 changes: 13 additions & 1 deletion client/app/components/ColorPicker/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { toString } from 'lodash';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import tinycolor from 'tinycolor2';
import Popover from 'antd/lib/popover';
import Card from 'antd/lib/card';
Expand All @@ -19,6 +20,7 @@ function validateColor(value, fallback = null) {

export default function ColorPicker({
color, placement, presetColors, presetColumns, triggerSize, interactive, children, onChange,
className, ...props
}) {
const [visible, setVisible] = useState(false);
const [currentColor, setCurrentColor] = useState('');
Expand Down Expand Up @@ -67,6 +69,7 @@ export default function ColorPicker({
overlayStyle={{ '--color-picker-selected-color': currentColor }}
content={(
<Card
data-test="ColorPicker"
className="color-picker-panel"
bordered={false}
title={toString(currentColor).toUpperCase()}
Expand All @@ -90,7 +93,14 @@ export default function ColorPicker({
visible={visible}
onVisibleChange={setVisible}
>
{children || (<Swatch className="color-picker-trigger" color={validateColor(color)} size={triggerSize} />)}
{children || (
<Swatch
className={cx('color-picker-trigger', className)}
color={validateColor(color)}
size={triggerSize}
{...props}
/>
)}
</Popover>
);
}
Expand All @@ -111,6 +121,7 @@ ColorPicker.propTypes = {
interactive: PropTypes.bool,
children: PropTypes.node,
onChange: PropTypes.func,
className: PropTypes.string,
};

ColorPicker.defaultProps = {
Expand All @@ -122,6 +133,7 @@ ColorPicker.defaultProps = {
interactive: false,
children: null,
onChange: () => {},
className: null,
};

ColorPicker.Input = ColorInput;
Expand Down
13 changes: 10 additions & 3 deletions client/app/visualizations/choropleth/Editor/ColorsSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ export default function ColorsSettings({ options, onOptionsChange }) {
<Select
id="choropleth-editor-clustering-mode"
className="w-100"
data-test="Choropleth.Editor.ClusteringMode"
defaultValue={options.clusteringMode}
onChange={clusteringMode => onOptionsChange({ clusteringMode })}
>
<Select.Option value="q">quantile</Select.Option>
<Select.Option value="e">equidistant</Select.Option>
<Select.Option value="k">k-means</Select.Option>
<Select.Option value="q" data-test="Choropleth.Editor.ClusteringMode.q">quantile</Select.Option>
<Select.Option value="e" data-test="Choropleth.Editor.ClusteringMode.e">equidistant</Select.Option>
<Select.Option value="k" data-test="Choropleth.Editor.ClusteringMode.k">k-means</Select.Option>
</Select>
</Grid.Col>
</Grid.Row>
Expand All @@ -38,6 +39,7 @@ export default function ColorsSettings({ options, onOptionsChange }) {
<InputNumber
id="choropleth-editor-color-steps"
className="w-100"
data-test="Choropleth.Editor.ColorSteps"
min={3}
max={11}
defaultValue={options.steps}
Expand All @@ -53,6 +55,7 @@ export default function ColorsSettings({ options, onOptionsChange }) {
<Grid.Col span={12}>
<ColorPicker
id="choropleth-editor-color-min"
data-test="Choropleth.Editor.Colors.Min"
interactive
presetColors={ColorPalette}
placement="topRight"
Expand All @@ -69,6 +72,7 @@ export default function ColorsSettings({ options, onOptionsChange }) {
<Grid.Col span={12}>
<ColorPicker
id="choropleth-editor-color-max"
data-test="Choropleth.Editor.Colors.Max"
interactive
presetColors={ColorPalette}
placement="topRight"
Expand All @@ -85,6 +89,7 @@ export default function ColorsSettings({ options, onOptionsChange }) {
<Grid.Col span={12}>
<ColorPicker
id="choropleth-editor-color-no-value"
data-test="Choropleth.Editor.Colors.NoValue"
interactive
presetColors={ColorPalette}
placement="topRight"
Expand All @@ -101,6 +106,7 @@ export default function ColorsSettings({ options, onOptionsChange }) {
<Grid.Col span={12}>
<ColorPicker
id="choropleth-editor-color-background"
data-test="Choropleth.Editor.Colors.Background"
interactive
presetColors={ColorPalette}
placement="topRight"
Expand All @@ -117,6 +123,7 @@ export default function ColorsSettings({ options, onOptionsChange }) {
<Grid.Col span={12}>
<ColorPicker
id="choropleth-editor-color-borders"
data-test="Choropleth.Editor.Colors.Borders"
interactive
presetColors={ColorPalette}
placement="topRight"
Expand Down
23 changes: 16 additions & 7 deletions client/app/visualizations/choropleth/Editor/FormatSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<Input
id="choropleth-editor-value-format"
className="w-100"
data-test="Choropleth.Editor.ValueFormat"
defaultValue={options.valueFormat}
onChange={event => onOptionsChangeDebounced({ valueFormat: event.target.value })}
/>
Expand All @@ -79,6 +80,7 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<Input
id="choropleth-editor-value-placeholder"
className="w-100"
data-test="Choropleth.Editor.ValuePlaceholder"
defaultValue={options.noValuePlaceholder}
onChange={event => onOptionsChangeDebounced({ noValuePlaceholder: event.target.value })}
/>
Expand All @@ -89,6 +91,7 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<label htmlFor="choropleth-editor-show-legend">
<Checkbox
id="choropleth-editor-show-legend"
data-test="Choropleth.Editor.LegendVisibility"
checked={options.legend.visible}
onChange={event => onOptionsChange({ legend: { visible: event.target.checked } })}
/>
Expand All @@ -102,37 +105,39 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<Select
id="choropleth-editor-legend-position"
className="w-100"
data-test="Choropleth.Editor.LegendPosition"
disabled={!options.legend.visible}
defaultValue={options.legend.position}
onChange={position => onOptionsChange({ legend: { position } })}
>
<Select.Option value="top-left">top / left</Select.Option>
<Select.Option value="top-right">top / right</Select.Option>
<Select.Option value="bottom-left">bottom / left</Select.Option>
<Select.Option value="bottom-right">bottom / right</Select.Option>
<Select.Option value="top-left" data-test="Choropleth.Editor.LegendPosition.TopLeft">top / left</Select.Option>
<Select.Option value="top-right" data-test="Choropleth.Editor.LegendPosition.TopRight">top / right</Select.Option>
<Select.Option value="bottom-left" data-test="Choropleth.Editor.LegendPosition.BottomLeft">bottom / left</Select.Option>
<Select.Option value="bottom-right" data-test="Choropleth.Editor.LegendPosition.BottomRight">bottom / right</Select.Option>
</Select>
</Grid.Col>
<Grid.Col span={12}>
<label htmlFor="choropleth-editor-legend-text-alignment">Legend text alignment</label>
<Radio.Group
id="choropleth-editor-legend-text-alignment"
className="choropleth-visualization-editor-legend-align-text"
data-test="Choropleth.Editor.LegendTextAlignment"
disabled={!options.legend.visible}
defaultValue={options.legend.alignText}
onChange={event => onOptionsChange({ legend: { alignText: event.target.value } })}
>
<Tooltip title="Align left" mouseEnterDelay={0} mouseLeaveDelay={0}>
<Radio.Button value="left">
<Radio.Button value="left" data-test="Choropleth.Editor.LegendTextAlignment.Left">
<Icon type="align-left" />
</Radio.Button>
</Tooltip>
<Tooltip title="Align center" mouseEnterDelay={0} mouseLeaveDelay={0}>
<Radio.Button value="center">
<Radio.Button value="center" data-test="Choropleth.Editor.LegendTextAlignment.Center">
<Icon type="align-center" />
</Radio.Button>
</Tooltip>
<Tooltip title="Align right" mouseEnterDelay={0} mouseLeaveDelay={0}>
<Radio.Button value="right">
<Radio.Button value="right" data-test="Choropleth.Editor.LegendTextAlignment.Right">
<Icon type="align-right" />
</Radio.Button>
</Tooltip>
Expand All @@ -144,6 +149,7 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<label htmlFor="choropleth-editor-show-tooltip">
<Checkbox
id="choropleth-editor-show-tooltip"
data-test="Choropleth.Editor.TooltipEnabled"
checked={options.tooltip.enabled}
onChange={event => onOptionsChange({ tooltip: { enabled: event.target.checked } })}
/>
Expand All @@ -156,6 +162,7 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<Input
id="choropleth-editor-tooltip-template"
className="w-100"
data-test="Choropleth.Editor.TooltipTemplate"
disabled={!options.tooltip.enabled}
defaultValue={options.tooltip.template}
onChange={event => onOptionsChangeDebounced({ tooltip: { template: event.target.value } })}
Expand All @@ -166,6 +173,7 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<label htmlFor="choropleth-editor-show-popup">
<Checkbox
id="choropleth-editor-show-popup"
data-test="Choropleth.Editor.PopupEnabled"
checked={options.popup.enabled}
onChange={event => onOptionsChange({ popup: { enabled: event.target.checked } })}
/>
Expand All @@ -178,6 +186,7 @@ export default function GeneralSettings({ options, onOptionsChange }) {
<Input.TextArea
id="choropleth-editor-popup-template"
className="w-100"
data-test="Choropleth.Editor.PopupTemplate"
disabled={!options.popup.enabled}
rows={4}
defaultValue={options.popup.template}
Expand Down
14 changes: 9 additions & 5 deletions client/app/visualizations/choropleth/Editor/GeneralSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ export default function GeneralSettings({ options, data, onOptionsChange }) {
<Select
id="choropleth-editor-map-type"
className="w-100"
data-test="Choropleth.Editor.MapType"
defaultValue={options.mapType}
onChange={mapType => handleChangeAndInferType({ mapType })}
>
<Select.Option key="countries">Countries</Select.Option>
<Select.Option key="subdiv_japan">Japan/Prefectures</Select.Option>
<Select.Option key="countries" data-test="Choropleth.Editor.MapType.Countries">Countries</Select.Option>
<Select.Option key="subdiv_japan" data-test="Choropleth.Editor.MapType.Japan">Japan/Prefectures</Select.Option>
</Select>
</div>

Expand All @@ -56,11 +57,12 @@ export default function GeneralSettings({ options, data, onOptionsChange }) {
<Select
id="choropleth-editor-key-column"
className="w-100"
data-test="Choropleth.Editor.KeyColumn"
defaultValue={options.countryCodeColumn}
onChange={countryCodeColumn => handleChangeAndInferType({ countryCodeColumn })}
>
{map(data.columns, ({ name }) => (
<Select.Option key={name}>{name}</Select.Option>
<Select.Option key={name} data-test={`Choropleth.Editor.KeyColumn.${name}`}>{name}</Select.Option>
))}
</Select>
</div>
Expand All @@ -70,11 +72,12 @@ export default function GeneralSettings({ options, data, onOptionsChange }) {
<Select
id="choropleth-editor-key-type"
className="w-100"
data-test="Choropleth.Editor.KeyType"
value={options.countryCodeType}
onChange={countryCodeType => onOptionsChange({ countryCodeType })}
>
{map(countryCodeTypes, (name, type) => (
<Select.Option key={type}>{name}</Select.Option>
<Select.Option key={type} data-test={`Choropleth.Editor.KeyType.${type}`}>{name}</Select.Option>
))}
</Select>
</div>
Expand All @@ -84,11 +87,12 @@ export default function GeneralSettings({ options, data, onOptionsChange }) {
<Select
id="choropleth-editor-value-column"
className="w-100"
data-test="Choropleth.Editor.ValueColumn"
defaultValue={options.valueColumn}
onChange={valueColumn => onOptionsChange({ valueColumn })}
>
{map(data.columns, ({ name }) => (
<Select.Option key={name}>{name}</Select.Option>
<Select.Option key={name} data-test={`Choropleth.Editor.ValueColumn.${name}`}>{name}</Select.Option>
))}
</Select>
</div>
Expand Down
86 changes: 86 additions & 0 deletions client/cypress/integration/visualizations/choropleth_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* global cy, Cypress */

import { createQuery } from '../../support/redash-api';

const SQL = `
SELECT 'AR' AS "code", 'Argentina' AS "name", 37.62 AS "value" UNION ALL
SELECT 'AU' AS "code", 'Australia' AS "name", 37.62 AS "value" UNION ALL
SELECT 'AT' AS "code", 'Austria' AS "name", 42.62 AS "value" UNION ALL
SELECT 'BE' AS "code", 'Belgium' AS "name", 37.62 AS "value" UNION ALL
SELECT 'BR' AS "code", 'Brazil' AS "name", 190.10 AS "value" UNION ALL
SELECT 'CA' AS "code", 'Canada' AS "name", 303.96 AS "value" UNION ALL
SELECT 'CL' AS "code", 'Chile' AS "name", 46.62 AS "value" UNION ALL
SELECT 'CZ' AS "code", 'Czech Republic' AS "name", 90.24 AS "value" UNION ALL
SELECT 'DK' AS "code", 'Denmark' AS "name", 37.62 AS "value" UNION ALL
SELECT 'FI' AS "code", 'Finland' AS "name", 41.62 AS "value" UNION ALL
SELECT 'FR' AS "code", 'France' AS "name", 195.10 AS "value" UNION ALL
SELECT 'DE' AS "code", 'Germany' AS "name", 156.48 AS "value" UNION ALL
SELECT 'HU' AS "code", 'Hungary' AS "name", 45.62 AS "value" UNION ALL
SELECT 'IN' AS "code", 'India' AS "name", 75.26 AS "value" UNION ALL
SELECT 'IE' AS "code", 'Ireland' AS "name", 45.62 AS "value" UNION ALL
SELECT 'IT' AS "code", 'Italy' AS "name", 37.62 AS "value" UNION ALL
SELECT 'NL' AS "code", 'Netherlands' AS "name", 40.62 AS "value" UNION ALL
SELECT 'NO' AS "code", 'Norway' AS "name", 39.62 AS "value" UNION ALL
SELECT 'PL' AS "code", 'Poland' AS "name", 37.62 AS "value" UNION ALL
SELECT 'PT' AS "code", 'Portugal' AS "name", 77.24 AS "value" UNION ALL
SELECT 'ES' AS "code", 'Spain' AS "name", 37.62 AS "value" UNION ALL
SELECT 'SE' AS "code", 'Sweden' AS "name", 38.62 AS "value" UNION ALL
SELECT 'US' AS "code", 'USA' AS "name", 523.06 AS "value" UNION ALL
SELECT 'GB' AS "code", 'United Kingdom' AS "name", 112.86 AS "value"
`;

describe('Choropleth', () => {
const viewportWidth = Cypress.config('viewportWidth');

beforeEach(() => {
cy.login();
createQuery({ query: SQL }).then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId('ExecuteButton').click();
});
});

it('creates visualization', () => {
cy.clickThrough(`
NewVisualization
VisualizationType
VisualizationType.CHOROPLETH
`);

cy.clickThrough(`
Choropleth.EditorTabs.General
Choropleth.Editor.MapType
Choropleth.Editor.MapType.Countries
Choropleth.Editor.KeyColumn
Choropleth.Editor.KeyColumn.name
Choropleth.Editor.KeyType
Choropleth.Editor.KeyType.name
Choropleth.Editor.ValueColumn
Choropleth.Editor.ValueColumn.value
`);

cy.clickThrough('Choropleth.EditorTabs.Colors');
cy.clickThrough('Choropleth.Editor.Colors.Min');
cy.fillInputs({ 'ColorPicker.CustomColor': 'yellow{enter}' });
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
cy.clickThrough('Choropleth.Editor.Colors.Max');
cy.fillInputs({ 'ColorPicker.CustomColor': 'red{enter}' });
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting
cy.clickThrough('Choropleth.Editor.Colors.Borders');
cy.fillInputs({ 'ColorPicker.CustomColor': 'black{enter}' });
cy.wait(100); // eslint-disable-line cypress/no-unnecessary-waiting

cy.clickThrough(`
Choropleth.EditorTabs.Format
Choropleth.Editor.LegendPosition
Choropleth.Editor.LegendPosition.TopRight
`);

cy.getByTestId('Choropleth.Editor.LegendTextAlignment.Left').check({ force: true });

// Wait for proper initialization of visualization
cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting
cy.getByTestId('VisualizationPreview').find('.map-visualization-container.leaflet-container').should('exist');
cy.percySnapshot('Visualizations - Choropleth', { widths: [viewportWidth] });
});
});
2 changes: 1 addition & 1 deletion client/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Cypress.Commands.add('clickThrough', (...args) => {

Cypress.Commands.add('fillInputs', (elements, { wait = 0 } = {}) => {
each(elements, (value, testId) => {
cy.getByTestId(testId).clear().type(value);
cy.getByTestId(testId).filter(':visible').clear().type(value);
if (wait > 0) {
cy.wait(wait); // eslint-disable-line cypress/no-unnecessary-waiting
}
Expand Down

0 comments on commit aa06b32

Please sign in to comment.