Files
sentinel/tests/acceptance/test_organization_dashboards.py
2024-02-15 17:01:50 +00:00

742 lines
29 KiB
Python

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()