diff --git a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx index e80e402ec0..ebd2ebfcb7 100644 --- a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx +++ b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx @@ -25,7 +25,7 @@ import Card from 'src/components/Card'; import Alert from 'src/components/Alert'; import Badge from 'src/components/Badge'; import shortid from 'shortid'; -import { styled, SupersetClient, t, supersetTheme } from '@superset-ui/core'; +import { styled, SupersetClient, t, withTheme } from '@superset-ui/core'; import { Select } from 'src/components'; import { FormLabel } from 'src/components/Form'; import Button from 'src/components/Button'; @@ -97,7 +97,7 @@ const StyledBadge = styled(Badge)` `; const EditLockContainer = styled.div` - font-size: ${supersetTheme.typography.sizes.s}px; + font-size: ${({ theme }) => theme.typography.sizes.s}px; display: flex; align-items: center; a { @@ -828,7 +828,7 @@ class DatasourceEditor extends React.PureComponent { ); } - renderSourceFieldset() { + renderSourceFieldset(theme) { const { datasource } = this.state; return (
@@ -989,13 +989,9 @@ class DatasourceEditor extends React.PureComponent { {this.state.isEditMode ? ( - + ) : ( - + )} {!this.state.isEditMode && ( @@ -1166,6 +1162,8 @@ class DatasourceEditor extends React.PureComponent { const { datasource, activeTabKey } = this.state; const { metrics } = datasource; const sortedMetrics = metrics?.length ? this.sortMetrics(metrics) : []; + const { theme } = this.props; + return ( {this.renderErrors()} @@ -1190,7 +1188,7 @@ class DatasourceEditor extends React.PureComponent { defaultActiveKey={activeTabKey} > - {this.renderSourceFieldset()} + {this.renderSourceFieldset(theme)} { - const mockStore = configureStore([thunk]); - const store = mockStore({}); fetchMock.get(DATASOURCE_ENDPOINT, []); - let wrapper; let el; - let inst; let isFeatureEnabledMock; beforeEach(() => { - el = ; - wrapper = shallow(el).dive(); - inst = wrapper.instance(); - }); - - afterEach(() => { - wrapper.unmount(); + el = ; + render(el, { useRedux: true }); }); it('is valid', () => { @@ -67,15 +48,18 @@ describe('DatasourceEditor', () => { }); it('renders Tabs', () => { - expect(wrapper.find('#table-tabs')).toExist(); + expect(screen.getByTestId('edit-dataset-tabs')).toBeInTheDocument(); }); it('makes an async request', () => new Promise(done => { - wrapper.setState({ activeTabKey: 2 }); - const syncButton = wrapper.find('.sync-from-source'); - expect(syncButton).toHaveLength(1); - syncButton.simulate('click'); + const columnsTab = screen.getByTestId('collection-tab-Columns'); + + userEvent.click(columnsTab); + const syncButton = screen.getByText(/sync columns from source/i); + expect(syncButton).toBeInTheDocument(); + + userEvent.click(syncButton); setTimeout(() => { expect(fetchMock.calls(DATASOURCE_ENDPOINT)).toHaveLength(1); @@ -84,76 +68,77 @@ describe('DatasourceEditor', () => { }, 0); })); - it('to add, remove and modify columns accordingly', () => { - const columns = [ - { - name: 'ds', - type: 'DATETIME', - nullable: true, - default: '', - primary_key: false, - is_dttm: true, - }, - { - name: 'gender', - type: 'VARCHAR(32)', - nullable: true, - default: '', - primary_key: false, - is_dttm: false, - }, - { - name: 'new_column', - type: 'VARCHAR(10)', - nullable: true, - default: '', - primary_key: false, - is_dttm: false, - }, - ]; + // to add, remove and modify columns accordingly + it('can modify columns', async () => { + const columnsTab = screen.getByTestId('collection-tab-Columns'); + userEvent.click(columnsTab); - const numCols = props.datasource.columns.length; - expect(inst.state.databaseColumns).toHaveLength(numCols); - inst.updateColumns(columns); - expect(inst.state.databaseColumns).toEqual( - expect.arrayContaining([ - { - type: 'DATETIME', - description: null, - filterable: false, - verbose_name: null, - is_dttm: true, - expression: '', - groupby: false, - column_name: 'ds', - }, - { - type: 'VARCHAR(32)', - description: null, - filterable: true, - verbose_name: null, - is_dttm: false, - expression: '', - groupby: true, - column_name: 'gender', - }, - expect.objectContaining({ - column_name: 'new_column', - type: 'VARCHAR(10)', - }), - ]), - ); - expect(inst.state.databaseColumns).not.toEqual( - expect.arrayContaining([expect.objectContaining({ name: 'name' })]), + const getToggles = screen.getAllByRole('button', { + name: /toggle expand/i, + }); + userEvent.click(getToggles[0]); + const getTextboxes = screen.getAllByRole('textbox'); + expect(getTextboxes.length).toEqual(5); + + const inputLabel = screen.getByPlaceholderText('Label'); + const inputDescription = screen.getByPlaceholderText('Description'); + const inputDtmFormat = screen.getByPlaceholderText('%Y/%m/%d'); + const inputCertifiedBy = screen.getByPlaceholderText('Certified by'); + const inputCertDetails = screen.getByPlaceholderText( + 'Certification details', ); + + userEvent.type(await inputLabel, 'test_lable'); + userEvent.type(await inputDescription, 'test'); + userEvent.type(await inputDtmFormat, 'test'); + userEvent.type(await inputCertifiedBy, 'test'); + userEvent.type(await inputCertDetails, 'test'); + }); + + it('can delete columns', async () => { + const columnsTab = screen.getByTestId('collection-tab-Columns'); + userEvent.click(columnsTab); + + const getToggles = screen.getAllByRole('button', { + name: /toggle expand/i, + }); + + userEvent.click(getToggles[0]); + screen.logTestingPlaygroundURL(); + const deleteButtons = screen.getAllByRole('button', { + name: /delete item/i, + }); + expect(deleteButtons.length).toEqual(7); + userEvent.click(deleteButtons[0]); + const countRows = screen.getAllByRole('button', { name: /delete item/i }); + expect(countRows.length).toEqual(6); + }); + + it('can add new columns', async () => { + const calcColsTab = screen.getByTestId('collection-tab-Calculated columns'); + userEvent.click(calcColsTab); + const addBtn = screen.getByRole('button', { + name: /add item/i, + }); + expect(addBtn).toBeInTheDocument(); + userEvent.click(addBtn); + const newColumn = screen.getByRole('button', { + name: //i, + }); + expect(newColumn).toBeInTheDocument(); }); it('renders isSqla fields', () => { - wrapper.setState({ activeTabKey: 4 }); - expect(wrapper.state('isSqla')).toBe(true); + const columnsTab = screen.getByRole('tab', { + name: /settings/i, + }); + userEvent.click(columnsTab); + const extraField = screen.getAllByText(/extra/i); + expect(extraField.length).toEqual(2); expect( - wrapper.find(Field).find({ fieldKey: 'fetch_values_predicate' }).exists(), - ).toBe(true); + screen.getByText(/autocomplete query predicate/i), + ).toBeInTheDocument(); + expect(screen.getByText(/template parameters/i)).toBeInTheDocument(); }); describe('enable edit Source tab', () => { @@ -161,7 +146,6 @@ describe('DatasourceEditor', () => { isFeatureEnabledMock = jest .spyOn(featureFlags, 'isFeatureEnabled') .mockImplementation(() => false); - wrapper = shallow(el, { context: { store } }).dive(); }); afterAll(() => { @@ -169,50 +153,49 @@ describe('DatasourceEditor', () => { }); it('Source Tab: edit mode', () => { - wrapper.setState({ activeTabKey: 0, isEditMode: true }); - const sourceTab = wrapper.find(Tabs.TabPane).first(); - expect(sourceTab.find(Radio).first().prop('disabled')).toBe(false); - - const icon = wrapper.find(Icons.LockUnlocked); - expect(icon).toExist(); - - const tableSelector = sourceTab.find(Field).shallow().find(TableSelector); - expect(tableSelector.length).toBe(1); - expect(tableSelector.prop('readOnly')).toBe(false); + const getLockBtn = screen.getByRole('img', { name: /lock-locked/i }); + userEvent.click(getLockBtn); + const physicalRadioBtn = screen.getByRole('radio', { + name: /physical \(table or view\)/i, + }); + const vituralRadioBtn = screen.getByRole('radio', { + name: /virtual \(sql\)/i, + }); + expect(physicalRadioBtn).toBeEnabled(); + expect(vituralRadioBtn).toBeEnabled(); }); it('Source Tab: readOnly mode', () => { - const sourceTab = wrapper.find(Tabs.TabPane).first(); - expect(sourceTab.find(Radio).length).toBe(2); - expect(sourceTab.find(Radio).first().prop('disabled')).toBe(true); - - const icon = wrapper.find(Icons.LockLocked); - expect(icon).toExist(); - icon.parent().simulate('click'); - expect(wrapper.state('isEditMode')).toBe(true); - - const tableSelector = sourceTab.find(Field).shallow().find(TableSelector); - expect(tableSelector.length).toBe(1); - expect(tableSelector.prop('readOnly')).toBe(true); + const getLockBtn = screen.getByRole('img', { name: /lock-locked/i }); + expect(getLockBtn).toBeInTheDocument(); + const physicalRadioBtn = screen.getByRole('radio', { + name: /physical \(table or view\)/i, + }); + const vituralRadioBtn = screen.getByRole('radio', { + name: /virtual \(sql\)/i, + }); + expect(physicalRadioBtn).toBeDisabled(); + expect(vituralRadioBtn).toBeDisabled(); }); }); - it('disable edit Source tab', () => { - // when edit is disabled, show readOnly controls and no padlock - isFeatureEnabledMock = jest - .spyOn(featureFlags, 'isFeatureEnabled') - .mockImplementation(() => true); - wrapper = shallow(el, { context: { store } }).dive(); - wrapper.setState({ activeTabKey: 0 }); + describe('render editor with feature flag false', () => { + beforeAll(() => { + isFeatureEnabledMock = jest + .spyOn(featureFlags, 'isFeatureEnabled') + .mockImplementation(() => true); + }); - const sourceTab = wrapper.find(Tabs.TabPane).first(); - expect(sourceTab.find(Radio).length).toBe(2); - expect(sourceTab.find(Radio).first().prop('disabled')).toBe(true); + beforeEach(() => { + render(el, { useRedux: true }); + }); - const icon = sourceTab.find(Icons.LockLocked); - expect(icon).toHaveLength(0); - - isFeatureEnabledMock.mockRestore(); + it('disable edit Source tab', () => { + expect( + screen.queryByRole('img', { name: /lock-locked/i }), + ).not.toBeInTheDocument(); + isFeatureEnabledMock.mockRestore(); + }); }); }); @@ -227,9 +210,7 @@ describe('DatasourceEditor RTL', () => { /certification details/i, ); expect(certificationDetails.value).toEqual('foo'); - const warningMarkdown = await await screen.findByPlaceholderText( - /certified by/i, - ); + const warningMarkdown = await screen.findByPlaceholderText(/certified by/i); expect(warningMarkdown.value).toEqual('someone'); }); it('properly updates the metric information', async () => {