mirror of
https://github.com/apache/superset.git
synced 2026-04-25 11:04:48 +00:00
[feat] Feature flag system via config (#5960)
* [feat] Feature flag system via config
Adding a feature flag system that is driven by superset_config.py. This change includes:
- Server side changes to specify a dedicated FEATURE_FLAG dictionary for listing feature flags. E.g.
```
FEATURE_FLAGS = { 'SCOPED_FILTER': true }
```
- Pass the new feature flags to client via bootstrap-data
- Client side changes to inject feature flags into the redux state tree for dashboard, explore view and SqlLab
- Client side refactor/clean up so the feature flags can be properly tested. Also avoid modifying incoming bootstrap data when creating initial state for the redux state tree
- Re-enable tests that were previously disabled for ExploreViewContainer
* Fix lint errors.
* Remove the partial attempt to get reference to src working in tests (so we don't have to write ../../../src and such in tests). This will in a separate PR.
This commit is contained in:
committed by
Beto Dealmeida
parent
414a4bfc6f
commit
604524b671
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { shallow } from 'enzyme';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import Dashboard from '../../../../src/dashboard/containers/Dashboard';
|
||||
import getInitialState from '../../../../src/dashboard/reducers/getInitialState';
|
||||
|
||||
describe('Dashboard Container', () => {
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
let store;
|
||||
let wrapper;
|
||||
|
||||
before(() => {
|
||||
const bootstrapData = {
|
||||
dashboard_data: {
|
||||
slices: [],
|
||||
metadata: {},
|
||||
},
|
||||
common: {
|
||||
feature_flags: {
|
||||
FOO_BAR: true,
|
||||
},
|
||||
conf: {},
|
||||
},
|
||||
};
|
||||
store = mockStore(getInitialState(bootstrapData), {});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<Dashboard />, { context: { store } });
|
||||
});
|
||||
|
||||
it('should set feature flags', () => {
|
||||
expect(wrapper.prop('isFeatureEnabled')('FOO_BAR')).to.equal(true);
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
// this test must be commented out because ChartContainer is now importing files
|
||||
// from visualizations/*.js which are also importing css files which breaks in the testing env.
|
||||
|
||||
// import React from 'react';
|
||||
// import { expect } from 'chai';
|
||||
// // import { shallow } from 'enzyme';
|
||||
|
||||
// import ExploreViewContainer
|
||||
// from '../../../../src/explore/components/ExploreViewContainer';
|
||||
// import QueryAndSaveBtns
|
||||
// from '../../../../src/explore/components/QueryAndSaveBtns';
|
||||
// import ControlPanelsContainer
|
||||
// from '../../../../src/explore/components/ControlPanelsContainer';
|
||||
// import ChartContainer
|
||||
// from '../../../../src/explore/components/ChartContainer';
|
||||
|
||||
// describe('ExploreViewContainer', () => {
|
||||
// it('renders', () => {
|
||||
// expect(
|
||||
// React.isValidElement(<ExploreViewContainer />)
|
||||
// ).to.equal(true);
|
||||
// });
|
||||
|
||||
// it('renders QueryAndSaveButtons', () => {
|
||||
// const wrapper = shallow(<ExploreViewContainer />);
|
||||
// expect(wrapper.find(QueryAndSaveBtns)).to.have.length(1);
|
||||
// });
|
||||
|
||||
// it('renders ControlPanelsContainer', () => {
|
||||
// const wrapper = shallow(<ExploreViewContainer />);
|
||||
// expect(wrapper.find(ControlPanelsContainer)).to.have.length(1);
|
||||
// });
|
||||
|
||||
// it('renders ChartContainer', () => {
|
||||
// const wrapper = shallow(<ExploreViewContainer />);
|
||||
// expect(wrapper.find(ChartContainer)).to.have.length(1);
|
||||
// });
|
||||
// });
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { expect } from 'chai';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import getInitialState from '../../../../src/explore/reducers/getInitialState';
|
||||
import ExploreViewContainer
|
||||
from '../../../../src/explore/components/ExploreViewContainer';
|
||||
import QueryAndSaveBtns
|
||||
from '../../../../src/explore/components/QueryAndSaveBtns';
|
||||
import ControlPanelsContainer
|
||||
from '../../../../src/explore/components/ControlPanelsContainer';
|
||||
import ChartContainer
|
||||
from '../../../../src/explore/components/ExploreChartPanel';
|
||||
|
||||
describe('ExploreViewContainer', () => {
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
let store;
|
||||
let wrapper;
|
||||
|
||||
before(() => {
|
||||
const bootstrapData = {
|
||||
common: {
|
||||
feature_flags: {
|
||||
FOO_BAR: true,
|
||||
},
|
||||
conf: {},
|
||||
},
|
||||
datasource: {
|
||||
columns: [],
|
||||
},
|
||||
form_data: {
|
||||
datasource: {},
|
||||
},
|
||||
};
|
||||
store = mockStore(getInitialState(bootstrapData), {});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<ExploreViewContainer />, {
|
||||
context: { store },
|
||||
disableLifecycleMethods: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set feature flags', () => {
|
||||
expect(wrapper.prop('isFeatureEnabled')('FOO_BAR')).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(
|
||||
React.isValidElement(<ExploreViewContainer />),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders QueryAndSaveButtons', () => {
|
||||
expect(wrapper.dive().find(QueryAndSaveBtns)).to.have.length(1);
|
||||
});
|
||||
|
||||
it('renders ControlPanelsContainer', () => {
|
||||
expect(wrapper.dive().find(ControlPanelsContainer)).to.have.length(1);
|
||||
});
|
||||
|
||||
it('renders ChartContainer', () => {
|
||||
expect(wrapper.dive().find(ChartContainer)).to.have.length(1);
|
||||
});
|
||||
});
|
||||
@@ -8,16 +8,31 @@ import sinon from 'sinon';
|
||||
|
||||
import App from '../../../src/SqlLab/components/App';
|
||||
import TabbedSqlEditors from '../../../src/SqlLab/components/TabbedSqlEditors';
|
||||
import { sqlLabReducer } from '../../../src/SqlLab/reducers';
|
||||
import getInitialState from '../../../src/SqlLab/getInitialState';
|
||||
|
||||
describe('App', () => {
|
||||
describe('SqlLab App', () => {
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
const store = mockStore({ sqlLab: sqlLabReducer(undefined, {}), messageToasts: [] });
|
||||
|
||||
let store;
|
||||
let wrapper;
|
||||
|
||||
before(() => {
|
||||
const bootstrapData = {
|
||||
common: {
|
||||
feature_flags: {
|
||||
FOO_BAR: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
store = mockStore(getInitialState(bootstrapData), {});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<App />, { context: { store } }).dive();
|
||||
wrapper = shallow(<App />, { context: { store } });
|
||||
});
|
||||
|
||||
it('should set feature flags', () => {
|
||||
expect(wrapper.prop('isFeatureEnabled')('FOO_BAR')).to.equal(true);
|
||||
});
|
||||
|
||||
it('is valid', () => {
|
||||
@@ -25,14 +40,16 @@ describe('App', () => {
|
||||
});
|
||||
|
||||
it('should handler resize', () => {
|
||||
sinon.spy(wrapper.instance(), 'getHeight');
|
||||
wrapper.instance().handleResize();
|
||||
expect(wrapper.instance().getHeight.callCount).to.equal(1);
|
||||
wrapper.instance().getHeight.restore();
|
||||
const inner = wrapper.dive();
|
||||
sinon.spy(inner.instance(), 'getHeight');
|
||||
inner.instance().handleResize();
|
||||
expect(inner.instance().getHeight.callCount).to.equal(1);
|
||||
inner.instance().getHeight.restore();
|
||||
});
|
||||
|
||||
it('should render', () => {
|
||||
expect(wrapper.find('.SqlLab')).to.have.length(1);
|
||||
expect(wrapper.find(TabbedSqlEditors)).to.have.length(1);
|
||||
const inner = wrapper.dive();
|
||||
expect(inner.find('.SqlLab')).to.have.length(1);
|
||||
expect(inner.find(TabbedSqlEditors)).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user