This is an automated email from the ASF dual-hosted git repository.
yjc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push:
new 700429f fix: chart validation error not cleared on control value
update (#10224)
700429f is described below
commit 700429f431d20b624e886cf554b2b493793448be
Author: Jesse Yang <[email protected]>
AuthorDate: Wed Jul 1 18:32:27 2020 -0700
fix: chart validation error not cleared on control value update (#10224)
---
.../integration/explore/AdhocFilters.test.ts | 72 ++++++
.../integration/explore/AdhocMetrics.test.ts | 131 +++++++++++
.../cypress/integration/explore/advanced.test.ts | 97 ++++++++
.../cypress/integration/explore/control.test.js | 249 ---------------------
.../explore/visualizations/line.test.js | 19 +-
.../{table.test.js => table.test.ts} | 17 +-
.../cypress-base/cypress/support/index.d.ts | 14 +-
.../cypress-base/cypress/support/index.ts | 18 +-
.../spec/javascripts/explore/controlUtils_spec.jsx | 10 +-
.../explore/components/ControlPanelsContainer.jsx | 7 -
superset-frontend/src/explore/controlUtils.js | 3 +-
.../src/explore/reducers/exploreReducer.js | 20 +-
12 files changed, 362 insertions(+), 295 deletions(-)
diff --git
a/superset-frontend/cypress-base/cypress/integration/explore/AdhocFilters.test.ts
b/superset-frontend/cypress-base/cypress/integration/explore/AdhocFilters.test.ts
new file mode 100644
index 0000000..dc12961
--- /dev/null
+++
b/superset-frontend/cypress-base/cypress/integration/explore/AdhocFilters.test.ts
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+describe('AdhocFilters', () => {
+ beforeEach(() => {
+ cy.login();
+ cy.server();
+ cy.route('GET', '/superset/explore_json/**').as('getJson');
+ cy.route('POST', '/superset/explore_json/**').as('postJson');
+ });
+
+ it('Set simple adhoc filter', () => {
+ cy.visitChartByName('Num Births Trend');
+ cy.verifySliceSuccess({ waitAlias: '@postJson' });
+
+ cy.get('[data-test=adhoc_filters]').within(() => {
+ cy.get('.Select__control').click();
+ cy.get('input[type=text]').type('name{enter}');
+ });
+ cy.get('#filter-edit-popover').within(() => {
+ cy.get('[data-test=adhoc-filter-simple-value]').within(() => {
+ cy.get('.Select__control').click();
+ cy.get('input[type=text]').type('Any{enter}');
+ });
+ cy.get('button').contains('Save').click();
+ });
+
+ cy.get('button.query').click();
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ chartSelector: 'svg',
+ });
+ });
+
+ it('Set custom adhoc filter', () => {
+ cy.visitChartByName('Num Births Trend');
+ cy.verifySliceSuccess({ waitAlias: '@postJson' });
+
+ cy.get('[data-test=adhoc_filters]').within(() => {
+ cy.get('.Select__control').click();
+ cy.get('input[type=text]').type('name{enter}');
+ });
+
+ cy.get('#filter-edit-popover').within(() => {
+ cy.get('#adhoc-filter-edit-tabs-tab-SQL').click();
+ cy.get('.ace_content').click();
+ cy.get('.ace_text-input').type("'Amy' OR name = 'Bob'");
+ cy.get('button').contains('Save').click();
+ });
+
+ cy.get('button.query').click();
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ chartSelector: 'svg',
+ });
+ });
+});
diff --git
a/superset-frontend/cypress-base/cypress/integration/explore/AdhocMetrics.test.ts
b/superset-frontend/cypress-base/cypress/integration/explore/AdhocMetrics.test.ts
new file mode 100644
index 0000000..ef7dc9b
--- /dev/null
+++
b/superset-frontend/cypress-base/cypress/integration/explore/AdhocMetrics.test.ts
@@ -0,0 +1,131 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+describe('AdhocMetrics', () => {
+ beforeEach(() => {
+ cy.login();
+ cy.server();
+ cy.route('GET', '/superset/explore_json/**').as('getJson');
+ cy.route('POST', '/superset/explore_json/**').as('postJson');
+ });
+
+ it('Clear metric and set simple adhoc metric', () => {
+ const metric = 'sum(sum_girls)';
+ const metricName = 'Girl Births';
+
+ cy.visitChartByName('Num Births Trend');
+ cy.verifySliceSuccess({ waitAlias: '@postJson' });
+
+ cy.get('[data-test=metrics]').within(() => {
+ cy.get('.Select__clear-indicator').click();
+ cy.get('.Select__control input').type('sum_girls');
+ cy.get('.Select__option--is-focused').trigger('mousedown').click();
+ });
+
+ cy.get('#metrics-edit-popover').within(() => {
+ cy.get('.popover-title').within(() => {
+ cy.get('span').click();
+ cy.get('input').type(metricName);
+ });
+ cy.get('button').contains('Save').click();
+ });
+ cy.get('.Select__multi-value__label').contains(metricName);
+
+ cy.get('button.query').click();
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ querySubstring: `${metric} AS "${metricName}"`, // SQL statement
+ chartSelector: 'svg',
+ });
+ });
+
+ it('Switch from simple to custom sql', () => {
+ cy.visitChartByName('Num Births Trend');
+ cy.verifySliceSuccess({ waitAlias: '@postJson' });
+
+ // select column "num"
+ cy.get('[data-test=metrics]').within(() => {
+ cy.get('.Select__clear-indicator').click();
+ cy.get('.Select__control').click();
+ cy.get('.Select__control input').type('num');
+ cy.get('.option-label').contains(/^num$/).click();
+ });
+
+ // add custom SQL
+ cy.get('#metrics-edit-popover').within(() => {
+ cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();
+ cy.get('.ace_content').click();
+ cy.get('.ace_text-input').type('/COUNT(DISTINCT name)', { force: true });
+ cy.get('button').contains('Save').click();
+ });
+
+ cy.get('button.query').click();
+
+ const metric = 'SUM(num)/COUNT(DISTINCT name)';
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ querySubstring: `${metric} AS "${metric}"`,
+ chartSelector: 'svg',
+ });
+ });
+
+ it('Switch from custom sql tabs to simple', () => {
+ cy.get('[data-test=metrics]').within(() => {
+ cy.get('.Select__dropdown-indicator').click();
+ cy.get('input[type=text]').type('sum_girls{enter}');
+ });
+
+ cy.get('#metrics-edit-popover').within(() => {
+ cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();
+ cy.get('.ace_identifier').contains('sum_girls');
+ cy.get('.ace_content').click();
+ cy.get('.ace_text-input').type('{selectall}{backspace}SUM(num)');
+ cy.get('#adhoc-metric-edit-tabs-tab-SIMPLE').click();
+ cy.get('.Select__single-value').contains(/^num$/);
+ cy.get('button').contains('Save').click();
+ });
+
+ cy.get('button.query').click();
+
+ const metric = 'SUM(num)';
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ querySubstring: `${metric} AS "${metric}"`,
+ chartSelector: 'svg',
+ });
+ });
+
+ it('Typing starts with aggregate function name', () => {
+ // select column "num"
+ cy.get('[data-test=metrics]').within(() => {
+ cy.get('.Select__dropdown-indicator').click();
+ cy.get('.Select__control input[type=text]').type('avg(');
+ cy.get('.Select__option').contains('ds');
+ cy.get('.Select__option').contains('name');
+ cy.get('.Select__option').contains('sum_boys').click();
+ });
+
+ const metric = 'AVG(sum_boys)';
+ cy.get('button.query').click();
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ querySubstring: `${metric} AS "${metric}"`,
+ chartSelector: 'svg',
+ });
+ });
+});
diff --git
a/superset-frontend/cypress-base/cypress/integration/explore/advanced.test.ts
b/superset-frontend/cypress-base/cypress/integration/explore/advanced.test.ts
new file mode 100644
index 0000000..4a21f00
--- /dev/null
+++
b/superset-frontend/cypress-base/cypress/integration/explore/advanced.test.ts
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+describe('Advanced analytics', () => {
+ beforeEach(() => {
+ cy.login();
+ cy.server();
+ cy.route('GET', '/superset/explore_json/**').as('getJson');
+ cy.route('POST', '/superset/explore_json/**').as('postJson');
+ });
+
+ it('Create custom time compare', () => {
+ cy.visitChartByName('Num Births Trend');
+ cy.verifySliceSuccess({ waitAlias: '@postJson' });
+
+ cy.get('.panel-title').contains('Advanced Analytics').click();
+
+ cy.get('[data-test=time_compare]').within(() => {
+ cy.get('.Select__control').click();
+ cy.get('input[type=text]').type('28 days{enter}');
+
+ cy.get('.Select__control').click();
+ cy.get('input[type=text]').type('364 days{enter}');
+ cy.get('.Select__multi-value__label').contains('364 days');
+ });
+
+ cy.get('button.query').click();
+ cy.wait('@postJson');
+ cy.reload();
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ chartSelector: 'svg',
+ });
+
+ cy.get('[data-test=time_compare]').within(() => {
+ cy.get('.Select__multi-value__label').contains('364 days');
+ cy.get('.Select__multi-value__label').contains('28 days');
+ });
+ });
+});
+
+describe('Annotations', () => {
+ beforeEach(() => {
+ cy.login();
+ cy.server();
+ cy.route('GET', '/superset/explore_json/**').as('getJson');
+ cy.route('POST', '/superset/explore_json/**').as('postJson');
+ });
+
+ it('Create formula annotation y-axis goal line', () => {
+ cy.visitChartByName('Num Births Trend');
+ cy.verifySliceSuccess({ waitAlias: '@postJson' });
+
+ cy.get('[data-test=annotation_layers]').within(() => {
+ cy.get('button').click();
+ });
+
+ cy.get('.popover-content').within(() => {
+ cy.get('[data-test=annotation-layer-name-header]')
+ .siblings()
+ .first()
+ .within(() => {
+ cy.get('input').type('Goal line');
+ });
+ cy.get('[data-test=annotation-layer-value-header]')
+ .siblings()
+ .first()
+ .within(() => {
+ cy.get('input').type('y=1400000');
+ });
+ cy.get('button').contains('OK').click();
+ });
+
+ cy.get('button.query').click();
+ cy.verifySliceSuccess({
+ waitAlias: '@postJson',
+ chartSelector: 'svg',
+ });
+
+ cy.get('.nv-legend-text').should('have.length', 2);
+ });
+});
diff --git
a/superset-frontend/cypress-base/cypress/integration/explore/control.test.js
b/superset-frontend/cypress-base/cypress/integration/explore/control.test.js
index b012a13..e2da3a6 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/control.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/explore/control.test.js
@@ -40,255 +40,6 @@ describe('Groupby', () => {
});
});
-describe('AdhocMetrics', () => {
- beforeEach(() => {
- cy.login();
- cy.server();
- cy.route('GET', '/superset/explore_json/**').as('getJson');
- cy.route('POST', '/superset/explore_json/**').as('postJson');
- });
-
- it('Clear metric and set simple adhoc metric', () => {
- const metric = 'sum(sum_girls)';
- const metricName = 'Girl Births';
-
- cy.visitChartByName('Num Births Trend');
- cy.verifySliceSuccess({ waitAlias: '@postJson' });
-
- cy.get('[data-test=metrics]').within(() => {
- cy.get('.Select__clear-indicator').click();
- cy.get('.Select__control input').type('sum_girls');
- cy.get('.Select__option--is-focused').trigger('mousedown').click();
- });
-
- cy.get('#metrics-edit-popover').within(() => {
- cy.get('.popover-title').within(() => {
- cy.get('span').click();
- cy.get('input').type(metricName);
- });
- cy.get('button').contains('Save').click();
- });
- cy.get('.Select__multi-value__label').contains(metricName);
-
- cy.get('button.query').click();
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- querySubstring: `${metric} AS "${metricName}"`, // SQL statement
- chartSelector: 'svg',
- });
- });
-
- it('Switch from simple to custom sql', () => {
- cy.visitChartByName('Num Births Trend');
- cy.verifySliceSuccess({ waitAlias: '@postJson' });
-
- // select column "num"
- cy.get('[data-test=metrics]').within(() => {
- cy.get('.Select__clear-indicator').click();
- cy.get('.Select__control').click();
- cy.get('.Select__control input').type('num');
- cy.get('.option-label').contains(/^num$/).click();
- });
-
- // add custom SQL
- cy.get('#metrics-edit-popover').within(() => {
- cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();
- cy.get('.ace_content').click();
- cy.get('.ace_text-input').type('/COUNT(DISTINCT name)', { force: true });
- cy.get('button').contains('Save').click();
- });
-
- cy.get('button.query').click();
-
- const metric = 'SUM(num)/COUNT(DISTINCT name)';
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- querySubstring: `${metric} AS "${metric}"`,
- chartSelector: 'svg',
- });
- });
-
- it('Switch from custom sql tabs to simple', () => {
- cy.get('[data-test=metrics]').within(() => {
- cy.get('.Select__dropdown-indicator').click();
- cy.get('input[type=text]').type('sum_girls{enter}');
- });
-
- cy.get('#metrics-edit-popover').within(() => {
- cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();
- cy.get('.ace_identifier').contains('sum_girls');
- cy.get('.ace_content').click();
- cy.get('.ace_text-input').type('{selectall}{backspace}SUM(num)');
- cy.get('#adhoc-metric-edit-tabs-tab-SIMPLE').click();
- cy.get('.Select__single-value').contains(/^num$/);
- cy.get('button').contains('Save').click();
- });
-
- cy.get('button.query').click();
-
- const metric = 'SUM(num)';
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- querySubstring: `${metric} AS "${metric}"`,
- chartSelector: 'svg',
- });
- });
-
- it('Typing starts with aggregate function name', () => {
- // select column "num"
- cy.get('[data-test=metrics]').within(() => {
- cy.get('.Select__dropdown-indicator').click();
- cy.get('.Select__control input[type=text]').type('avg(');
- cy.get('.Select__option').contains('ds');
- cy.get('.Select__option').contains('name');
- cy.get('.Select__option').contains('sum_boys').click();
- });
-
- const metric = 'AVG(sum_boys)';
- cy.get('button.query').click();
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- querySubstring: `${metric} AS "${metric}"`,
- chartSelector: 'svg',
- });
- });
-});
-
-describe('AdhocFilters', () => {
- beforeEach(() => {
- cy.login();
- cy.server();
- cy.route('GET', '/superset/explore_json/**').as('getJson');
- cy.route('POST', '/superset/explore_json/**').as('postJson');
- });
-
- it('Set simple adhoc filter', () => {
- cy.visitChartByName('Num Births Trend');
- cy.verifySliceSuccess({ waitAlias: '@postJson' });
-
- cy.get('[data-test=adhoc_filters]').within(() => {
- cy.get('.Select__control').click();
- cy.get('input[type=text]').type('name{enter}');
- });
- cy.get('#filter-edit-popover').within(() => {
- cy.get('[data-test=adhoc-filter-simple-value]').within(() => {
- cy.get('.Select__control').click();
- cy.get('input[type=text]').type('Any{enter}');
- });
- cy.get('button').contains('Save').click();
- });
-
- cy.get('button.query').click();
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- chartSelector: 'svg',
- });
- });
-
- it('Set custom adhoc filter', () => {
- cy.visitChartByName('Num Births Trend');
- cy.verifySliceSuccess({ waitAlias: '@postJson' });
-
- cy.get('[data-test=adhoc_filters]').within(() => {
- cy.get('.Select__control').click();
- cy.get('input[type=text]').type('name{enter}');
- });
-
- cy.get('#filter-edit-popover').within(() => {
- cy.get('#adhoc-filter-edit-tabs-tab-SQL').click();
- cy.get('.ace_content').click();
- cy.get('.ace_text-input').type("'Amy' OR name = 'Bob'");
- cy.get('button').contains('Save').click();
- });
-
- cy.get('button.query').click();
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- chartSelector: 'svg',
- });
- });
-});
-
-describe('Advanced analytics', () => {
- beforeEach(() => {
- cy.login();
- cy.server();
- cy.route('GET', '/superset/explore_json/**').as('getJson');
- cy.route('POST', '/superset/explore_json/**').as('postJson');
- });
-
- it('Create custom time compare', () => {
- cy.visitChartByName('Num Births Trend');
- cy.verifySliceSuccess({ waitAlias: '@postJson' });
-
- cy.get('.panel-title').contains('Advanced Analytics').click();
-
- cy.get('[data-test=time_compare]').within(() => {
- cy.get('.Select__control').click();
- cy.get('input[type=text]').type('28 days{enter}');
-
- cy.get('.Select__control').click();
- cy.get('input[type=text]').type('364 days{enter}');
- cy.get('.Select__multi-value__label').contains('364 days');
- });
-
- cy.get('button.query').click();
- cy.wait('@postJson');
- cy.reload();
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- chartSelector: 'svg',
- });
-
- cy.get('[data-test=time_compare]').within(() => {
- cy.get('.Select__multi-value__label').contains('364 days');
- cy.get('.Select__multi-value__label').contains('28 days');
- });
- });
-});
-
-describe('Annotations', () => {
- beforeEach(() => {
- cy.login();
- cy.server();
- cy.route('GET', '/superset/explore_json/**').as('getJson');
- cy.route('POST', '/superset/explore_json/**').as('postJson');
- });
-
- it('Create formula annotation y-axis goal line', () => {
- cy.visitChartByName('Num Births Trend');
- cy.verifySliceSuccess({ waitAlias: '@postJson' });
-
- cy.get('[data-test=annotation_layers]').within(() => {
- cy.get('button').click();
- });
-
- cy.get('.popover-content').within(() => {
- cy.get('[data-test=annotation-layer-name-header]')
- .siblings()
- .first()
- .within(() => {
- cy.get('input').type('Goal line');
- });
- cy.get('[data-test=annotation-layer-value-header]')
- .siblings()
- .first()
- .within(() => {
- cy.get('input').type('y=1400000');
- });
- cy.get('button').contains('OK').click();
- });
-
- cy.get('button.query').click();
- cy.verifySliceSuccess({
- waitAlias: '@postJson',
- chartSelector: 'svg',
- });
-
- cy.get('.nv-legend-text').should('have.length', 2);
- });
-});
-
describe('Time range filter', () => {
beforeEach(() => {
cy.login();
diff --git
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.js
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.js
index 5314a01..229c7f4 100644
---
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.js
+++
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.js
@@ -27,9 +27,26 @@ describe('Visualization > Line', () => {
cy.route('POST', '/superset/explore_json/**').as('getJson');
});
+ it('should show validator error when no metric', () => {
+ const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
+ cy.visitChartByParams(JSON.stringify(formData));
+ cy.get('.alert-warning').contains(`"Metrics" cannot be empty`);
+ });
+
+ it('should not show validator error when metric added', () => {
+ const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
+ cy.visitChartByParams(JSON.stringify(formData));
+ cy.get('.alert-warning').contains(`"Metrics" cannot be empty`);
+ cy.get('.text-danger').contains('Metrics');
+ cy.get('.metrics-select .Select__input input:eq(0)')
+ .focus()
+ .type('SUM(num){enter}');
+ cy.get('.text-danger').should('not.exist');
+ cy.get('.alert-warning').should('not.exist');
+ });
+
it('should work with adhoc metric', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
-
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
diff --git
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.js
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.ts
similarity index 95%
rename from
superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.js
rename to
superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.ts
index e95fb48..b7db68e 100644
---
a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.js
+++
b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/table.test.ts
@@ -30,9 +30,7 @@ describe('Visualization > Table', () => {
});
it('Test table with adhoc metric', () => {
- const formData = { ...VIZ_DEFAULTS, metrics: NUM_METRIC };
-
- cy.visitChartByParams(JSON.stringify(formData));
+ cy.visitChartByParams({ ...VIZ_DEFAULTS, metrics: NUM_METRIC });
cy.verifySliceSuccess({
waitAlias: '@getJson',
querySubstring: NUM_METRIC.label,
@@ -41,16 +39,14 @@ describe('Visualization > Table', () => {
});
it('Test table with groupby', () => {
- const formData = {
+ cy.visitChartByParams({
...VIZ_DEFAULTS,
metrics: NUM_METRIC,
groupby: ['name'],
- };
-
- cy.visitChartByParams(JSON.stringify(formData));
+ });
cy.verifySliceSuccess({
waitAlias: '@getJson',
- querySubstring: formData.groupby[0],
+ querySubstring: /groupby.*name/,
chartSelector: 'table',
});
});
@@ -62,7 +58,6 @@ describe('Visualization > Table', () => {
metrics: [],
groupby: ['name'],
};
-
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' });
});
@@ -74,23 +69,19 @@ describe('Visualization > Table', () => {
groupby: ['name'],
order_desc: true,
};
-
cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' });
});
it('Test table with groupby and limit', () => {
const limit = 10;
-
const formData = {
...VIZ_DEFAULTS,
metrics: NUM_METRIC,
groupby: ['name'],
row_limit: limit,
};
-
cy.visitChartByParams(JSON.stringify(formData));
-
cy.wait('@getJson').then(async xhr => {
cy.verifyResponseCodes(xhr);
cy.verifySliceContainer('table');
diff --git a/superset-frontend/cypress-base/cypress/support/index.d.ts
b/superset-frontend/cypress-base/cypress/support/index.d.ts
index 80a936e..0c21db7 100644
--- a/superset-frontend/cypress-base/cypress/support/index.d.ts
+++ b/superset-frontend/cypress-base/cypress/support/index.d.ts
@@ -30,6 +30,10 @@ declare namespace Cypress {
*/
login(): void;
+ visitChartByParams(params: string | object): cy;
+ visitChartByName(name: string): cy;
+ visitChartById(id: number): cy;
+
/**
* Verify a waitXHR response and parse response JSON.
*/
@@ -46,14 +50,10 @@ declare namespace Cypress {
/**
* Verify slice successfully loaded.
*/
- verifySliceSuccess({
- waitAlias,
- querySubString,
- chartSelector,
- }: {
+ verifySliceSuccess(options: {
waitAlias: string;
- querySubString: string;
- chartSelector: JQuery.Selector;
+ querySubstring?: string | RegExp;
+ chartSelector?: JQuery.Selector;
}): cy;
}
}
diff --git a/superset-frontend/cypress-base/cypress/support/index.ts
b/superset-frontend/cypress-base/cypress/support/index.ts
index ad70b73..759539c 100644
--- a/superset-frontend/cypress-base/cypress/support/index.ts
+++ b/superset-frontend/cypress-base/cypress/support/index.ts
@@ -42,7 +42,9 @@ Cypress.Commands.add('visitChartById', chartId => {
});
Cypress.Commands.add('visitChartByParams', params => {
- return cy.visit(`${BASE_EXPLORE_URL}${params}`);
+ const queryString =
+ typeof params === 'string' ? params : JSON.stringify(params);
+ return cy.visit(`${BASE_EXPLORE_URL}${queryString}`);
});
Cypress.Commands.add('verifyResponseCodes', (xhr: XMLHttpRequest, callback) =>
{
@@ -78,17 +80,21 @@ Cypress.Commands.add(
chartSelector,
}: {
waitAlias: string;
- querySubstring: string;
chartSelector: JQuery.Selector;
+ querySubstring?: string | RegExp;
}) => {
cy.wait(waitAlias).then(xhr => {
cy.verifySliceContainer(chartSelector);
cy.verifyResponseCodes(xhr, responseBody => {
if (querySubstring) {
- type QueryResponse = { query: string };
- expect(
- responseBody && (responseBody as QueryResponse).query,
- ).contains(querySubstring);
+ const query = responseBody
+ ? (responseBody as { query: string }).query
+ : '';
+ if (querySubstring instanceof RegExp) {
+ expect(query).to.match(querySubstring);
+ } else {
+ expect(query).to.contain(querySubstring);
+ }
}
});
});
diff --git a/superset-frontend/spec/javascripts/explore/controlUtils_spec.jsx
b/superset-frontend/spec/javascripts/explore/controlUtils_spec.jsx
index 2b18063..946517d 100644
--- a/superset-frontend/spec/javascripts/explore/controlUtils_spec.jsx
+++ b/superset-frontend/spec/javascripts/explore/controlUtils_spec.jsx
@@ -189,15 +189,15 @@ describe('controlUtils', () => {
it('removes the mapStateToProps key from the object', () => {
let control = getControlConfig('all_columns', 'table');
control = applyMapStateToPropsToControl(control, state);
- expect(control.mapStateToProps).toBe(undefined);
+ expect(control.mapStateToProps[0]).toBe(undefined);
});
});
describe('getControlState', () => {
- it('to be function free', () => {
- const control = getControlState('all_columns', 'table', state, ['a']);
- expect(control.mapStateToProps).toBe(undefined);
- expect(control.validators).toBe(undefined);
+ it('to still have the functions', () => {
+ const control = getControlState('metrics', 'table', state, ['a']);
+ expect(typeof control.mapStateToProps).toBe('function');
+ expect(typeof control.validators[0]).toBe('function');
});
it('to fix multi with non-array values', () => {
diff --git
a/superset-frontend/src/explore/components/ControlPanelsContainer.jsx
b/superset-frontend/src/explore/components/ControlPanelsContainer.jsx
index 22b948f..77aa5a7 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.jsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.jsx
@@ -76,13 +76,6 @@ class ControlPanelsContainer extends React.Component {
// apply current value in formData
value: formData[name],
};
- const { mapStateToProps: mapFn } = controlData;
- if (mapFn) {
- Object.assign(
- controlData,
- mapFn(exploreState, controlData, actions) || {},
- );
- }
const {
validationErrors,
provideFormDataToProps,
diff --git a/superset-frontend/src/explore/controlUtils.js
b/superset-frontend/src/explore/controlUtils.js
index daa395e..41c5de0 100644
--- a/superset-frontend/src/explore/controlUtils.js
+++ b/superset-frontend/src/explore/controlUtils.js
@@ -45,7 +45,6 @@ export function validateControl(control, processedState) {
validationErrors.push(v);
}
});
- delete validatedControl.validators;
return { ...validatedControl, validationErrors };
}
return control;
@@ -95,7 +94,6 @@ export function applyMapStateToPropsToControl(control, state)
{
if (state) {
Object.assign(appliedControl, control.mapStateToProps(state, control));
}
- delete appliedControl.mapStateToProps;
return appliedControl;
}
return control;
@@ -141,6 +139,7 @@ export function
getControlStateFromControlConfig(controlConfig, state, value) {
// If a choice control went from multi=false to true, wrap value in array
const controlValue =
controlConfig.multi && value && !Array.isArray(value) ? [value] : value;
+
controlState.value =
typeof controlValue === 'undefined' ? controlState.default : controlValue;
diff --git a/superset-frontend/src/explore/reducers/exploreReducer.js
b/superset-frontend/src/explore/reducers/exploreReducer.js
index 5e1df2c..83cd62e 100644
--- a/superset-frontend/src/explore/reducers/exploreReducer.js
+++ b/superset-frontend/src/explore/reducers/exploreReducer.js
@@ -99,24 +99,34 @@ export default function exploreReducer(state = {}, action) {
},
[actions.SET_FIELD_VALUE]() {
const new_form_data = state.form_data;
- new_form_data[action.controlName] = action.value;
+ const { controlName, value, validationErrors } = action;
+ new_form_data[controlName] = value;
- // These errors are reported from the Control components
- let errors = action.validationErrors || [];
const vizType = new_form_data.viz_type;
+
// Use the processed control config (with overrides and everything)
// if `controlName` does not existing in current controls,
const controlConfig =
state.controls[action.controlName] ||
getControlConfig(action.controlName, vizType) ||
{};
+
+ // will call validators again
const control = {
...getControlStateFromControlConfig(controlConfig, state,
action.value),
};
- // These errors are based on control config `validators`
- errors = errors.concat(control.validationErrors || []);
+ // combine newly detected errors with errors from `onChange` event of
+ // each control component (passed via reducer action).
+ const errors = control.validationErrors || [];
+ (validationErrors || []).forEach(err => {
+ // skip duplicated errors
+ if (!errors.includes(err)) {
+ errors.push(err);
+ }
+ });
const hasErrors = errors && errors.length > 0;
+
return {
...state,
form_data: new_form_data,