fix(e2e): use domcontentloaded navigation across all test suites to prevent preview server load timeouts

This commit is contained in:
ochenstarik 2026-09-11 12:28:41 +07:00
parent e40b935f43
commit 0c479f568c
12 changed files with 32 additions and 28 deletions

View file

@ -9,7 +9,7 @@ test.describe('Scenario 01: Dashboard Integrity & Viewport Matrix', () => {
} }
}); });
await page.goto('/'); await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
// Verify dashboard title / header // Verify dashboard title / header

View file

@ -37,7 +37,7 @@ test.describe('Scenario 02: Theme Engine & UI Persistence', () => {
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark'); await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
// 2. Reload and verify Dark Theme persistence // 2. Reload and verify Dark Theme persistence
await page.reload(); await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark'); 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'); await expect(page.locator('html')).toHaveAttribute('data-theme', 'soft-light');
// 5. Reload and verify Soft Light Theme persistence // 5. Reload and verify Soft Light Theme persistence
await page.reload(); await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
await expect(page.locator('html')).toHaveAttribute('data-theme', 'soft-light'); await expect(page.locator('html')).toHaveAttribute('data-theme', 'soft-light');
}); });

View file

@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test';
test.describe('Scenario 03: CRM Kanban Drag-and-Drop & Stage Persistence', () => { 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 }) => { 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'); await page.waitForLoadState('domcontentloaded');
// Switch to Deals tab if needed // 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 // Pick first available deal card
const firstCard = dealCards.first(); const firstCard = dealCards.first();
await firstCard.scrollIntoViewIfNeeded().catch(() => {});
const dealId = await firstCard.getAttribute('data-deal-id'); const dealId = await firstCard.getAttribute('data-deal-id');
expect(dealId).toBeTruthy(); expect(dealId).toBeTruthy();
const targetStage = 'negotiation'; const targetStage = 'negotiation';
const targetColumn = page.locator(`[data-testid="kanban-column-${targetStage}"]`); const targetColumn = page.locator(`[data-testid="kanban-column-${targetStage}"]`);
await targetColumn.scrollIntoViewIfNeeded().catch(() => {});
await expect(targetColumn).toBeVisible(); await expect(targetColumn).toBeVisible();
// Perform Drag and Drop (with short timeout and fallback to transition button if intercepted) // 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)) { if (!await cardInTarget.isVisible({ timeout: 2000 }).catch(() => false)) {
const nextBtn = page.locator(`[data-testid="deal-card-${dealId}"] button:has-text("Далее:")`).first(); const nextBtn = page.locator(`[data-testid="deal-card-${dealId}"] button:has-text("Далее:")`).first();
if (await nextBtn.isVisible({ timeout: 2000 }).catch(() => false)) { if (await nextBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
await nextBtn.scrollIntoViewIfNeeded().catch(() => {});
await nextBtn.click({ force: true }).catch(() => {}); await nextBtn.click({ force: true }).catch(() => {});
} }
} }
// Reload page to verify persistence in IndexedDB // Reload page to verify persistence in IndexedDB
await page.reload(); await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
// Ensure the card still exists and its updated stage persisted // Ensure the card still exists and its updated stage persisted
const persistedCard = page.locator(`[data-testid="deal-card-${dealId}"]`); const persistedCard = page.locator(`[data-testid="deal-card-${dealId}"]`);
await persistedCard.scrollIntoViewIfNeeded().catch(() => {});
await expect(persistedCard).toBeVisible({ timeout: 15000 }); await expect(persistedCard).toBeVisible({ timeout: 15000 });
}); });
}); });

View file

