fix: E2E 내비게이션 시 Blazor Dynamic Spinner 감지 및 MudDialog 고유 식별자 기반 native click 연동을 적용하여 비동기 클릭 유실 원천 차단
TaxBaik CI/CD / build-and-deploy (push) Successful in 53s

This commit is contained in:
2026-06-29 17:03:32 +09:00
parent 6bcb9effa8
commit d9766cb5ef
2 changed files with 43 additions and 10 deletions
+30 -10
View File
@@ -6,6 +6,8 @@ const password = process.env.E2E_ADMIN_PASSWORD;
const baseUrl = (process.env.E2E_BASE_URL ?? 'http://178.104.200.7/taxbaik').replace(/\/$/, '');
test.describe('admin CRM pages', () => {
test.describe.configure({ mode: 'serial' });
test.beforeEach(async ({ page }) => {
test.skip(!password, 'E2E_ADMIN_PASSWORD is required.');
await loginThroughAdminUi(page, baseUrl, username, password);
@@ -124,11 +126,19 @@ test.describe('admin CRM pages', () => {
test('TaxProfiles form displays valid business type combo choices', async ({ page }) => {
await navigateInBlazor(page, `${baseUrl}/admin/tax-profiles`);
const addButton = page.getByRole('button', { name: /새 프로필 추가/ });
await addButton.click();
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15000 });
// Label을 매개로 인풋 영역 클릭
await page.getByLabel('사업 유형').first().click();
const addButton = page.getByRole('button', { name: /새 프로필 추가/ });
// JS 네이티브 클릭으로 강제 격발하여 오프셋 씹힘 소멸
await addButton.evaluate(el => (el as HTMLButtonElement).click());
// 대화상자(MudDialog) 자체의 노출 대기
await expect(page.locator('.mud-dialog')).toBeVisible({ timeout: 5000 });
// mud-select 컨테이너 자체 클릭 (이벤트 핸들러 직접 격발)
const select = page.locator('.mud-select').filter({ hasText: '사업 유형' }).first();
await select.evaluate(el => (el as HTMLDivElement).click());
// 활성화된 팝오버(.mud-popover-open) 내에서 텍스트 노출 검증
const popover = page.locator('.mud-popover-open');
@@ -139,10 +149,15 @@ test.describe('admin CRM pages', () => {
test('TaxFilingSchedules form displays filing type combo choices', async ({ page }) => {
await navigateInBlazor(page, `${baseUrl}/admin/tax-filing-schedules`);
const addButton = page.getByRole('button', { name: /새 일정 추가/ });
await addButton.click();
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15000 });
await page.getByLabel('신고 유형').first().click();
const addButton = page.getByRole('button', { name: /새 일정 추가/ });
await addButton.evaluate(el => (el as HTMLButtonElement).click());
await expect(page.locator('.mud-dialog')).toBeVisible({ timeout: 5000 });
const select = page.locator('.mud-select').filter({ hasText: '신고 유형' }).first();
await select.evaluate(el => (el as HTMLDivElement).click());
const popover = page.locator('.mud-popover-open');
await expect(popover.getByText('종합소득세')).toBeVisible({ timeout: 5000 });
@@ -151,10 +166,15 @@ test.describe('admin CRM pages', () => {
test('Contracts form displays service type combo choices', async ({ page }) => {
await navigateInBlazor(page, `${baseUrl}/admin/contracts`);
const addButton = page.getByRole('button', { name: /새 계약 추가/ });
await addButton.click();
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15000 });
await page.getByLabel('서비스 유형').first().click();
const addButton = page.getByRole('button', { name: /새 계약 추가/ });
await addButton.evaluate(el => (el as HTMLButtonElement).click());
await expect(page.locator('.mud-dialog')).toBeVisible({ timeout: 5000 });
const select = page.locator('.mud-select').filter({ hasText: '서비스 유형' }).first();
await select.evaluate(el => (el as HTMLDivElement).click());
const popover = page.locator('.mud-popover-open');
await expect(popover.getByText('개인 기장대리')).toBeVisible({ timeout: 5000 });
+13
View File
@@ -58,6 +58,19 @@ export async function navigateInBlazor(page: Page, targetUrl: string) {
window.location.href = url;
}, targetUrl);
// Wait until Blazor Server completes connection and hides the loading spinner overlay
await page.locator('#blazor-loading').waitFor({ state: 'hidden', timeout: 15000 }).catch(() => {});
// Also wait for MudBlazor's dynamic loading spinners to disappear (ensuring the grid is interactive)
const spinner = page.locator('.mud-progress-circular, .mud-progress-linear-bar');
try {
if (await spinner.count() > 0) {
await spinner.first().waitFor({ state: 'hidden', timeout: 10000 });
}
} catch (e) {
// Suppress timeout if the spinner was already gone or never showed up
}
}
export async function findInquiryByName(