[cs frontend] 세션연장 테스트 케이스 추가
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import { useSessionExtendConfirm } from "../../../../../../packages/common/components/src/lib/use-session-extend-confirm";
|
||||
import { useState, useRef } from "react";
|
||||
|
||||
export default function TestSessionPage() {
|
||||
const [refreshCalled, setRefreshCalled] = useState(false);
|
||||
const [declineCalled, setDeclineCalled] = useState(false);
|
||||
|
||||
// 클로저 트랩 회피를 위한 Ref 선언
|
||||
const refreshCalledRef = useRef(false);
|
||||
const declineCalledRef = useRef(false);
|
||||
|
||||
useSessionExtendConfirm({
|
||||
enabled: true,
|
||||
getAccessToken: () => {
|
||||
// Ref 참조를 통해 최신의 갱신 상태를 즉각 반영하여 클로저 락 회피
|
||||
if (refreshCalledRef.current || declineCalledRef.current) {
|
||||
// 리프레시/거절 완료 후에는 만료 시간이 아주 충분히 남은 가상 토큰 반환 (1000초 남음)
|
||||
const payload = btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 1000 }));
|
||||
return `header.${payload}.signature`;
|
||||
}
|
||||
// 최초 실행 시에는 5분 버퍼(300초) 진입까지 정확히 2초가 남은 가상 토큰 반환 (302초 남음)
|
||||
const payload = btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 302 }));
|
||||
return `header.${payload}.signature`;
|
||||
},
|
||||
silentRefresh: async () => {
|
||||
refreshCalledRef.current = true;
|
||||
setRefreshCalled(true);
|
||||
},
|
||||
declineSessionExtend: () => {
|
||||
declineCalledRef.current = true;
|
||||
setDeclineCalled(true);
|
||||
},
|
||||
message: "로그인을 연장하시겠습니까?",
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ padding: "50px", textAlign: "center" }}>
|
||||
<h1>Session Extend Confirm Test Page</h1>
|
||||
<p>이 페이지는 useSessionExtendConfirm 훅 E2E 테스팅을 위한 격리 라우트입니다.</p>
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
{refreshCalled && <p data-testid="refresh-status">Refresh Successful</p>}
|
||||
{declineCalled && <p data-testid="decline-status">Decline Successful</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
const adminId = process.env.TEST_ADMIN_ID || "admin1gmail";
|
||||
const adminPassword = process.env.TEST_ADMIN_PW || "test1234!";
|
||||
|
||||
test.describe("세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트", () => {
|
||||
|
||||
test("1. 세션 만료 5분(버퍼 시간) 전 도달 시 컨펌 다이얼로그가 자동 노출되고, 승인 시 연장 처리가 수행되어야 함", async ({ page }) => {
|
||||
// 1. 로그인 수행하여 정상 세션 획득
|
||||
await page.goto("/auth/login");
|
||||
await page.fill("input#id", adminId);
|
||||
await page.fill("input#password", adminPassword);
|
||||
await page.click("button.login_btn");
|
||||
await page.waitForURL("**/dashboard");
|
||||
// 내비게이션 및 세션 적재의 미세 안정화를 위해 짧은 대기 추가
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// 2. 로그인된 상태에서 격리 테스팅 라우트로 이동
|
||||
await page.goto("/test-session");
|
||||
await page.waitForURL("**/test-session");
|
||||
|
||||
// 3. 2초 뒤 만료 버퍼 도달 시점에 노출되는 리액트 HTML 모달 텍스트 대기
|
||||
const messageLocator = page.locator("p", { hasText: "로그인을 연장하시겠습니까?" });
|
||||
await expect(messageLocator).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// 4. 모달 내부의 "확인" 버튼 클릭
|
||||
const confirmButton = page.locator("button", { hasText: "확인" });
|
||||
await confirmButton.click();
|
||||
|
||||
// 5. 모달이 정상적으로 닫혔는지 검증
|
||||
await expect(messageLocator).toBeHidden();
|
||||
|
||||
// 6. silentRefresh 성공 시 나타나는 엘리먼트 텍스트 검증
|
||||
const statusText = page.locator("[data-testid='refresh-status']");
|
||||
await expect(statusText).toHaveText("Refresh Successful");
|
||||
|
||||
// 7. 세션 연장 처리 완료 후 추가 시간이 흐르더라도 다음 팝업이 즉시 중복 재호출되지 않아야 함
|
||||
await page.waitForTimeout(2000);
|
||||
await expect(messageLocator).toBeHidden();
|
||||
});
|
||||
|
||||
test("2. 컨펌 다이얼로그를 거절(Dismiss)한 경우 세션 연장 처리가 취소되고 재차 팝업이 노출되지 않아야 함", async ({ page }) => {
|
||||
// 1. 로그인 수행하여 정상 세션 획득
|
||||
await page.goto("/auth/login");
|
||||
await page.fill("input#id", adminId);
|
||||
await page.fill("input#password", adminPassword);
|
||||
await page.click("button.login_btn");
|
||||
await page.waitForURL("**/dashboard");
|
||||
// 내비게이션 및 세션 적재의 미세 안정화를 위해 짧은 대기 추가
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// 2. 로그인된 상태에서 격리 테스팅 라우트로 이동
|
||||
await page.goto("/test-session");
|
||||
await page.waitForURL("**/test-session");
|
||||
|
||||
// 3. 컨펌 모달 대기
|
||||
const messageLocator = page.locator("p", { hasText: "로그인을 연장하시겠습니까?" });
|
||||
await expect(messageLocator).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// 4. 모달 내부의 "취소" 버튼 클릭
|
||||
const cancelButton = page.locator("button", { hasText: "취소" });
|
||||
await cancelButton.click();
|
||||
|
||||
// 5. 모달이 정상적으로 닫혔는지 확인
|
||||
await expect(messageLocator).toBeHidden();
|
||||
|
||||
// 6. declineSessionExtend 성공 시 나타나는 엘리먼트 텍스트 검증
|
||||
const statusText = page.locator("[data-testid='decline-status']");
|
||||
await expect(statusText).toHaveText("Decline Successful");
|
||||
|
||||
// 7. 거절 이후 추가 시간이 흘러도 모달이 중복 재노출되지 않아야 함
|
||||
await page.waitForTimeout(2000);
|
||||
await expect(messageLocator).toBeHidden();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,180 @@
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { useSessionExtendConfirm } from "../../../../packages/common/components/src/lib/use-session-extend-confirm";
|
||||
import { getRemainingMs } from "@common/auth";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
|
||||
// useConfirm 모킹
|
||||
const mockConfirm = vi.fn();
|
||||
vi.mock("@frontend-opensource/use-react-hooks", () => ({
|
||||
useConfirm: () => ({
|
||||
confirm: mockConfirm,
|
||||
}),
|
||||
}));
|
||||
|
||||
// @common/auth 모킹
|
||||
const mockGetRemainingMs = vi.fn();
|
||||
vi.mock("@common/auth", () => ({
|
||||
getRemainingMs: (...args: any[]) => mockGetRemainingMs(...args),
|
||||
SESSION_EXTEND_CONFIRM_BUFFER_MS: 300000,
|
||||
}));
|
||||
|
||||
describe("useSessionExtendConfirm 커스텀 훅 테스트 (Vitest)", () => {
|
||||
const mockGetAccessToken = vi.fn();
|
||||
const mockSilentRefresh = vi.fn();
|
||||
const mockDeclineSessionExtend = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("1. enabled가 false일 때는 어떤 타이머나 컨펌도 동작하지 않아야 함", () => {
|
||||
mockGetAccessToken.mockReturnValue("mock-token-123");
|
||||
mockGetRemainingMs.mockReturnValue(10000); // 10초 뒤 컨펌 필요
|
||||
|
||||
renderHook(() =>
|
||||
useSessionExtendConfirm({
|
||||
enabled: false,
|
||||
getAccessToken: mockGetAccessToken,
|
||||
silentRefresh: mockSilentRefresh,
|
||||
declineSessionExtend: mockDeclineSessionExtend,
|
||||
})
|
||||
);
|
||||
|
||||
// 타이머가 동작하지 않으므로 시간 경과 후에도 confirm이 실행되지 않아야 함
|
||||
vi.advanceTimersByTime(10000);
|
||||
expect(mockConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("2. 토큰 만료 버퍼 진입 전인 경우, 해당 시간만큼 대기 후 컨펌창이 나타나야 함", async () => {
|
||||
mockGetAccessToken.mockReturnValue("mock-token-123");
|
||||
// getRemainingMs(token, buffer) -> 10000ms (10초 후에 버퍼 시점 도달)
|
||||
// getRemainingMs(token, 0) -> 310000ms (토큰 유효함)
|
||||
mockGetRemainingMs.mockImplementation((token, buffer) => {
|
||||
return buffer === 0 ? 310000 : 10000;
|
||||
});
|
||||
mockConfirm.mockResolvedValue(true);
|
||||
|
||||
renderHook(() =>
|
||||
useSessionExtendConfirm({
|
||||
enabled: true,
|
||||
getAccessToken: mockGetAccessToken,
|
||||
silentRefresh: mockSilentRefresh,
|
||||
declineSessionExtend: mockDeclineSessionExtend,
|
||||
})
|
||||
);
|
||||
|
||||
// 9초 지났을 때는 아직 컨펌이 뜨지 않아야 함
|
||||
vi.advanceTimersByTime(9000);
|
||||
expect(mockConfirm).not.toHaveBeenCalled();
|
||||
|
||||
// 10초째에 진입했을 때 컨펌창 노출
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
expect(mockConfirm).toHaveBeenCalledWith("로그인을 연장하시겠습니까?");
|
||||
});
|
||||
|
||||
it("3. 컨펌창에서 연장을 승인(Accept)하면 silentRefresh가 호출되고 재스케줄링되어야 함", async () => {
|
||||
let token = "mock-token-123";
|
||||
mockGetAccessToken.mockImplementation(() => token);
|
||||
mockGetRemainingMs.mockImplementation((t, buffer) => {
|
||||
return buffer === 0 ? 310000 : 10000;
|
||||
});
|
||||
|
||||
mockConfirm.mockResolvedValue(true); // 연장 승인
|
||||
mockSilentRefresh.mockImplementation(async () => {
|
||||
token = "new-mock-token-456"; // 토큰 갱신 모사
|
||||
});
|
||||
|
||||
renderHook(() =>
|
||||
useSessionExtendConfirm({
|
||||
enabled: true,
|
||||
getAccessToken: mockGetAccessToken,
|
||||
silentRefresh: mockSilentRefresh,
|
||||
declineSessionExtend: mockDeclineSessionExtend,
|
||||
})
|
||||
);
|
||||
|
||||
// 10초 대기하여 컨펌 활성화 및 연장 승인 처리 진행
|
||||
await vi.advanceTimersByTimeAsync(10000);
|
||||
|
||||
expect(mockSilentRefresh).toHaveBeenCalled();
|
||||
// 신규 발급된 토큰("new-mock-token-456")을 기반으로 getAccessToken이 재차 호출되었는지 확인
|
||||
expect(mockGetAccessToken).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("4. 컨펌창에서 연장을 거절(Decline)하면 declineSessionExtend가 실행되고, 해당 토큰에 대해 재확인하지 않아야 함", async () => {
|
||||
mockGetAccessToken.mockReturnValue("mock-token-123");
|
||||
mockGetRemainingMs.mockImplementation((t, buffer) => {
|
||||
return buffer === 0 ? 310000 : 10000;
|
||||
});
|
||||
mockConfirm.mockResolvedValue(false); // 연장 거절
|
||||
|
||||
const { rerender } = renderHook(() =>
|
||||
useSessionExtendConfirm({
|
||||
enabled: true,
|
||||
getAccessToken: mockGetAccessToken,
|
||||
silentRefresh: mockSilentRefresh,
|
||||
declineSessionExtend: mockDeclineSessionExtend,
|
||||
})
|
||||
);
|
||||
|
||||
// 10초 대기하여 거절 실행
|
||||
await vi.advanceTimersByTimeAsync(10000);
|
||||
|
||||
expect(mockDeclineSessionExtend).toHaveBeenCalled();
|
||||
expect(mockSilentRefresh).not.toHaveBeenCalled();
|
||||
|
||||
// 거절 이후 훅이 다시 실행되어도(혹은 상태가 업데이트되어 스케줄이 재호출되어도)
|
||||
// dismissedTokenRef에 동일 토큰이 설정되어 있으므로 더 이상 컨펌창이 호출되지 않아야 함
|
||||
mockConfirm.mockClear();
|
||||
rerender();
|
||||
await vi.advanceTimersByTimeAsync(10000);
|
||||
expect(mockConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("5. 이미 만료 버퍼 이하로 남은 경우, 대기 시간 없이 즉시 컨펌창이 나타나야 함", async () => {
|
||||
mockGetAccessToken.mockReturnValue("mock-token-123");
|
||||
// getRemainingMs(token, buffer) -> 0ms (이미 버퍼 시간 이내임)
|
||||
// getRemainingMs(token, 0) -> 50000ms (토큰 자체는 살아있음)
|
||||
mockGetRemainingMs.mockImplementation((t, buffer) => {
|
||||
return buffer === 0 ? 50000 : 0;
|
||||
});
|
||||
mockConfirm.mockResolvedValue(false); // 거절 처리하여 무한 루프 방지
|
||||
|
||||
renderHook(() =>
|
||||
useSessionExtendConfirm({
|
||||
enabled: true,
|
||||
getAccessToken: mockGetAccessToken,
|
||||
silentRefresh: mockSilentRefresh,
|
||||
declineSessionExtend: mockDeclineSessionExtend,
|
||||
})
|
||||
);
|
||||
|
||||
// 타이머 흐름 대기 없이 즉시 동기/비동기 마이크로태스크 단계에서 컨펌 실행
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
expect(mockConfirm).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("6. 토큰이 아예 유효하지 않거나 이미 만료된 경우 컨펌창이 뜨지 않아야 함", () => {
|
||||
mockGetAccessToken.mockReturnValue("expired-token");
|
||||
// getRemainingMs(token, 0) -> 0ms (만료됨)
|
||||
mockGetRemainingMs.mockReturnValue(0);
|
||||
|
||||
renderHook(() =>
|
||||
useSessionExtendConfirm({
|
||||
enabled: true,
|
||||
getAccessToken: mockGetAccessToken,
|
||||
silentRefresh: mockSilentRefresh,
|
||||
declineSessionExtend: mockDeclineSessionExtend,
|
||||
})
|
||||
);
|
||||
|
||||
vi.advanceTimersByTime(10000);
|
||||
expect(mockConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -32,22 +32,59 @@ cs 애플리케이션 내의 컴포넌트 로직 검증 및 렌더링 검사 기
|
||||
* **로그인 실패 케이스**: `authService.login` API가 에러를 발생시킬 때 화면에 한글 에러 문구(`아이디와 비밀번호가 일치하지 않습니다.`)가 정상 표출되는지 검증.
|
||||
* **로그인 성공 케이스**: 정상적으로 로그인이 완료되었을 때 `window.location.href` 가 `"/dashboard"` 로 셋업되어 이동되는지 검증.
|
||||
* *Next.js `next/image` 컴포넌트 및 글로벌 `window.location`을 가상 돔 환경에 맞추어 적절하게 모킹 처리하였습니다.*
|
||||
* **[use-session-extend-confirm.test.ts](file:///Users/changwng/workspace.ona/frontend/apps/cs/tests/unit/use-session-extend-confirm.test.ts)**:
|
||||
* 세션 연장 팝업 제어용 공통 훅(`useSessionExtendConfirm`)에 대한 단위 테스트 스위트입니다.
|
||||
* **기본 타이머 대기 검증**: 토큰 만료 버퍼 도달 전까지는 컨펌창이 나타나지 않고, 해당 시점(예: 10초) 경과 후에 `confirm`이 정상 호출되는지 검증.
|
||||
* **연장 승인 시나리오**: 팝업에서 승인 시 `silentRefresh()`가 호출되고 갱신된 신규 토큰으로 재스케줄링되는지 검증.
|
||||
* **연장 거절 시나리오**: 팝업에서 거절 시 `declineSessionExtend()`가 호출되고, 해당 토큰에 대해서는 더 이상 팝업을 띄우지 않도록 무한 루프가 방지되는지 검증.
|
||||
* **즉시 노출 시나리오**: 이미 만료 버퍼 내인 경우 타이머 대기 없이 즉각적으로 컨펌창이 실행되는지 검증.
|
||||
* **미동작 예외 시나리오**: 비활성화 상태(`enabled: false`)이거나 이미 토큰이 완전히 만료된 경우 어떠한 스케줄도 잡지 않는지 검증.
|
||||
* *가상 타이머(`vi.useFakeTimers()`) 및 마이크로태스크 큐 제어를 결합하여 비동기 훅의 작동 방식을 정밀하고 빠르게 시뮬레이션하였습니다.*
|
||||
|
||||
### 4. Playwright E2E 테스트 코드 및 격리 스텁 뷰 추가
|
||||
* **[session-extend-confirm.spec.ts](file:///Users/changwng/workspace.ona/frontend/apps/cs/tests/session-extend-confirm.spec.ts)**:
|
||||
* 세션 연장 팝업의 만료 전 타이머 동작과 실제 화면 조작 및 훅 상호작용을 검증하는 브라우저 E2E 테스트 스펙입니다.
|
||||
* **승인 E2E 시나리오**: 만료 5분(버퍼 시간) 전 진입 시 리액트 HTML 모달 팝업이 노출되며, "확인" 버튼 클릭 시 리액트의 `silentRefresh()`가 트리거되고 모달이 닫히는 동작을 실 브라우저 상에서 검증.
|
||||
* **거절 E2E 시나리오**: 모달 팝업 노출 상태에서 "취소" 버튼 클릭 시 세션 연장 요청이 거절되고 모달이 닫히며, 이후 시간이 더 지나도 모달이 중복 재노출되지 않는 동작을 검증.
|
||||
* **[page.tsx](file:///Users/changwng/workspace.ona/frontend/apps/cs/app/(main)/(with-base-layout)/test-session/page.tsx)**:
|
||||
* 실시간 API와 계정/시간 몽키패칭 부작용으로 인한 브라우저 락(Lock) 및 로그인 튕김 현상을 완벽하게 예방하기 위해 구축한 **격리된 E2E 스텁 라우트**입니다.
|
||||
* `/test-session` 경로는 `ConfirmProvider`와 `ConfirmDialog` 래퍼가 마운트되는 기본 레이아웃 하위에서 작동하므로, 실 브라우저가 직접 마운트된 모달 HTML 요소를 안정적으로 감지하고 클릭할 수 있는 신뢰성 높은 환경을 제공합니다.
|
||||
|
||||
---
|
||||
|
||||
## 검증 결과
|
||||
|
||||
터미널에서 `pnpm --filter cs run test:unit` 명령을 실행하여 모든 테스트(총 7개)가 정상 통과함을 검증하였습니다:
|
||||
### 1. Vitest 단위 테스트 결과
|
||||
터미널에서 `pnpm --filter cs run test:unit` 명령을 실행하여 모든 단위 테스트(총 13개)가 정상 통과함을 검증하였습니다:
|
||||
|
||||
```bash
|
||||
RUN v4.1.10 /Users/changwng/workspace.ona/frontend/apps/cs
|
||||
|
||||
✓ tests/unit/sample-counter.test.tsx (4 tests) 89ms
|
||||
✓ tests/unit/login-page.test.tsx (3 tests) 148ms
|
||||
✓ tests/unit/use-session-extend-confirm.test.ts (6 tests) 16ms
|
||||
✓ tests/unit/sample-counter.test.tsx (4 tests) 94ms
|
||||
✓ tests/unit/login-page.test.tsx (3 tests) 152ms
|
||||
|
||||
Test Files 2 passed (2)
|
||||
Tests 7 passed (7)
|
||||
Start at 14:04:03
|
||||
Duration 718ms (transform 160ms, setup 71ms, import 243ms, tests 237ms, environment 557ms)
|
||||
Test Files 3 passed (3)
|
||||
Tests 13 passed (13)
|
||||
Start at 14:21:52
|
||||
Duration 819ms (transform 217ms, setup 109ms, import 367ms, tests 257ms, environment 1.04s)
|
||||
```
|
||||
속도 면에서 아주 빠르고 안정적으로 가상 돔 유닛 테스트 환경이 준비되었습니다.
|
||||
|
||||
### 2. Playwright E2E 테스트 결과
|
||||
터미널에서 `pnpm --filter cs exec playwright test tests/session-extend-confirm.spec.ts` 명령을 실행하여, `chromium`, `firefox`, `webkit` 3대 주요 실 브라우저 환경에서 연장 성공 시 다음 팝업 억제 검증을 포함한 세션 연장 E2E 테스트(총 6개 스위트)가 100% 정상 통과함을 확인하였습니다:
|
||||
|
||||
```bash
|
||||
Running 6 tests using 1 worker
|
||||
|
||||
✓ 1 [chromium] › tests/session-extend-confirm.spec.ts:8:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 1. 세션 만료 5분(버퍼 시간) 전 도달 시 컨펌 다이얼로그가 자동 노출되고, 승인 시 연장 처리가 수행되어야 함 (11.6s)
|
||||
✓ 2 [chromium] › tests/session-extend-confirm.spec.ts:42:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 2. 컨펌 다이얼로그를 거절(Dismiss)한 경우 세션 연장 처리가 취소되고 재차 팝업이 노출되지 않아야 함 (9.8s)
|
||||
✓ 3 [firefox] › tests/session-extend-confirm.spec.ts:8:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 1. 세션 만료 5분(버퍼 시간) 전 도달 시 컨펌 다이얼로그가 자동 노출되고, 승인 시 연장 처리가 수행되어야 함 (16.4s)
|
||||
✓ 4 [firefox] › tests/session-extend-confirm.spec.ts:42:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 2. 컨펌 다이얼로그를 거절(Dismiss)한 경우 세션 연장 처리가 취소되고 재차 팝업이 노출되지 않아야 함 (15.3s)
|
||||
✓ 5 [webkit] › tests/session-extend-confirm.spec.ts:8:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 1. 세션 만료 5분(버퍼 시간) 전 도달 시 컨펌 다이얼로그가 자동 노출되고, 승인 시 연장 처리가 수행되어야 함 (11.1s)
|
||||
✓ 6 [webkit] › tests/session-extend-confirm.spec.ts:42:7 › 세션 연장 확인 팝업 (useSessionExtendConfirm) E2E 테스트 › 2. 컨펌 다이얼로그를 거절(Dismiss)한 경우 세션 연장 처리가 취소되고 재차 팝업이 노출되지 않아야 함 (9.9s)
|
||||
|
||||
6 passed (1.3m)
|
||||
```
|
||||
이로써 단위/통합 테스트 환경과 연장 처리 성공 후 중복 팝업 억제 시나리오가 포함된 E2E 테스트 환경 구축 임무가 성공적으로 완결되었습니다.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user