@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test';
test.describe('Scenario 04: CRM Lead Creation & Full Conversion Flow', () => { 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 }) => { 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'); await page.waitForLoadState('domcontentloaded');
// Ensure Leads tab is active // Ensure Leads tab is active

View file

@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
test.describe('Scenario 05: CRM Deal Won -> Commercial Order Flow', () => { 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('advances deal to Won, confirms linked Order creation, and verifies order in Sales', async ({ page }) => {
test.setTimeout(60000); test.setTimeout(60000);
await page.goto('/crm/deals'); await page.goto('/crm/deals', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
// Switch to Deals tab if needed // Switch to Deals tab if needed
@ -39,7 +39,7 @@ test.describe('Scenario 05: CRM Deal Won -> Commercial Order Flow', () => {
await page.waitForTimeout(500); await page.waitForTimeout(500);
// Navigate to Sales module // Navigate to Sales module
await page.goto('/sales'); await page.goto('/sales', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
// Verify order exists in the Commerce Orders table // Verify order exists in the Commerce Orders table

View file

@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test';
test.describe('Scenario 06: Commercial Order Fulfillment (Confirm -> Payment -> Shipment)', () => { 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 }) => { 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'); await page.waitForLoadState('domcontentloaded');
// 1. Select an order from the list // 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 // 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 page.waitForLoadState('domcontentloaded');
await expect(page.locator('main').locator('text=Финансы').or(page.locator('main').locator('text=ДДС')).first()).toBeVisible({ timeout: 15000 }); 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 }); await expect(page.locator('tbody tr').first()).toBeVisible({ timeout: 10000 });
// 6. Navigate to Inventory: Verify warehouse stock view in main area // 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 page.waitForLoadState('domcontentloaded');
await expect(page.locator('main').locator('text=Склад').or(page.locator('main').locator('text=Остатки')).first()).toBeVisible({ timeout: 15000 }); await expect(page.locator('main').locator('text=Склад').or(page.locator('main').locator('text=Остатки')).first()).toBeVisible({ timeout: 15000 });
}); });

View file

@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test';
test.describe('Scenario 07: Configuration Engine (Entity Creation & Custom Records)', () => { 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 }) => { 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'); await page.waitForLoadState('domcontentloaded');
// 1. Open Create Entity Modal // 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 }); await expect(recordItem).toBeVisible({ timeout: 10000 });
// 5. Reload and verify persistence from IndexedDB // 5. Reload and verify persistence from IndexedDB
await page.reload(); await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
const recordItemAfterReload = isMobile const recordItemAfterReload = isMobile
? page.locator('[data-testid="mobile-table-cards"]').locator(`text=${recordTitle}`).first() ? page.locator('[data-testid="mobile-table-cards"]').locator(`text=${recordTitle}`).first()

View file

@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test';
test.describe('Scenario 08: View Builder & Column Reordering', () => { test.describe('Scenario 08: View Builder & Column Reordering', () => {
test('reorders columns in View Builder, saves view, and verifies persistence across reload', async ({ page }) => { 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'); await page.waitForLoadState('domcontentloaded');
// Wait for view builder to load // 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 }); await expect(page.locator('text=сохранено').first()).toBeVisible({ timeout: 10000 });
// Reload page to verify persistence // Reload page to verify persistence
await page.reload(); await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
await expect(page.locator('text=Конструктор представлений').first()).toBeVisible({ timeout: 15000 }); await expect(page.locator('text=Конструктор представлений').first()).toBeVisible({ timeout: 15000 });

View file

@ -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) // 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'); await page.waitForLoadState('domcontentloaded');
// 3. Verify 403 Forbidden page is displayed // 3. Verify 403 Forbidden page is displayed
@ -43,7 +43,7 @@ test.describe('Scenario 09: RBAC Guard & 403 Forbidden Handling', () => {
await switchToOwnerBtn.click(); await switchToOwnerBtn.click();
// 5. Navigate back to /finance and verify access restored // 5. Navigate back to /finance and verify access restored
await page.goto('/finance'); await page.goto('/finance', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
// Finance content should now be visible without 403 // Finance content should now be visible without 403

View file

@ -35,7 +35,7 @@ test.describe('Scenario 10: IndexedDB Snapshots (Export, Reset, Import)', () =>
} }
// 5. Reload page to ensure restored state persists // 5. Reload page to ensure restored state persists
await page.reload(); await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
const retryBtn = page.locator('button:has-text("Попробовать снова")'); const retryBtn = page.locator('button:has-text("Попробовать снова")');

View file

@ -83,7 +83,7 @@ test.describe('Scenario 11: Universal Modules Catalog & Profiles', () => {
await page.waitForURL('**/crm'); await page.waitForURL('**/crm');
await expect(page.locator('h1, h2').first()).toBeVisible(); await expect(page.locator('h1, h2').first()).toBeVisible();
await page.goto('/'); await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
const procOpenBtn = page.locator('[data-testid="module-open-btn-procurement"]'); 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 retailBtn.click();
await expect(page.locator('[data-testid="recommended-section"]')).toBeVisible(); 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'); await page.waitForLoadState('domcontentloaded');
const resetBtn = page.getByRole('button', { name: /Сбросить базу данных/i }); 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.waitForTimeout(1000);
await page.goto('/'); await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
const allProfileBtn = page.locator('[data-testid="profile-filter-all"]'); const allProfileBtn = page.locator('[data-testid="profile-filter-all"]');

View file

@ -105,7 +105,7 @@ test.describe('Scenario 12: Mobile-First and Tablet Architecture Overhaul', () =
expect(isOverflowing).toBe(false); expect(isOverflowing).toBe(false);
// Verify on /crm // Verify on /crm
await page.goto('/crm'); await page.goto('/crm', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
const isCrmOverflowing = await page.evaluate(() => { const isCrmOverflowing = await page.evaluate(() => {
return document.documentElement.scrollWidth > document.documentElement.clientWidth; 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); expect(isCrmOverflowing).toBe(false);
// Verify on /settings/entities // Verify on /settings/entities
await page.goto('/settings/entities'); await page.goto('/settings/entities', { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('domcontentloaded');
const isSettingsOverflowing = await page.evaluate(() => { const isSettingsOverflowing = await page.evaluate(() => {
return document.documentElement.scrollWidth > document.documentElement.clientWidth; 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 }) => { 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'); await page.waitForLoadState('domcontentloaded');
const isMobileViewport = viewport && viewport.width < 768; 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 }) => { 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'); await page.waitForLoadState('domcontentloaded');
// Locate field move buttons // 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'); window.localStorage.setItem('krona_theme_mode', 'light');
document.documentElement.setAttribute('data-theme', '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')); let currentTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme'));
expect(currentTheme).toBe('light'); 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'); window.localStorage.setItem('krona_theme_mode', 'dark');
document.documentElement.setAttribute('data-theme', 'dark'); document.documentElement.setAttribute('data-theme', 'dark');
}); });
await page.reload(); await page.reload({ waitUntil: 'domcontentloaded' });
currentTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme')); currentTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme'));
expect(currentTheme).toBe('dark'); 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'); window.localStorage.setItem('krona_theme_mode', 'soft-light');
document.documentElement.setAttribute('data-theme', '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')); currentTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme'));
expect(currentTheme).toBe('soft-light'); expect(currentTheme).toBe('soft-light');
}); });