forked from baron/baron-sso
test verify-only approval close routing
This commit is contained in:
@@ -3,6 +3,8 @@ import { expect, test, type Page, type Route } from '@playwright/test';
|
||||
type MockOptions = {
|
||||
sessionStatus?: number;
|
||||
captureApprove?: (pendingRef: string | null) => void;
|
||||
captureUserMe?: () => void;
|
||||
captureVerify?: (path: string, body: Record<string, unknown>) => void;
|
||||
};
|
||||
|
||||
async function seedTokenLogin(page: Page): Promise<void> {
|
||||
@@ -33,11 +35,12 @@ async function mockUserfrontApis(
|
||||
): Promise<void> {
|
||||
const sessionStatus = options.sessionStatus ?? 200;
|
||||
|
||||
await page.route('**/api/v1/**', async (route: Route) => {
|
||||
await page.context().route('**/api/v1/**', async (route: Route) => {
|
||||
const requestUrl = new URL(route.request().url());
|
||||
const path = requestUrl.pathname;
|
||||
|
||||
if (path.endsWith('/api/v1/user/me')) {
|
||||
options.captureUserMe?.();
|
||||
if (sessionStatus === 200) {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
@@ -117,6 +120,13 @@ async function mockUserfrontApis(
|
||||
path.endsWith('/api/v1/auth/login/code/verify') ||
|
||||
path.endsWith('/api/v1/auth/login/code/verify-short')
|
||||
) {
|
||||
let body: Record<string, unknown> = {};
|
||||
try {
|
||||
body = (route.request().postDataJSON() ?? {}) as Record<string, unknown>;
|
||||
} catch {
|
||||
body = {};
|
||||
}
|
||||
options.captureVerify?.(path, body);
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@@ -196,13 +206,35 @@ test.describe('UserFront WASM auth routing', () => {
|
||||
test('verifyOnly 승인 완료 화면의 상단 액션은 signin으로 이동시키지 않는다', async ({
|
||||
page,
|
||||
}) => {
|
||||
await mockUserfrontApis(page, { sessionStatus: 401 });
|
||||
let userMeCalls = 0;
|
||||
const verifyRequests: Array<{
|
||||
path: string;
|
||||
body: Record<string, unknown>;
|
||||
}> = [];
|
||||
|
||||
await mockUserfrontApis(page, {
|
||||
sessionStatus: 401,
|
||||
captureUserMe: () => {
|
||||
userMeCalls += 1;
|
||||
},
|
||||
captureVerify: (path, body) => {
|
||||
verifyRequests.push({ path, body });
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/ko/l/AB123456');
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/l\/AB123456$/);
|
||||
await page.waitForTimeout(500);
|
||||
await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
|
||||
await expect(page).toHaveURL(/\/ko\/l\/AB123456$/);
|
||||
expect(userMeCalls).toBe(0);
|
||||
expect(verifyRequests[0].path).toContain(
|
||||
'/api/v1/auth/login/code/verify-short',
|
||||
);
|
||||
expect(verifyRequests[0].body).toMatchObject({
|
||||
shortCode: 'AB123456',
|
||||
verifyOnly: true,
|
||||
});
|
||||
|
||||
await page.locator('flt-glass-pane').click({
|
||||
position: { x: 30, y: 28 },
|
||||
@@ -212,4 +244,188 @@ test.describe('UserFront WASM auth routing', () => {
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/l\/AB123456$/);
|
||||
});
|
||||
|
||||
test('verifyOnly 승인 완료 버튼은 SMS 링크에서 user/me 조회나 루트 이동을 만들지 않는다', async ({
|
||||
page,
|
||||
}) => {
|
||||
let userMeCalls = 0;
|
||||
let verifyCalls = 0;
|
||||
|
||||
await mockUserfrontApis(page, {
|
||||
sessionStatus: 401,
|
||||
captureUserMe: () => {
|
||||
userMeCalls += 1;
|
||||
},
|
||||
captureVerify: () => {
|
||||
verifyCalls += 1;
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/ko/l/AB123456');
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/l\/AB123456$/);
|
||||
await expect.poll(() => verifyCalls, { timeout: 10_000 }).toBe(1);
|
||||
expect(userMeCalls).toBe(0);
|
||||
|
||||
const viewport = page.viewportSize();
|
||||
if (!viewport) throw new Error('viewport is required');
|
||||
await page.locator('flt-glass-pane').click({
|
||||
position: {
|
||||
x: Math.floor(viewport.width / 2),
|
||||
y: Math.floor(viewport.height * 0.66),
|
||||
},
|
||||
force: true,
|
||||
});
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
expect(userMeCalls).toBe(0);
|
||||
await expect(page).toHaveURL(/\/ko\/l\/AB123456$/);
|
||||
});
|
||||
|
||||
test('verifyOnly 승인 링크를 팝업에서 닫으면 창만 닫히고 부모는 이동하지 않는다', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
let userMeCalls = 0;
|
||||
let verifyCalls = 0;
|
||||
|
||||
await mockUserfrontApis(page, {
|
||||
sessionStatus: 401,
|
||||
captureUserMe: () => {
|
||||
userMeCalls += 1;
|
||||
},
|
||||
captureVerify: () => {
|
||||
verifyCalls += 1;
|
||||
},
|
||||
});
|
||||
|
||||
const baseURL = testInfo.project.use.baseURL;
|
||||
if (typeof baseURL !== 'string') throw new Error('baseURL is required');
|
||||
const popupURL = new URL('/ko/l/AB123456', baseURL).toString();
|
||||
|
||||
await page.goto('about:blank');
|
||||
await expect(page).toHaveURL('about:blank');
|
||||
|
||||
const popupPromise = page.waitForEvent('popup');
|
||||
await page.evaluate((url) => {
|
||||
window.open(url, '_blank');
|
||||
}, popupURL);
|
||||
const popup = await popupPromise;
|
||||
|
||||
await expect(popup).toHaveURL(/\/ko\/l\/AB123456$/);
|
||||
await expect.poll(() => verifyCalls, { timeout: 10_000 }).toBe(1);
|
||||
expect(userMeCalls).toBe(0);
|
||||
|
||||
const viewport = popup.viewportSize();
|
||||
if (!viewport) throw new Error('viewport is required');
|
||||
const closePromise = popup.waitForEvent('close');
|
||||
await popup.locator('flt-glass-pane').click({
|
||||
position: {
|
||||
x: Math.floor(viewport.width / 2),
|
||||
y: Math.floor(viewport.height * 0.66),
|
||||
},
|
||||
force: true,
|
||||
});
|
||||
await closePromise;
|
||||
|
||||
expect(userMeCalls).toBe(0);
|
||||
await expect(page).toHaveURL('about:blank');
|
||||
});
|
||||
|
||||
test('verifyOnly 승인 완료 버튼은 이메일 magic link에서도 user/me 조회나 루트 이동을 만들지 않는다', async ({
|
||||
page,
|
||||
}) => {
|
||||
let userMeCalls = 0;
|
||||
const verifyRequests: Array<{
|
||||
path: string;
|
||||
body: Record<string, unknown>;
|
||||
}> = [];
|
||||
|
||||
await mockUserfrontApis(page, {
|
||||
sessionStatus: 401,
|
||||
captureUserMe: () => {
|
||||
userMeCalls += 1;
|
||||
},
|
||||
captureVerify: (path, body) => {
|
||||
verifyRequests.push({ path, body });
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/ko/verify/e2e-email-token');
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/verify\/e2e-email-token$/);
|
||||
await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
|
||||
expect(userMeCalls).toBe(0);
|
||||
expect(verifyRequests[0].path).toContain('/api/v1/auth/magic-link/verify');
|
||||
expect(verifyRequests[0].body).toMatchObject({
|
||||
token: 'e2e-email-token',
|
||||
verifyOnly: true,
|
||||
});
|
||||
|
||||
const viewport = page.viewportSize();
|
||||
if (!viewport) throw new Error('viewport is required');
|
||||
await page.locator('flt-glass-pane').click({
|
||||
position: {
|
||||
x: Math.floor(viewport.width / 2),
|
||||
y: Math.floor(viewport.height * 0.66),
|
||||
},
|
||||
force: true,
|
||||
});
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
expect(userMeCalls).toBe(0);
|
||||
await expect(page).toHaveURL(/\/ko\/verify\/e2e-email-token$/);
|
||||
});
|
||||
|
||||
test('verifyOnly 승인 완료 버튼은 이메일 code link에서도 user/me 조회나 루트 이동을 만들지 않는다', async ({
|
||||
page,
|
||||
}) => {
|
||||
let userMeCalls = 0;
|
||||
const verifyRequests: Array<{
|
||||
path: string;
|
||||
body: Record<string, unknown>;
|
||||
}> = [];
|
||||
|
||||
await mockUserfrontApis(page, {
|
||||
sessionStatus: 401,
|
||||
captureUserMe: () => {
|
||||
userMeCalls += 1;
|
||||
},
|
||||
captureVerify: (path, body) => {
|
||||
verifyRequests.push({ path, body });
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto(
|
||||
'/ko/verify?loginId=e2e%40example.com&code=654321&pendingRef=pending-email',
|
||||
);
|
||||
|
||||
await expect(page).toHaveURL(
|
||||
/\/ko\/verify\?loginId=e2e(?:%40|@)example\.com&code=654321&pendingRef=pending-email$/,
|
||||
);
|
||||
await expect.poll(() => verifyRequests.length, { timeout: 10_000 }).toBe(1);
|
||||
expect(userMeCalls).toBe(0);
|
||||
expect(verifyRequests[0].path).toContain('/api/v1/auth/login/code/verify');
|
||||
expect(verifyRequests[0].body).toMatchObject({
|
||||
loginId: 'e2e@example.com',
|
||||
code: '654321',
|
||||
pendingRef: 'pending-email',
|
||||
verifyOnly: true,
|
||||
});
|
||||
|
||||
const viewport = page.viewportSize();
|
||||
if (!viewport) throw new Error('viewport is required');
|
||||
await page.locator('flt-glass-pane').click({
|
||||
position: {
|
||||
x: Math.floor(viewport.width / 2),
|
||||
y: Math.floor(viewport.height * 0.66),
|
||||
},
|
||||
force: true,
|
||||
});
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
expect(userMeCalls).toBe(0);
|
||||
await expect(page).toHaveURL(
|
||||
/\/ko\/verify\?loginId=e2e(?:%40|@)example\.com&code=654321&pendingRef=pending-email$/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user