diff --git a/playwright/editing-dashboard.spec.ts b/playwright/editing-dashboard.spec.ts index 9f83510..a3fab05 100644 --- a/playwright/editing-dashboard.spec.ts +++ b/playwright/editing-dashboard.spec.ts @@ -1,18 +1,17 @@ -import { test, expect, Page } from '@playwright/test'; +import { Page, expect, test } from '@playwright/test'; import { disableCookiePrompt } from '@redhat-cloud-services/playwright-test-auth'; - -const DASHBOARD_HUB_URL = '/dashboard-hub'; -const TABLE_SELECTOR = '[data-ouia-component-id="DashboardsTable"]'; +import { DASHBOARD_HUB_URL, TABLE_SELECTOR, PAGE_LOAD_TIMEOUT_MS, WIDGET_LOAD_TIMEOUT_MS, deleteTestDashboard } from './helpers'; const navigateToDashboardHub = async (page: Page) => { await page.goto(DASHBOARD_HUB_URL); - await page.locator(TABLE_SELECTOR).waitFor({ state: 'visible', timeout: 30000 }); + await page.locator(TABLE_SELECTOR).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + await page.locator(TABLE_SELECTOR).locator('a').first().waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); }; const navigateToGenericDashboard = async (page: Page, dashboardName: string) => { await navigateToDashboardHub(page); await page.getByRole('link', { name: dashboardName, exact: true }).click(); - await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: 30000 }); + await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); }; const openKebabDropdown = async (page: Page) => { @@ -20,6 +19,7 @@ const openKebabDropdown = async (page: Page) => { await page.getByRole('menuitem', { name: 'Set as homepage' }).waitFor({ state: 'visible' }); }; + const findDashboardNames = (page: Page) => { return page.evaluate((selector) => { const table = document.querySelector(selector); @@ -44,19 +44,22 @@ const findDashboardNames = (page: Page) => { }; const hasHomeIcon = async (page: Page, dashboardName: string) => { - return page.evaluate(({ selector, name }) => { - const table = document.querySelector(selector); - if (!table) return false; - const rows = table.querySelectorAll('tbody tr'); - for (const row of rows) { - const link = row.querySelector(':scope > td:nth-child(2) a'); - if (link?.textContent === name) { - const firstTd = row.querySelector(':scope > td:first-child'); - return !!firstTd?.querySelector('svg'); + return page.evaluate( + ({ selector, name }) => { + const table = document.querySelector(selector); + if (!table) return false; + const rows = table.querySelectorAll('tbody tr'); + for (const row of rows) { + const link = row.querySelector(':scope > td:nth-child(2) a'); + if (link?.textContent === name) { + const firstTd = row.querySelector(':scope > td:first-child'); + return !!firstTd?.querySelector('svg'); + } } - } - return false; - }, { selector: TABLE_SELECTOR, name: dashboardName }); + return false; + }, + { selector: TABLE_SELECTOR, name: dashboardName } + ); }; test.describe('Set Dashboard as Homepage from Generic Page', () => { @@ -155,8 +158,8 @@ test.describe('Inline Editing Dashboard Name', () => { await input.fill(newName); await page.getByRole('button', { name: 'Confirm name' }).click(); - await expect(input).not.toBeVisible({ timeout: 5000 }); - await expect(page.locator('h1').filter({ hasText: newName })).toBeVisible({ timeout: 5000 }); + await expect(input).not.toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + await expect(page.locator('h1').filter({ hasText: newName })).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); await navigateToDashboardHub(page); await expect(page.getByRole('link', { name: newName })).toBeVisible({ timeout: 10000 }); @@ -193,3 +196,217 @@ test.describe('Inline Editing Dashboard Name', () => { await expect(page.locator('h1').filter({ hasText: nonDefaultName })).toBeVisible({ timeout: 5000 }); }); }); + +test.describe('Dashboard Hub - Page Rendering', () => { + test.beforeEach(async ({ page }) => { + await disableCookiePrompt(page); + }); + + test('should render the dashboard hub with table and headers', async ({ page }) => { + await navigateToDashboardHub(page); + + await expect(page.getByRole('heading', { name: 'Dashboard Hub', level: 1 })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Create dashboard' })).toBeVisible(); + + const table = page.locator(TABLE_SELECTOR); + await expect(table).toBeVisible(); + + await expect(table.getByRole('columnheader', { name: 'Homepage' })).toBeVisible(); + await expect(table.getByRole('columnheader', { name: 'Name' })).toBeVisible(); + await expect(table.getByRole('columnheader', { name: 'Last Modified' })).toBeVisible(); + + // Wait for data rows to load (table renders before data arrives) + const firstLink = table.locator('a').first(); + await expect(firstLink).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + }); +}); + +test.describe('Dashboard Hub - Create Blank Dashboard', () => { + test.beforeEach(async ({ page }) => { + await disableCookiePrompt(page); + }); + + test('should create a blank dashboard and see it in the hub table', async ({ page }) => { + const TEST_NAME = `__e2e_create_${Date.now()}`; + await navigateToDashboardHub(page); + + try { + await page.waitForLoadState('networkidle'); + await page.getByRole('button', { name: 'Create dashboard' }).click(); + await page.getByRole('menuitem', { name: 'Create from blank' }).click(); + + const modal = page.locator('[data-ouia-component-id="CreateBlankDashboardModal"]'); + await modal.waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + await modal.getByPlaceholder('from-scratch dashboard').fill(TEST_NAME); + + const createBtn = modal.getByRole('button', { name: 'Create dashboard' }); + await expect(createBtn).toBeEnabled({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + await createBtn.click(); + + await page.getByRole('link', { name: TEST_NAME, exact: true }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + } finally { + await deleteTestDashboard(page, TEST_NAME); + } + }); +}); + +test.describe('Dashboard Hub - Duplicate Dashboard', () => { + test.beforeEach(async ({ page }) => { + await disableCookiePrompt(page); + }); + + test('should duplicate an existing dashboard from the hub table', async ({ page }) => { + const DUPLICATE_NAME = `__e2e_duplicate_${Date.now()}`; + await navigateToDashboardHub(page); + + // Wait for data rows to load + const table = page.locator(TABLE_SELECTOR); + const firstLink = table.locator('a').first(); + await expect(firstLink).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + + // Get the Duplicate button from the first row's actions + const firstDuplicateBtn = table.getByRole('button', { name: 'Duplicate' }).first(); + await page.waitForLoadState('networkidle'); + + try { + await firstDuplicateBtn.click(); + + const modal = page.locator('[data-ouia-component-id="DuplicateDashboardModal"]'); + await expect(modal).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + + await modal.getByPlaceholder('duplicate dashboard').fill(DUPLICATE_NAME); + const duplicateBtn = modal.getByRole('button', { name: 'Duplicate dashboard' }); + await expect(duplicateBtn).toBeEnabled({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + await duplicateBtn.click(); + + // Verify success notification + await page.getByText(`Dashboard '${DUPLICATE_NAME}' duplicated successfully`).waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + + // Table doesn't auto-refresh after duplication — reload to verify + await page.goto(DASHBOARD_HUB_URL); + await page.locator(TABLE_SELECTOR).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + await expect(page.getByRole('link', { name: DUPLICATE_NAME, exact: true })).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + } finally { + await deleteTestDashboard(page, DUPLICATE_NAME); + } + }); +}); + +test.describe('Dashboard Hub - Delete Dashboard', () => { + test.beforeEach(async ({ page }) => { + await disableCookiePrompt(page); + }); + + test('should delete a dashboard and see it removed from the hub', async ({ page }) => { + const DELETE_NAME = `__e2e_delete_${Date.now()}`; + await navigateToDashboardHub(page); + + // Create a dashboard to delete + await page.waitForLoadState('networkidle'); + await page.getByRole('button', { name: 'Create dashboard' }).click(); + await page.getByRole('menuitem', { name: 'Create from blank' }).click(); + + const modal = page.locator('[data-ouia-component-id="CreateBlankDashboardModal"]'); + await modal.waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + await modal.getByPlaceholder('from-scratch dashboard').fill(DELETE_NAME); + const createBtn = modal.getByRole('button', { name: 'Create dashboard' }); + await expect(createBtn).toBeEnabled({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + await createBtn.click(); + + await page.getByRole('link', { name: DELETE_NAME, exact: true }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + + // Navigate to the dashboard + await page.getByRole('link', { name: DELETE_NAME, exact: true }).click(); + await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + + // Delete it + await page.waitForLoadState('networkidle'); + await page.getByRole('button', { name: 'kebab dropdown toggle' }).click(); + await page.getByRole('menuitem', { name: 'Delete dashboard' }).click(); + + await page.getByText(/Deleting the dashboard will remove/).waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + await page.getByRole('checkbox', { name: /I understand that this action cannot be undone/i }).check(); + await page.getByRole('button', { name: 'Delete dashboard' }).click(); + + // Verify redirected to hub and dashboard is gone + await page.locator(TABLE_SELECTOR).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + await expect(page.getByRole('link', { name: DELETE_NAME, exact: true })).not.toBeVisible(); + }); +}); + +test.describe('Dashboard Hub - Navigation', () => { + test.beforeEach(async ({ page }) => { + await disableCookiePrompt(page); + }); + + test('should navigate from hub to dashboard and back via breadcrumb', async ({ page }) => { + await navigateToDashboardHub(page); + + const table = page.locator(TABLE_SELECTOR); + const firstLink = table.locator('a').first(); + await expect(firstLink).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + const dashboardName = await firstLink.textContent(); + + if (!dashboardName) { + test.skip(true, 'No dashboard found to navigate to'); + return; + } + + // Navigate to the dashboard + await firstLink.click(); + await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + + // Verify breadcrumb shows dashboard name + const breadcrumb = page.getByRole('navigation', { name: 'Breadcrumb' }); + await expect(breadcrumb.getByText(dashboardName)).toBeVisible(); + await expect(breadcrumb.getByRole('link', { name: 'Dashboard Hub' })).toBeVisible(); + + // Navigate back via breadcrumb + await breadcrumb.getByRole('link', { name: 'Dashboard Hub' }).click(); + + // Verify back on hub + await expect(page.getByRole('heading', { name: 'Dashboard Hub', level: 1 })).toBeVisible({ timeout: PAGE_LOAD_TIMEOUT_MS }); + await expect(table).toBeVisible(); + }); +}); + +test.describe('Generic Dashboard Page - Rendering', () => { + test.beforeEach(async ({ page }) => { + await disableCookiePrompt(page); + }); + + test('should render the generic dashboard page with all controls', async ({ page }) => { + await navigateToDashboardHub(page); + + const table = page.locator(TABLE_SELECTOR); + const firstLink = table.locator('a').first(); + await expect(firstLink).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + const dashboardName = await firstLink.textContent(); + + if (!dashboardName) { + test.skip(true, 'No dashboard found to navigate to'); + return; + } + + await navigateToGenericDashboard(page, dashboardName); + + // Header controls + await expect(page.locator('h1').filter({ hasText: dashboardName })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Edit dashboard name' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Add widgets' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'kebab dropdown toggle' })).toBeVisible(); + + // Breadcrumb + const breadcrumb = page.getByRole('navigation', { name: 'Breadcrumb' }); + await expect(breadcrumb.getByRole('link', { name: 'Home' })).toBeVisible(); + await expect(breadcrumb.getByRole('link', { name: 'Dashboard Hub' })).toBeVisible(); + await expect(breadcrumb.getByText(dashboardName)).toBeVisible(); + + // Kebab menu items + await openKebabDropdown(page); + await expect(page.getByRole('menuitem', { name: 'Set as homepage' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'Duplicate dashboard' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'Copy configuration string' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'Delete dashboard' })).toBeVisible(); + }); +}); diff --git a/playwright/helpers.ts b/playwright/helpers.ts new file mode 100644 index 0000000..dd85770 --- /dev/null +++ b/playwright/helpers.ts @@ -0,0 +1,26 @@ +import { Page } from '@playwright/test'; + +export const DASHBOARD_HUB_URL = '/dashboard-hub'; +export const TABLE_SELECTOR = '[data-ouia-component-id="DashboardsTable"]'; +export const PAGE_LOAD_TIMEOUT_MS = 30000; +export const WIDGET_LOAD_TIMEOUT_MS = 10000; + +export const deleteTestDashboard = async (page: Page, dashboardName: string) => { + await page.goto(DASHBOARD_HUB_URL); + await page.locator(TABLE_SELECTOR).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + await page.locator(TABLE_SELECTOR).locator('a').first().waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + + const testLink = page.getByRole('link', { name: dashboardName, exact: true }); + if (!(await testLink.isVisible())) return; + + await testLink.click(); + await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + await page.waitForLoadState('networkidle'); + + await page.getByRole('button', { name: 'kebab dropdown toggle' }).click(); + await page.getByRole('menuitem', { name: 'Delete dashboard' }).click(); + + await page.getByText(/Deleting the dashboard will remove/).waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + await page.getByRole('checkbox', { name: /I understand that this action cannot be undone/i }).check(); + await page.getByRole('button', { name: 'Delete dashboard' }).click(); +}; diff --git a/playwright/widget-layout.spec.ts b/playwright/widget-layout.spec.ts index 7b0f577..5ce886a 100644 --- a/playwright/widget-layout.spec.ts +++ b/playwright/widget-layout.spec.ts @@ -1,5 +1,8 @@ -import { test, expect } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { disableCookiePrompt } from '@redhat-cloud-services/playwright-test-auth'; +import { TABLE_SELECTOR, PAGE_LOAD_TIMEOUT_MS, WIDGET_LOAD_TIMEOUT_MS, deleteTestDashboard } from './helpers'; + +const DRAWER_TIMEOUT_MS = 5000; test.describe('Widget Layout - Basic Rendering', () => { test.beforeEach(async ({ page }) => { @@ -20,7 +23,7 @@ test.describe('Widget Layout - Basic Rendering', () => { expect(title.length).toBeGreaterThan(0); // Verify authenticated page elements are present - await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: 30000 }); + await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); await expect(page.getByRole('button', { name: 'Add widgets' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Reset to default' })).toBeVisible(); @@ -34,8 +37,16 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { test.beforeEach(async ({ page }) => { await disableCookiePrompt(page); await page.goto('/'); - // Wait for dashboard to be ready - await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: 30000 }); + await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + + // If dashboard is empty, reset to default to ensure it has widgets + const emptyState = page.getByText('No dashboard content'); + if (await emptyState.isVisible()) { + await page.getByRole('button', { name: 'Reset to default' }).click(); + await page.getByRole('checkbox', { name: /I understand that this action cannot be undone/i }).check(); + await page.getByRole('button', { name: 'Reset layout' }).click(); + await page.locator('.pf-v6-c-card__title-text').first().waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + } }); test('should open the widget drawer when clicking Add widgets button', async ({ page }) => { @@ -51,7 +62,7 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { // Drawer is already open, close it first to test the opening action await addWidgetButton.click(); await page.waitForTimeout(1000); - await expect(drawerText).not.toBeVisible({ timeout: 5000 }); + await expect(drawerText).not.toBeVisible({ timeout: DRAWER_TIMEOUT_MS }); } // Now open the drawer @@ -61,7 +72,7 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { await page.waitForTimeout(1000); // Verify the drawer opens by checking for the instruction text - await expect(drawerText).toBeVisible({ timeout: 10000 }); + await expect(drawerText).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); // Verify the instruction about drag and drop is visible await expect(page.getByText(/drag and drop to a new location/i)).toBeVisible(); @@ -79,7 +90,7 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { } // Wait for drawer to be visible - await expect(drawerText).toBeVisible({ timeout: 5000 }); + await expect(drawerText).toBeVisible({ timeout: DRAWER_TIMEOUT_MS }); // Check for example draggable widgets in the drawer const drawerSection = page.locator('text=Add new and previously removed widgets').locator('..'); @@ -95,7 +106,7 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { // Open the drawer await page.getByRole('button', { name: 'Add widgets' }).click(); await page.waitForTimeout(1000); - await expect(drawerText).toBeVisible({ timeout: 5000 }); + await expect(drawerText).toBeVisible({ timeout: DRAWER_TIMEOUT_MS }); } // Click Add widgets again to close @@ -105,7 +116,7 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { await page.waitForTimeout(1000); // Verify the instruction text is no longer visible - await expect(drawerText).not.toBeVisible({ timeout: 5000 }); + await expect(drawerText).not.toBeVisible({ timeout: DRAWER_TIMEOUT_MS }); }); test('should display main widget cards on the page', async ({ page }) => { @@ -113,10 +124,11 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { const mainContent = page.locator('main'); await expect(mainContent).toBeVisible(); - // Check for service widget cards on the page - target specific card title elements - await expect(page.locator('#widget-layout-container .pf-v6-widget-grid-tile__title').filter({ hasText: 'Red Hat Enterprise Linux' })).toBeVisible(); - await expect(page.locator('#widget-layout-container .pf-v6-widget-grid-tile__title').filter({ hasText: /^Red Hat OpenShift$/ })).toBeVisible(); - await expect(page.getByText('Recently visited')).toBeVisible(); + // Check that widget cards are rendered on the page + const widgetCards = page.locator('.pf-v6-c-card__title-text'); + await widgetCards.first().waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + const count = await widgetCards.count(); + expect(count).toBeGreaterThanOrEqual(3); }); test('should have Reset to default button visible', async ({ page }) => { @@ -126,9 +138,10 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { }); test('should not show the widget drawer by default on page load', async ({ page }) => { - await page.locator('#widget-layout-container .pf-v6-widget-grid-tile__title') + await page + .locator('.pf-v6-c-card__title-text') .first() - .waitFor({ state: 'visible', timeout: 10000 }); + .waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); const drawerText = page.getByText('Add new and previously removed widgets'); await expect(drawerText).not.toBeVisible(); @@ -136,38 +149,42 @@ test.describe('Widget Layout - Add Widget from Drawer', () => { }); test.describe('Widget Layout - Empty Dashboard', () => { - test('should auto-open the widget drawer when dashboard has no widgets', async ({ page }) => { + test('should auto-open the widget drawer when landing on an empty dashboard', async ({ page }) => { + const TEST_DASHBOARD_NAME = `__e2e_empty_dashboard_${Date.now()}`; await disableCookiePrompt(page); - await page.addInitScript(() => { - const originalFetch = window.fetch; - window.fetch = async (...args) => { - const url = typeof args[0] === 'string' ? args[0] : args[0] instanceof Request ? args[0].url : ''; - if ( - (url.includes('/api/widget-layout/v1/') && url.includes('dashboardType=')) || - (url.includes('/api/chrome-service/v1/dashboard-templates') && url.includes('dashboard=')) - ) { - return new Response(JSON.stringify({ - data: [{ - id: 1, - default: true, - templateBase: { name: 'landing-landingPage', displayName: 'Landing Page' }, - templateConfig: { sm: [], md: [], lg: [], xl: [] }, - dashboardName: 'Test Dashboard', - createdAt: '2024-01-01T00:00:00Z', - updatedAt: '2024-01-01T00:00:00Z', - deletedAt: null, - userId: 'test-user', - }], - }), { status: 200, headers: { 'Content-Type': 'application/json' } }); - } - return originalFetch(...args); - }; - }); - await page.goto('/'); - await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: 10000 }); + await page.goto('/dashboard-hub'); + await page.locator(TABLE_SELECTOR).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); - const drawerText = page.getByText('Add new and previously removed widgets'); - await expect(drawerText).toBeVisible({ timeout: 10000 }); + try { + // Create a blank dashboard (no homepage change needed) + await page.waitForLoadState('networkidle'); + await page.getByRole('button', { name: 'Create dashboard' }).click(); + await page.getByRole('menuitem', { name: 'Create from blank' }).click(); + + const modal = page.locator('[data-ouia-component-id="CreateBlankDashboardModal"]'); + await modal.waitFor({ state: 'visible', timeout: WIDGET_LOAD_TIMEOUT_MS }); + await modal.getByPlaceholder('from-scratch dashboard').fill(TEST_DASHBOARD_NAME); + + const createBtn = modal.getByRole('button', { name: 'Create dashboard' }); + await expect(createBtn).toBeEnabled({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + await createBtn.click(); + + // Wait for the new dashboard to appear in the table + await page.getByRole('link', { name: TEST_DASHBOARD_NAME, exact: true }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + + // Navigate to the empty dashboard + await page.getByRole('link', { name: TEST_DASHBOARD_NAME, exact: true }).click(); + await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: PAGE_LOAD_TIMEOUT_MS }); + + // Verify drawer auto-opened on empty dashboard + const drawerText = page.getByText('Add new and previously removed widgets'); + await expect(drawerText).toBeVisible({ timeout: WIDGET_LOAD_TIMEOUT_MS }); + + // Verify empty state is shown + await expect(page.getByText('No dashboard content')).toBeVisible(); + } finally { + await deleteTestDashboard(page, TEST_DASHBOARD_NAME); + } }); });