From 0c479f568c371a045151dad119050bfda1ff47b9 Mon Sep 17 00:00:00 2001 From: ochenstarik Date: Fri, 11 Sep 2026 12:28:41 +0700 Subject: [PATCH] fix(e2e): use domcontentloaded navigation across all test suites to prevent preview server load timeouts --- e2e/01-dashboard.spec.ts | 2 +- e2e/02-theme.spec.ts | 4 ++-- e2e/03-crm-dnd.spec.ts | 8 ++++++-- e2e/04-crm-lead-conversion.spec.ts | 2 +- e2e/05-crm-deal-won-order.spec.ts | 4 ++-- e2e/06-order-fulfillment.spec.ts | 6 +++--- e2e/07-configuration-engine.spec.ts | 4 ++-- e2e/08-view-builder.spec.ts | 4 ++-- e2e/09-rbac.spec.ts | 4 ++-- e2e/10-indexeddb-snapshots.spec.ts | 2 +- e2e/11-modules-catalog.spec.ts | 6 +++--- e2e/12-mobile-first.spec.ts | 14 +++++++------- 12 files changed, 32 insertions(+), 28 deletions(-) diff --git a/e2e/01-dashboard.spec.ts b/e2e/01-dashboard.spec.ts index e9f72be..1bbd8eb 100644 --- a/e2e/01-dashboard.spec.ts +++ b/e2e/01-dashboard.spec.ts @@ -9,7 +9,7 @@ test.describe('Scenario 01: Dashboard Integrity & Viewport Matrix', () => { } }); - await page.goto('/'); + await page.goto('/', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Verify dashboard title / header diff --git a/e2e/02-theme.spec.ts b/e2e/02-theme.spec.ts index 43e379d..5895077 100644 --- a/e2e/02-theme.spec.ts +++ b/e2e/02-theme.spec.ts @@ -37,7 +37,7 @@ test.describe('Scenario 02: Theme Engine & UI Persistence', () => { await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark'); // 2. Reload and verify Dark Theme persistence - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark'); @@ -52,7 +52,7 @@ test.describe('Scenario 02: Theme Engine & UI Persistence', () => { await expect(page.locator('html')).toHaveAttribute('data-theme', 'soft-light'); // 5. Reload and verify Soft Light Theme persistence - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); await expect(page.locator('html')).toHaveAttribute('data-theme', 'soft-light'); }); diff --git a/e2e/03-crm-dnd.spec.ts b/e2e/03-crm-dnd.spec.ts index 9339bad..2f9c021 100644 --- a/e2e/03-crm-dnd.spec.ts +++ b/e2e/03-crm-dnd.spec.ts @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; test.describe('Scenario 03: CRM Kanban Drag-and-Drop & Stage Persistence', () => { test('drags a deal card between kanban stages and verifies persistence after reload', async ({ page }) => { - await page.goto('/crm/deals'); + await page.goto('/crm/deals', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Switch to Deals tab if needed @@ -17,11 +17,13 @@ test.describe('Scenario 03: CRM Kanban Drag-and-Drop & Stage Persistence', () => // Pick first available deal card const firstCard = dealCards.first(); + await firstCard.scrollIntoViewIfNeeded().catch(() => {}); const dealId = await firstCard.getAttribute('data-deal-id'); expect(dealId).toBeTruthy(); const targetStage = 'negotiation'; const targetColumn = page.locator(`[data-testid="kanban-column-${targetStage}"]`); + await targetColumn.scrollIntoViewIfNeeded().catch(() => {}); await expect(targetColumn).toBeVisible(); // Perform Drag and Drop (with short timeout and fallback to transition button if intercepted) @@ -32,16 +34,18 @@ test.describe('Scenario 03: CRM Kanban Drag-and-Drop & Stage Persistence', () => if (!await cardInTarget.isVisible({ timeout: 2000 }).catch(() => false)) { const nextBtn = page.locator(`[data-testid="deal-card-${dealId}"] button:has-text("Далее:")`).first(); if (await nextBtn.isVisible({ timeout: 2000 }).catch(() => false)) { + await nextBtn.scrollIntoViewIfNeeded().catch(() => {}); await nextBtn.click({ force: true }).catch(() => {}); } } // Reload page to verify persistence in IndexedDB - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Ensure the card still exists and its updated stage persisted const persistedCard = page.locator(`[data-testid="deal-card-${dealId}"]`); + await persistedCard.scrollIntoViewIfNeeded().catch(() => {}); await expect(persistedCard).toBeVisible({ timeout: 15000 }); }); }); diff --git a/e2e/04-crm-lead-conversion.spec.ts b/e2e/04-crm-lead-conversion.spec.ts index 4b11687..4a60f8e 100644 --- a/e2e/04-crm-lead-conversion.spec.ts +++ b/e2e/04-crm-lead-conversion.spec.ts @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; test.describe('Scenario 04: CRM Lead Creation & Full Conversion Flow', () => { test('creates a new lead, converts it into contact and deal, and verifies both', async ({ page }) => { - await page.goto('/crm/leads'); + await page.goto('/crm/leads', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Ensure Leads tab is active diff --git a/e2e/05-crm-deal-won-order.spec.ts b/e2e/05-crm-deal-won-order.spec.ts index c9a85d3..5af0911 100644 --- a/e2e/05-crm-deal-won-order.spec.ts +++ b/e2e/05-crm-deal-won-order.spec.ts @@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test'; test.describe('Scenario 05: CRM Deal Won -> Commercial Order Flow', () => { test('advances deal to Won, confirms linked Order creation, and verifies order in Sales', async ({ page }) => { test.setTimeout(60000); - await page.goto('/crm/deals'); + await page.goto('/crm/deals', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Switch to Deals tab if needed @@ -39,7 +39,7 @@ test.describe('Scenario 05: CRM Deal Won -> Commercial Order Flow', () => { await page.waitForTimeout(500); // Navigate to Sales module - await page.goto('/sales'); + await page.goto('/sales', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Verify order exists in the Commerce Orders table diff --git a/e2e/06-order-fulfillment.spec.ts b/e2e/06-order-fulfillment.spec.ts index 3b06fa2..daa1755 100644 --- a/e2e/06-order-fulfillment.spec.ts +++ b/e2e/06-order-fulfillment.spec.ts @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; test.describe('Scenario 06: Commercial Order Fulfillment (Confirm -> Payment -> Shipment)', () => { test('executes end-to-end order lifecycle and verifies finance and inventory sync', async ({ page }) => { - await page.goto('/sales'); + await page.goto('/sales', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // 1. Select an order from the list @@ -38,13 +38,13 @@ test.describe('Scenario 06: Commercial Order Fulfillment (Confirm -> Payment -> } // 5. Navigate to Finance: Verify Cash Flow / DDS Reflection in main area - await page.goto('/finance'); + await page.goto('/finance', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); await expect(page.locator('main').locator('text=Финансы').or(page.locator('main').locator('text=ДДС')).first()).toBeVisible({ timeout: 15000 }); await expect(page.locator('tbody tr').first()).toBeVisible({ timeout: 10000 }); // 6. Navigate to Inventory: Verify warehouse stock view in main area - await page.goto('/inventory'); + await page.goto('/inventory', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); await expect(page.locator('main').locator('text=Склад').or(page.locator('main').locator('text=Остатки')).first()).toBeVisible({ timeout: 15000 }); }); diff --git a/e2e/07-configuration-engine.spec.ts b/e2e/07-configuration-engine.spec.ts index c9e74c7..b10a09c 100644 --- a/e2e/07-configuration-engine.spec.ts +++ b/e2e/07-configuration-engine.spec.ts @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; test.describe('Scenario 07: Configuration Engine (Entity Creation & Custom Records)', () => { test('creates custom entity in Catalog, adds record at runtime, and verifies persistence', async ({ page }) => { - await page.goto('/settings/entities'); + await page.goto('/settings/entities', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // 1. Open Create Entity Modal @@ -66,7 +66,7 @@ test.describe('Scenario 07: Configuration Engine (Entity Creation & Custom Recor await expect(recordItem).toBeVisible({ timeout: 10000 }); // 5. Reload and verify persistence from IndexedDB - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const recordItemAfterReload = isMobile ? page.locator('[data-testid="mobile-table-cards"]').locator(`text=${recordTitle}`).first() diff --git a/e2e/08-view-builder.spec.ts b/e2e/08-view-builder.spec.ts index 59d1da4..860b2e8 100644 --- a/e2e/08-view-builder.spec.ts +++ b/e2e/08-view-builder.spec.ts @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; test.describe('Scenario 08: View Builder & Column Reordering', () => { test('reorders columns in View Builder, saves view, and verifies persistence across reload', async ({ page }) => { - await page.goto('/settings/entities/deal/views'); + await page.goto('/settings/entities/deal/views', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Wait for view builder to load @@ -35,7 +35,7 @@ test.describe('Scenario 08: View Builder & Column Reordering', () => { await expect(page.locator('text=сохранено').first()).toBeVisible({ timeout: 10000 }); // Reload page to verify persistence - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); await expect(page.locator('text=Конструктор представлений').first()).toBeVisible({ timeout: 15000 }); diff --git a/e2e/09-rbac.spec.ts b/e2e/09-rbac.spec.ts index 7a56e44..c2d0d9c 100644 --- a/e2e/09-rbac.spec.ts +++ b/e2e/09-rbac.spec.ts @@ -29,7 +29,7 @@ test.describe('Scenario 09: RBAC Guard & 403 Forbidden Handling', () => { } // 2. Navigate to protected route /finance (Cashier has no permission to Finance) - await page.goto('/finance'); + await page.goto('/finance', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // 3. Verify 403 Forbidden page is displayed @@ -43,7 +43,7 @@ test.describe('Scenario 09: RBAC Guard & 403 Forbidden Handling', () => { await switchToOwnerBtn.click(); // 5. Navigate back to /finance and verify access restored - await page.goto('/finance'); + await page.goto('/finance', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Finance content should now be visible without 403 diff --git a/e2e/10-indexeddb-snapshots.spec.ts b/e2e/10-indexeddb-snapshots.spec.ts index c267f31..74873e5 100644 --- a/e2e/10-indexeddb-snapshots.spec.ts +++ b/e2e/10-indexeddb-snapshots.spec.ts @@ -35,7 +35,7 @@ test.describe('Scenario 10: IndexedDB Snapshots (Export, Reset, Import)', () => } // 5. Reload page to ensure restored state persists - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const retryBtn = page.locator('button:has-text("Попробовать снова")'); diff --git a/e2e/11-modules-catalog.spec.ts b/e2e/11-modules-catalog.spec.ts index e5bc7e3..908768c 100644 --- a/e2e/11-modules-catalog.spec.ts +++ b/e2e/11-modules-catalog.spec.ts @@ -83,7 +83,7 @@ test.describe('Scenario 11: Universal Modules Catalog & Profiles', () => { await page.waitForURL('**/crm'); await expect(page.locator('h1, h2').first()).toBeVisible(); - await page.goto('/'); + await page.goto('/', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const procOpenBtn = page.locator('[data-testid="module-open-btn-procurement"]'); @@ -119,7 +119,7 @@ test.describe('Scenario 11: Universal Modules Catalog & Profiles', () => { await retailBtn.click(); await expect(page.locator('[data-testid="recommended-section"]')).toBeVisible(); - await page.goto('/settings/entities'); + await page.goto('/settings/entities', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const resetBtn = page.getByRole('button', { name: /Сбросить базу данных/i }); @@ -133,7 +133,7 @@ test.describe('Scenario 11: Universal Modules Catalog & Profiles', () => { await page.waitForTimeout(1000); - await page.goto('/'); + await page.goto('/', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const allProfileBtn = page.locator('[data-testid="profile-filter-all"]'); diff --git a/e2e/12-mobile-first.spec.ts b/e2e/12-mobile-first.spec.ts index 4060a7f..e956a08 100644 --- a/e2e/12-mobile-first.spec.ts +++ b/e2e/12-mobile-first.spec.ts @@ -105,7 +105,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () = expect(isOverflowing).toBe(false); // Verify on /crm - await page.goto('/crm'); + await page.goto('/crm', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const isCrmOverflowing = await page.evaluate(() => { return document.documentElement.scrollWidth > document.documentElement.clientWidth; @@ -113,7 +113,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () = expect(isCrmOverflowing).toBe(false); // Verify on /settings/entities - await page.goto('/settings/entities'); + await page.goto('/settings/entities', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const isSettingsOverflowing = await page.evaluate(() => { return document.documentElement.scrollWidth > document.documentElement.clientWidth; @@ -122,7 +122,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () = }); test('5. DynamicTable renders responsive card list on mobile screens', async ({ page, viewport }) => { - await page.goto('/crm'); + await page.goto('/crm', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); const isMobileViewport = viewport && viewport.width < 768; @@ -136,7 +136,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () = }); test('6. FormBuilder supports touch-friendly up and down field reordering', async ({ page }) => { - await page.goto('/settings/entities/contact/form-builder'); + await page.goto('/settings/entities/contact/form-builder', { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('domcontentloaded'); // Locate field move buttons @@ -170,7 +170,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () = window.localStorage.setItem('krona_theme_mode', 'light'); document.documentElement.setAttribute('data-theme', 'light'); }); - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); let currentTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme')); expect(currentTheme).toBe('light'); @@ -179,7 +179,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () = window.localStorage.setItem('krona_theme_mode', 'dark'); document.documentElement.setAttribute('data-theme', 'dark'); }); - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); currentTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme')); expect(currentTheme).toBe('dark'); @@ -188,7 +188,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () = window.localStorage.setItem('krona_theme_mode', 'soft-light'); document.documentElement.setAttribute('data-theme', 'soft-light'); }); - await page.reload(); + await page.reload({ waitUntil: 'domcontentloaded' }); currentTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme')); expect(currentTheme).toBe('soft-light'); });