import pytest
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

from fixtures.page_objects.dashboard_detail import (
    EDIT_WIDGET_BUTTON,
    WIDGET_DRAG_HANDLE,
    WIDGET_RESIZE_HANDLE,
    WIDGET_TITLE_FIELD,
    DashboardDetailPage,
)
from sentry.models.dashboard import Dashboard
from sentry.models.dashboard_widget import (
    DashboardWidget,
    DashboardWidgetDisplayTypes,
    DashboardWidgetQuery,
    DashboardWidgetTypes,
)
from sentry.testutils.cases import AcceptanceTestCase
from sentry.testutils.helpers.datetime import before_now, iso_format
from sentry.testutils.silo import no_silo_test

FEATURE_NAMES = [
    "organizations:discover-basic",
    "organizations:discover-query",
    "organizations:dashboards-basic",
    "organizations:global-views",
]

EDIT_FEATURE = ["organizations:dashboards-edit"]


@no_silo_test
class OrganizationDashboardsAcceptanceTest(AcceptanceTestCase):
    def setUp(self):
        super().setUp()
        min_ago = iso_format(before_now(minutes=1))
        self.store_event(
            data={"event_id": "a" * 32, "message": "oh no", "timestamp": min_ago},
            project_id=self.project.id,
        )
        self.dashboard = Dashboard.objects.create(
            title="Dashboard 1", created_by_id=self.user.id, organization=self.organization
        )
        self.page = DashboardDetailPage(
            self.browser, self.client, organization=self.organization, dashboard=self.dashboard
        )
        self.login_as(self.user)

    def capture_screenshots(self, screenshot_name):
        """
        Captures screenshots in both a pre and post refresh state.

        Necessary for verifying that the layout persists after saving.
        """
        self.page.wait_until_loaded()
        self.browser.refresh()
        self.page.wait_until_loaded()

    def test_default_overview_dashboard_layout(self):
        with self.feature(FEATURE_NAMES):
            self.page.visit_default_overview()

    def test_add_and_move_new_widget_on_existing_dashboard(self):
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            self.page.add_widget_through_dashboard("New Widget")

            # Drag to the right
            dragHandle = self.browser.element(WIDGET_DRAG_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(dragHandle, 1000, 0).perform()

            self.page.save_dashboard()

            self.capture_screenshots("dashboards - save new widget layout in custom dashboard")

    def test_create_new_dashboard_with_modified_widget_layout(self):
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            # Create a new dashboard
            self.page.visit_create_dashboard()

            self.page.add_widget_through_dashboard("New Widget")

            # Drag to the right
            dragHandle = self.browser.element(WIDGET_DRAG_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(dragHandle, 1000, 0).perform()

            self.page.save_dashboard()

            # Wait for page redirect, or else loading check passes too early
            wait = WebDriverWait(self.browser.driver, 10)
            wait.until(
                lambda driver: (
                    f"/organizations/{self.organization.slug}/dashboards/new/"
                    not in driver.current_url
                )
            )

            self.capture_screenshots("dashboards - save widget layout in new custom dashboard")

    def test_move_existing_widget_on_existing_dashboard(self):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Existing Widget",
            display_type=DashboardWidgetDisplayTypes.LINE_CHART,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            # Drag to the right
            dragHandle = self.browser.element(WIDGET_DRAG_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(dragHandle, 1000, 0).perform()

            self.page.save_dashboard()

            self.capture_screenshots("dashboards - move existing widget on existing dashboard")

    @pytest.mark.skip(reason="flaky: DD-1216")
    def test_widget_edit_keeps_same_layout_after_modification(self):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Existing Widget",
            display_type=DashboardWidgetDisplayTypes.LINE_CHART,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            # Drag existing widget to the right
            dragHandle = self.browser.element(WIDGET_DRAG_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(dragHandle, 1000, 0).perform()

            # Edit the existing widget
            button = self.browser.element(EDIT_WIDGET_BUTTON)
            button.click()
            title_input = self.browser.element(WIDGET_TITLE_FIELD)
            title_input.clear()
            title_input.send_keys(Keys.END, "Existing WidgetUPDATED!!")
            button = self.browser.element('[aria-label="Update Widget"]')
            button.click()

            # Add and drag new widget to the right
            self.page.add_widget_through_dashboard("New Widget")
            dragHandle = self.browser.element(
                f".react-grid-item:nth-of-type(2) {WIDGET_DRAG_HANDLE}"
            )
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(dragHandle, 1000, 0)
            action.perform()

            # Edit the new widget
            button = self.browser.element(f".react-grid-item:nth-of-type(2) {EDIT_WIDGET_BUTTON}")
            button.click()
            title_input = self.browser.element(WIDGET_TITLE_FIELD)
            title_input.clear()
            title_input.send_keys(Keys.END, "New WidgetUPDATED!!")
            button = self.browser.element('[aria-label="Update Widget"]')
            button.click()

            self.page.save_dashboard()

            self.capture_screenshots(
                "dashboards - edit widgets after layout change does not reset layout"
            )

    def test_add_issue_widgets_do_not_overlap(self):
        def add_issue_widget(widget_title):
            self.browser.wait_until_clickable('[data-test-id="widget-add"]')
            self.page.click_dashboard_add_widget_button()
            title_input = self.browser.element(WIDGET_TITLE_FIELD)
            title_input.clear()
            title_input.send_keys(widget_title)
            self.browser.element('[aria-label="Issues (States, Assignment, Time, etc.)"]').click()
            button = self.browser.element('[aria-label="Add Widget"]')
            button.click()

        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            add_issue_widget("Issue Widget 1")
            add_issue_widget("Issue Widget 2")
            self.page.save_dashboard()

            self.capture_screenshots("dashboards - issue widgets do not overlap")

    def test_resize_new_and_existing_widgets(self):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Existing Widget",
            display_type=DashboardWidgetDisplayTypes.LINE_CHART,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            # Resize existing widget
            resizeHandle = self.browser.element(WIDGET_RESIZE_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(resizeHandle, 500, 0).perform()

            self.page.add_widget_through_dashboard("New Widget")

            # Drag it to the left for consistency
            dragHandle = self.browser.element(
                f".react-grid-item:nth-of-type(2) {WIDGET_DRAG_HANDLE}"
            )
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(dragHandle, -1000, 0).perform()

            # Resize new widget, get the 2nd element instead of the "last" because the "last" is
            # the add widget button
            resizeHandle = self.browser.element(
                f".react-grid-item:nth-of-type(2) {WIDGET_RESIZE_HANDLE}"
            )
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(resizeHandle, 500, 0).perform()

            self.page.save_dashboard()

            self.capture_screenshots("dashboards - resize new and existing widgets")

    def test_delete_existing_widget_does_not_trigger_new_widget_layout_reset(self):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Existing Widget",
            display_type=DashboardWidgetDisplayTypes.LINE_CHART,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
        )

        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            self.page.add_widget_through_dashboard("New Widget")

            # Drag it to the bottom left
            dragHandle = self.browser.element(
                f".react-grid-item:nth-of-type(2) {WIDGET_DRAG_HANDLE}"
            )
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(dragHandle, -500, 500).perform()

            # Resize new widget, get the 2nd element instead of the "last" because the "last" is
            # the add widget button
            resizeHandle = self.browser.element(
                f".react-grid-item:nth-of-type(2) {WIDGET_RESIZE_HANDLE}"
            )
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(resizeHandle, 500, 0).perform()

            # Delete first existing widget
            delete_widget_button = self.browser.element(
                '.react-grid-item:first-of-type [data-test-id="widget-delete"]'
            )
            delete_widget_button.click()

            self.page.save_dashboard()

            self.capture_screenshots(
                "dashboards - delete existing widget does not reset new widget layout"
            )

    def test_resize_big_number_widget(self):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Big Number Widget",
            display_type=DashboardWidgetDisplayTypes.BIG_NUMBER,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget,
            fields=["count_unique(issue)"],
            columns=[],
            aggregates=["count_unique(issue)"],
            order=0,
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            # Resize existing widget
            resizeHandle = self.browser.element(WIDGET_RESIZE_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(resizeHandle, 200, 200).perform()

            self.page.save_dashboard()

            self.capture_screenshots("dashboards - resize big number widget")

    def test_default_layout_when_widgets_do_not_have_layout_set(self):
        existing_widgets = DashboardWidget.objects.bulk_create(
            [
                DashboardWidget(
                    dashboard=self.dashboard,
                    order=i,
                    title=f"Existing Widget {i}",
                    display_type=DashboardWidgetDisplayTypes.LINE_CHART,
                    widget_type=DashboardWidgetTypes.DISCOVER,
                    interval="1d",
                )
                for i in range(4)
            ]
        )
        DashboardWidgetQuery.objects.bulk_create(
            [
                DashboardWidgetQuery(
                    widget=existing_widget,
                    fields=["count()"],
                    columns=[],
                    aggregates=["count()"],
                    order=0,
                )
                for existing_widget in existing_widgets
            ]
        )

        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            self.page.wait_until_loaded()

    def test_duplicate_widget_in_view_mode(self):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Big Number Widget",
            display_type=DashboardWidgetDisplayTypes.BIG_NUMBER,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget,
            fields=["count_unique(issue)"],
            columns=[],
            aggregates=["count_unique(issue)"],
            order=0,
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            self.browser.element('[aria-label="Widget actions"]').click()
            self.browser.element('[data-test-id="duplicate-widget"]').click()
            self.page.wait_until_loaded()

            self.browser.element('[aria-label="Widget actions"]').click()
            self.browser.element('[data-test-id="duplicate-widget"]').click()
            self.page.wait_until_loaded()

            # Should not trigger alert
            self.page.enter_edit_state()
            self.page.click_cancel_button()
            wait = WebDriverWait(self.browser.driver, 5)
            wait.until_not(EC.alert_is_present())

    def test_delete_widget_in_view_mode(self):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Big Number Widget",
            display_type=DashboardWidgetDisplayTypes.BIG_NUMBER,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget,
            fields=["count_unique(issue)"],
            columns=[],
            aggregates=["count_unique(issue)"],
            order=0,
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            self.browser.element('[aria-label="Widget actions"]').click()
            self.browser.element('[data-test-id="delete-widget"]').click()
            self.browser.element('[data-test-id="confirm-button"]').click()

            self.page.wait_until_loaded()

    def test_cancel_without_changes_does_not_trigger_confirm_with_custom_widget_through_header(
        self,
    ):
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            self.page.click_dashboard_header_add_widget_button()
            title_input = self.browser.element(WIDGET_TITLE_FIELD)
            title_input.send_keys("New custom widget")
            button = self.browser.element('[aria-label="Add Widget"]')
            button.click()
            self.page.wait_until_loaded()

            # Should not trigger confirm dialog
            self.page.enter_edit_state()
            self.page.click_cancel_button()
            wait = WebDriverWait(self.browser.driver, 5)
            wait.until_not(EC.alert_is_present())

    def test_position_when_adding_multiple_widgets_through_add_widget_tile_in_edit(
        self,
    ):
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()
            self.page.enter_edit_state()

            # Widgets should take up the whole first row and the first spot in second row
            self.page.add_widget_through_dashboard("A")
            self.page.add_widget_through_dashboard("B")
            self.page.add_widget_through_dashboard("C")
            self.page.add_widget_through_dashboard("D")
            self.page.wait_until_loaded()

            self.page.save_dashboard()
            self.capture_screenshots(
                "dashboards - position when adding multiple widgets through Add Widget tile in edit"
            )

    @pytest.mark.skip(reason="flaky: DD-1217")
    def test_position_when_adding_multiple_widgets_through_add_widget_tile_in_create(
        self,
    ):
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_create_dashboard()

            # Widgets should take up the whole first row and the first spot in second row
            self.page.add_widget_through_dashboard("A")
            self.page.add_widget_through_dashboard("B")
            self.page.add_widget_through_dashboard("C")
            self.page.add_widget_through_dashboard("D")
            self.page.wait_until_loaded()

            self.page.save_dashboard()

            # Wait for page redirect, or else loading check passes too early
            wait = WebDriverWait(self.browser.driver, 10)
            wait.until(
                lambda driver: (
                    f"/organizations/{self.organization.slug}/dashboards/new/"
                    not in driver.current_url
                )
            )
            self.capture_screenshots(
                "dashboards - position when adding multiple widgets through Add Widget tile in create"
            )

    def test_deleting_stacked_widgets_by_context_menu_does_not_trigger_confirm_on_edit_cancel(
        self,
    ):
        layouts = [
            {"x": 0, "y": 0, "w": 2, "h": 2, "minH": 2},
            {"x": 0, "y": 2, "w": 2, "h": 2, "minH": 2},
        ]
        existing_widgets = DashboardWidget.objects.bulk_create(
            [
                DashboardWidget(
                    dashboard=self.dashboard,
                    order=i,
                    title=f"Existing Widget {i}",
                    display_type=DashboardWidgetDisplayTypes.LINE_CHART,
                    widget_type=DashboardWidgetTypes.DISCOVER,
                    interval="1d",
                    detail={"layout": layout},
                )
                for i, layout in enumerate(layouts)
            ]
        )
        DashboardWidgetQuery.objects.bulk_create(
            DashboardWidgetQuery(
                widget=widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
            )
            for widget in existing_widgets
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            dropdown_trigger = self.browser.element('[aria-label="Widget actions"]')
            dropdown_trigger.click()

            delete_widget_menu_item = self.browser.element('[data-test-id="delete-widget"]')
            delete_widget_menu_item.click()

            confirm_button = self.browser.element('[data-test-id="confirm-button"]')
            confirm_button.click()

            wait = WebDriverWait(self.browser.driver, 5)
            wait.until(
                EC.presence_of_element_located(
                    (By.XPATH, "//*[contains(text(),'Dashboard updated')]")
                )
            )

            # Should not trigger confirm dialog
            self.page.enter_edit_state()
            self.page.click_cancel_button()
            wait.until_not(EC.alert_is_present())

    def test_changing_number_widget_to_area_updates_widget_height(
        self,
    ):
        layouts = [
            (DashboardWidgetDisplayTypes.BIG_NUMBER, {"x": 0, "y": 0, "w": 2, "h": 1, "minH": 1}),
            (DashboardWidgetDisplayTypes.LINE_CHART, {"x": 0, "y": 1, "w": 2, "h": 2, "minH": 2}),
        ]
        existing_widgets = DashboardWidget.objects.bulk_create(
            [
                DashboardWidget(
                    dashboard=self.dashboard,
                    order=i,
                    title=f"Widget {i}",
                    display_type=display_type,
                    widget_type=DashboardWidgetTypes.DISCOVER,
                    interval="1d",
                    detail={"layout": layout},
                )
                for i, (display_type, layout) in enumerate(layouts)
            ]
        )
        DashboardWidgetQuery.objects.bulk_create(
            DashboardWidgetQuery(
                widget=widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
            )
            for widget in existing_widgets
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            # Open edit modal for first widget
            dropdown_trigger = self.browser.element('[aria-label="Widget actions"]')
            dropdown_trigger.click()
            edit_widget_menu_item = self.browser.element('[data-test-id="edit-widget"]')
            edit_widget_menu_item.click()

            # Change the chart type to the first visualization option - Area chart
            chart_type_input = self.browser.element("#react-select-2-input")
            chart_type_input.send_keys("Area", Keys.ENTER)
            button = self.browser.element('[aria-label="Update Widget"]')
            button.click()

            # No confirm dialog because of shifting lower element
            self.page.enter_edit_state()
            self.page.click_cancel_button()
            wait = WebDriverWait(self.browser.driver, 5)
            wait.until_not(EC.alert_is_present())

            # Try to decrease height to 1 row, should stay at 2 rows
            self.page.enter_edit_state()
            resizeHandle = self.browser.element(WIDGET_RESIZE_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(resizeHandle, 0, -100).perform()

            self.page.save_dashboard()

    @pytest.mark.skip(reason="flaky behaviour due to loading spinner")
    def test_changing_number_widget_larger_than_min_height_for_area_chart_keeps_height(
        self,
    ):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Originally Big Number - 3 rows",
            display_type=DashboardWidgetDisplayTypes.BIG_NUMBER,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
            detail={"layout": {"x": 0, "y": 0, "w": 2, "h": 3, "minH": 1}},
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            # Open edit modal for first widget
            dropdown_trigger = self.browser.element('[aria-label="Widget actions"]')
            dropdown_trigger.click()
            edit_widget_menu_item = self.browser.element('[data-test-id="edit-widget"]')
            edit_widget_menu_item.click()

            # Change the chart type to the first visualization option - Area chart
            chart_type_input = self.browser.element("#react-select-2-input")
            chart_type_input.send_keys("Area", Keys.ENTER)
            button = self.browser.element('[aria-label="Update Widget"]')
            button.click()

            self.page.wait_until_loaded()

            # Try to decrease height by >1 row, should be at 2 rows
            self.page.enter_edit_state()
            resizeHandle = self.browser.element(WIDGET_RESIZE_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(resizeHandle, 0, -400).perform()

            self.page.save_dashboard()

    @pytest.mark.skip(reason="flaky: DD-1211")
    def test_changing_area_widget_larger_than_min_height_for_number_chart_keeps_height(
        self,
    ):
        existing_widget = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Originally Area Chart - 3 rows",
            display_type=DashboardWidgetDisplayTypes.AREA_CHART,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
            detail={"layout": {"x": 0, "y": 0, "w": 2, "h": 3, "minH": 2}},
        )
        DashboardWidgetQuery.objects.create(
            widget=existing_widget, fields=["count()"], columns=[], aggregates=["count()"], order=0
        )
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.page.visit_dashboard_detail()

            # Open edit modal for first widget
            dropdown_trigger = self.browser.element('[aria-label="Widget actions"]')
            dropdown_trigger.click()
            edit_widget_menu_item = self.browser.element('[data-test-id="edit-widget"]')
            edit_widget_menu_item.click()

            # Change the chart type to big number
            chart_type_input = self.browser.element("#react-select-2-input")
            chart_type_input.send_keys("Big Number", Keys.ENTER)
            button = self.browser.element('[aria-label="Update Widget"]')
            button.click()

            self.page.wait_until_loaded()

            # Decrease height by >1 row, should stop at 1 row
            self.page.enter_edit_state()
            resizeHandle = self.browser.element(WIDGET_RESIZE_HANDLE)
            action = ActionChains(self.browser.driver)
            action.drag_and_drop_by_offset(resizeHandle, 0, -400).perform()

            self.page.save_dashboard()


@no_silo_test
class OrganizationDashboardsManageAcceptanceTest(AcceptanceTestCase):
    def setUp(self):
        super().setUp()
        self.team = self.create_team(organization=self.organization, name="Mariachi Band")
        self.project = self.create_project(
            organization=self.organization, teams=[self.team], name="Bengal"
        )
        self.dashboard = Dashboard.objects.create(
            title="Dashboard 1", created_by_id=self.user.id, organization=self.organization
        )
        self.widget_1 = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=0,
            title="Widget 1",
            display_type=DashboardWidgetDisplayTypes.LINE_CHART,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        self.widget_2 = DashboardWidget.objects.create(
            dashboard=self.dashboard,
            order=1,
            title="Widget 2",
            display_type=DashboardWidgetDisplayTypes.TABLE,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )
        self.login_as(self.user)

        self.default_path = f"/organizations/{self.organization.slug}/dashboards/"

    def wait_until_loaded(self):
        self.browser.wait_until_not('[data-test-id="loading-indicator"]')
        self.browser.wait_until_not('[data-test-id="loading-placeholder"]')

    def test_dashboard_manager(self):
        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.browser.get(self.default_path)
            self.wait_until_loaded()

    def test_dashboard_manager_with_unset_layouts_and_defined_layouts(self):
        dashboard_with_layouts = Dashboard.objects.create(
            title="Dashboard with some defined layouts",
            created_by_id=self.user.id,
            organization=self.organization,
        )
        DashboardWidget.objects.create(
            dashboard=dashboard_with_layouts,
            order=0,
            title="Widget 1",
            display_type=DashboardWidgetDisplayTypes.BAR_CHART,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
            detail={"layout": {"x": 1, "y": 0, "w": 3, "h": 3, "minH": 2}},
        )

        # This widget has no layout, but should position itself at
        # x: 4, y: 0, w: 2, h: 2
        DashboardWidget.objects.create(
            dashboard=dashboard_with_layouts,
            order=1,
            title="Widget 2",
            display_type=DashboardWidgetDisplayTypes.TABLE,
            widget_type=DashboardWidgetTypes.DISCOVER,
            interval="1d",
        )

        with self.feature(FEATURE_NAMES + EDIT_FEATURE):
            self.browser.get(self.default_path)
            self.wait_until_loaded()