From 3c59a8683411b3a26fcb23105c21e26e7da9ad2e Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Wed, 17 Aug 2022 18:32:07 -0700 Subject: [PATCH 1/3] [fab] Add extra to list of columns (#21118) --- superset/connectors/sqla/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/connectors/sqla/views.py b/superset/connectors/sqla/views.py index cee0a07b54bb7..3558ab9f19fb3 100644 --- a/superset/connectors/sqla/views.py +++ b/superset/connectors/sqla/views.py @@ -209,7 +209,7 @@ class SqlMetricInlineView( # pylint: disable=too-many-ancestors add_title = _("Add Metric") edit_title = _("Edit Metric") - list_columns = ["metric_name", "verbose_name", "metric_type"] + list_columns = ["metric_name", "verbose_name", "metric_type", "extra"] edit_columns = [ "metric_name", "description", From 1ffa5670b39d84d2a28e4d7e2d685e962b6472e0 Mon Sep 17 00:00:00 2001 From: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com> Date: Wed, 17 Aug 2022 22:35:28 -0500 Subject: [PATCH 2/3] test: Fix async tests in SaveDatasetModal and complete overwrite screen test (#21020) * initial push * SaveDatasetModal test fix * Remove arbitrary async declarations Co-authored-by: AAfghahi --- .../SaveDatasetModal.test.tsx | 74 +++++++++++++------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx index 7e838a44e3e26..f707998d1df67 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx @@ -18,13 +18,7 @@ */ import React from 'react'; import * as reactRedux from 'react-redux'; -import { - render, - screen, - waitFor, - within, - cleanup, -} from 'spec/helpers/testing-library'; +import { render, screen, cleanup, waitFor } from 'spec/helpers/testing-library'; import userEvent from '@testing-library/user-event'; import fetchMock from 'fetch-mock'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; @@ -43,6 +37,8 @@ fetchMock.get('glob:*/api/v1/dataset?*', { dataset_count: 3, }); +jest.useFakeTimers(); + // Mock the user const useSelectorMock = jest.spyOn(reactRedux, 'useSelector'); beforeEach(() => { @@ -51,7 +47,7 @@ beforeEach(() => { }); describe('SaveDatasetModal', () => { - it('renders a "Save as new" field', async () => { + it('renders a "Save as new" field', () => { render(, { useRedux: true }); const saveRadioBtn = screen.getByRole('radio', { @@ -68,7 +64,7 @@ describe('SaveDatasetModal', () => { expect(inputFieldText).toBeVisible(); }); - it('renders an "Overwrite existing" field', async () => { + it('renders an "Overwrite existing" field', () => { render(, { useRedux: true }); const overwriteRadioBtn = screen.getByRole('radio', { @@ -84,20 +80,20 @@ describe('SaveDatasetModal', () => { expect(placeholderText).toBeVisible(); }); - it('renders a close button', async () => { + it('renders a close button', () => { render(, { useRedux: true }); expect(screen.getByRole('button', { name: /close/i })).toBeVisible(); }); - it('renders a save button when "Save as new" is selected', async () => { + it('renders a save button when "Save as new" is selected', () => { render(, { useRedux: true }); // "Save as new" is selected when the modal opens by default expect(screen.getByRole('button', { name: /save/i })).toBeVisible(); }); - it('renders an overwrite button when "Overwrite existing" is selected', async () => { + it('renders an overwrite button when "Overwrite existing" is selected', () => { render(, { useRedux: true }); // Click the overwrite radio button to reveal the overwrite confirmation and back buttons @@ -109,7 +105,7 @@ describe('SaveDatasetModal', () => { expect(screen.getByRole('button', { name: /overwrite/i })).toBeVisible(); }); - it('renders the overwrite button as disabled until an existing dataset is selected, confirms overwrite', async () => { + it('renders the overwrite button as disabled until an existing dataset is selected', async () => { useSelectorMock.mockReturnValue({ ...user }); render(, { useRedux: true }); @@ -117,9 +113,7 @@ describe('SaveDatasetModal', () => { const overwriteRadioBtn = screen.getByRole('radio', { name: /overwrite existing/i, }); - await waitFor(async () => { - userEvent.click(overwriteRadioBtn); - }); + userEvent.click(overwriteRadioBtn); // Overwrite confirmation button should be disabled at this point const overwriteConfirmationBtn = screen.getByRole('button', { @@ -127,21 +121,57 @@ describe('SaveDatasetModal', () => { }); expect(overwriteConfirmationBtn).toBeDisabled(); - // Click the select component + // Click the overwrite select component const select = screen.getByRole('combobox', { name: /existing dataset/i })!; - await waitFor(async () => userEvent.click(select)); + userEvent.click(select); + + await waitFor(() => + expect(screen.queryByText('Loading...')).not.toBeVisible(), + ); // Select the first "existing dataset" from the listbox - const option = within( - document.querySelector('.rc-virtual-list')!, - ).getByText('coolest table 0')!; + const option = screen.getAllByText('coolest table 0')[1]; userEvent.click(option); // Overwrite button should now be enabled expect(overwriteConfirmationBtn).toBeEnabled(); + }); + + it('renders a confirm overwrite screen when overwrite is clicked', async () => { + useSelectorMock.mockReturnValue({ ...user }); + render(, { useRedux: true }); + + // Click the overwrite radio button + const overwriteRadioBtn = screen.getByRole('radio', { + name: /overwrite existing/i, + }); + userEvent.click(overwriteRadioBtn); + + // Click the overwrite select component + const select = screen.getByRole('combobox', { name: /existing dataset/i }); + userEvent.click(select); + + await waitFor(() => + expect(screen.queryByText('Loading...')).not.toBeVisible(), + ); + + // Select the first "existing dataset" from the listbox + const option = screen.getAllByText('coolest table 0')[1]; + userEvent.click(option); - // Check Overwrite confirmation functionality + // Click the overwrite button to access the confirmation screen + const overwriteConfirmationBtn = screen.getByRole('button', { + name: /overwrite/i, + }); userEvent.click(overwriteConfirmationBtn); + + // Overwrite screen text + expect(screen.getByText(/save or overwrite dataset/i)).toBeVisible(); + expect( + screen.getByText(/are you sure you want to overwrite this dataset\?/i), + ).toBeVisible(); + // Overwrite screen buttons + expect(screen.getByRole('button', { name: /close/i })).toBeVisible(); expect(screen.getByRole('button', { name: /back/i })).toBeVisible(); expect(screen.getByRole('button', { name: /overwrite/i })).toBeVisible(); }); From 1afcdfb9fa47184f4ee54a57fa9d1985bb2ec6df Mon Sep 17 00:00:00 2001 From: Yongjie Zhao Date: Thu, 18 Aug 2022 11:42:58 +0800 Subject: [PATCH 3/3] fix: datetime value should be seconds in sqlite (#21113) --- superset/db_engine_specs/sqlite.py | 2 +- tests/unit_tests/db_engine_specs/test_sqlite.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/superset/db_engine_specs/sqlite.py b/superset/db_engine_specs/sqlite.py index b82b498c1689d..3dfe647e37185 100644 --- a/superset/db_engine_specs/sqlite.py +++ b/superset/db_engine_specs/sqlite.py @@ -110,7 +110,7 @@ def convert_dttm( ) -> Optional[str]: tt = target_type.upper() if tt in (utils.TemporalType.TEXT, utils.TemporalType.DATETIME): - return f"""'{dttm.isoformat(sep=" ", timespec="microseconds")}'""" + return f"""'{dttm.isoformat(sep=" ", timespec="seconds")}'""" return None @classmethod diff --git a/tests/unit_tests/db_engine_specs/test_sqlite.py b/tests/unit_tests/db_engine_specs/test_sqlite.py index 576f4ef9e9f17..03470173dc40e 100644 --- a/tests/unit_tests/db_engine_specs/test_sqlite.py +++ b/tests/unit_tests/db_engine_specs/test_sqlite.py @@ -27,13 +27,13 @@ def test_convert_dttm(dttm: datetime) -> None: from superset.db_engine_specs.sqlite import SqliteEngineSpec - assert SqliteEngineSpec.convert_dttm("TEXT", dttm) == "'2019-01-02 03:04:05.678900'" + assert SqliteEngineSpec.convert_dttm("TEXT", dttm) == "'2019-01-02 03:04:05'" def test_convert_dttm_lower(dttm: datetime) -> None: from superset.db_engine_specs.sqlite import SqliteEngineSpec - assert SqliteEngineSpec.convert_dttm("text", dttm) == "'2019-01-02 03:04:05.678900'" + assert SqliteEngineSpec.convert_dttm("text", dttm) == "'2019-01-02 03:04:05'" def test_convert_dttm_invalid_type(dttm: datetime) -> None: