forked from baron/baron-sso
e2e 구조변경
This commit is contained in:
257
userfront-e2e/tests/password-and-reset.spec.ts
Normal file
257
userfront-e2e/tests/password-and-reset.spec.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { expect, test, type Page, type Route } from '@playwright/test';
|
||||
|
||||
type RequestCapture = {
|
||||
loginBody?: Record<string, unknown>;
|
||||
resetBody?: Record<string, unknown>;
|
||||
resetToken?: string | null;
|
||||
clientLogs: string[];
|
||||
};
|
||||
|
||||
const SIGNIN_PASSWORD_TAB_X = 522;
|
||||
const SIGNIN_TAB_Y = 158;
|
||||
const SIGNIN_LOGIN_ID_X = 640;
|
||||
const SIGNIN_LOGIN_ID_Y = 245;
|
||||
const SIGNIN_PASSWORD_X = 640;
|
||||
const SIGNIN_PASSWORD_Y = 311;
|
||||
const SIGNIN_SUBMIT_X = 640;
|
||||
const SIGNIN_SUBMIT_Y = 381;
|
||||
|
||||
const RESET_NEW_PASSWORD_X = 640;
|
||||
const RESET_NEW_PASSWORD_Y = 401;
|
||||
const RESET_CONFIRM_PASSWORD_X = 640;
|
||||
const RESET_CONFIRM_PASSWORD_Y = 464;
|
||||
const RESET_SUBMIT_X = 640;
|
||||
const RESET_SUBMIT_Y = 534;
|
||||
|
||||
async function clickPasswordTab(page: Page): Promise<void> {
|
||||
await page.waitForTimeout(900);
|
||||
const pane = page.locator('flt-glass-pane');
|
||||
await pane.click({
|
||||
position: { x: SIGNIN_PASSWORD_TAB_X, y: SIGNIN_TAB_Y },
|
||||
force: true,
|
||||
});
|
||||
await page.waitForTimeout(120);
|
||||
await pane.click({
|
||||
position: { x: SIGNIN_PASSWORD_TAB_X, y: SIGNIN_TAB_Y },
|
||||
force: true,
|
||||
});
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
|
||||
async function fillAt(page: Page, x: number, y: number, value: string): Promise<void> {
|
||||
const pane = page.locator('flt-glass-pane');
|
||||
await pane.click({ position: { x, y }, force: true });
|
||||
await page.waitForTimeout(100);
|
||||
await page.keyboard.press('Control+A');
|
||||
await page.keyboard.press('Backspace');
|
||||
await page.keyboard.type(value);
|
||||
}
|
||||
|
||||
async function mockAuthApis(page: Page, capture: RequestCapture): Promise<void> {
|
||||
await page.route('**/api/v1/**', async (route: Route) => {
|
||||
const requestUrl = new URL(route.request().url());
|
||||
const path = requestUrl.pathname;
|
||||
|
||||
if (path.endsWith('/api/v1/auth/password/login')) {
|
||||
capture.loginBody = (route.request().postDataJSON() ?? {}) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
|
||||
const loginId = String(capture.loginBody.loginId ?? '');
|
||||
const password = String(capture.loginBody.password ?? '');
|
||||
if (loginId === 'e2e@example.com' && password === 'ValidPass1!') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
sessionJwt: 'e30.e30.e30',
|
||||
provider: 'ory',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ error: 'password_or_email_mismatch' }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.endsWith('/api/v1/auth/password/policy')) {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
minLength: 12,
|
||||
minCharacterTypes: 3,
|
||||
lowercase: true,
|
||||
uppercase: true,
|
||||
number: true,
|
||||
nonAlphanumeric: true,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.endsWith('/api/v1/auth/password/reset/complete')) {
|
||||
capture.resetBody = (route.request().postDataJSON() ?? {}) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
capture.resetToken = requestUrl.searchParams.get('token');
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ status: 'ok' }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.endsWith('/api/v1/client-log')) {
|
||||
const payload = (route.request().postDataJSON() ?? {}) as {
|
||||
message?: string;
|
||||
};
|
||||
if (payload.message != null) {
|
||||
capture.clientLogs.push(payload.message);
|
||||
}
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ ok: true }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.endsWith('/api/v1/user/me')) {
|
||||
const authHeader = route.request().headers()['authorization'] ?? '';
|
||||
if (!authHeader.startsWith('Bearer ')) {
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ error: 'unauthorized' }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
id: 'e2e-user',
|
||||
email: 'e2e@example.com',
|
||||
name: 'E2E User',
|
||||
phone: '+821012341234',
|
||||
department: 'QA',
|
||||
affiliationType: 'employee',
|
||||
companyCode: 'BARON',
|
||||
tenant: {
|
||||
id: 'tenant-1',
|
||||
name: 'Baron',
|
||||
slug: 'baron',
|
||||
description: 'E2E tenant',
|
||||
},
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.endsWith('/api/v1/user/rp/linked')) {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ items: [] }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.endsWith('/api/v1/audit/auth/timeline')) {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ items: [], next_cursor: '' }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test.describe('UserFront WASM password login and reset', () => {
|
||||
test('비밀번호 로그인 성공 시 dashboard로 이동하고 토큰을 저장한다', async ({ page }) => {
|
||||
const capture: RequestCapture = { clientLogs: [] };
|
||||
await mockAuthApis(page, capture);
|
||||
|
||||
await page.goto('/ko/signin');
|
||||
await clickPasswordTab(page);
|
||||
await fillAt(page, SIGNIN_LOGIN_ID_X, SIGNIN_LOGIN_ID_Y, 'e2e@example.com');
|
||||
await fillAt(page, SIGNIN_PASSWORD_X, SIGNIN_PASSWORD_Y, 'ValidPass1!');
|
||||
await page.locator('flt-glass-pane').click({
|
||||
position: { x: SIGNIN_SUBMIT_X, y: SIGNIN_SUBMIT_Y },
|
||||
force: true,
|
||||
});
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/dashboard$/);
|
||||
|
||||
expect(capture.loginBody?.loginId).toBe('e2e@example.com');
|
||||
expect(capture.loginBody?.password).toBe('ValidPass1!');
|
||||
|
||||
const storedToken = await page.evaluate(() =>
|
||||
window.localStorage.getItem('baron_auth_token'),
|
||||
);
|
||||
expect(storedToken).toBe('e30.e30.e30');
|
||||
});
|
||||
|
||||
test('비밀번호 로그인 실패 시 에러 코드를 사용자에게 표시한다', async ({ page }) => {
|
||||
const capture: RequestCapture = { clientLogs: [] };
|
||||
await mockAuthApis(page, capture);
|
||||
|
||||
await page.goto('/ko/signin');
|
||||
await clickPasswordTab(page);
|
||||
await fillAt(page, SIGNIN_LOGIN_ID_X, SIGNIN_LOGIN_ID_Y, 'e2e@example.com');
|
||||
await fillAt(page, SIGNIN_PASSWORD_X, SIGNIN_PASSWORD_Y, 'WrongPass1!');
|
||||
await page.locator('flt-glass-pane').click({
|
||||
position: { x: SIGNIN_SUBMIT_X, y: SIGNIN_SUBMIT_Y },
|
||||
force: true,
|
||||
});
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/signin$/);
|
||||
await expect
|
||||
.poll(() =>
|
||||
capture.clientLogs.some((message) =>
|
||||
message.includes('password_or_email_mismatch'),
|
||||
),
|
||||
)
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
test('reset-password에서 변경 성공 시 signin으로 이동한다', async ({ page }) => {
|
||||
const capture: RequestCapture = { clientLogs: [] };
|
||||
await mockAuthApis(page, capture);
|
||||
|
||||
await page.goto('/ko/reset-password?token=reset-token-e2e');
|
||||
await page.waitForTimeout(900);
|
||||
await fillAt(page, RESET_NEW_PASSWORD_X, RESET_NEW_PASSWORD_Y, 'ValidPass1!A');
|
||||
await fillAt(
|
||||
page,
|
||||
RESET_CONFIRM_PASSWORD_X,
|
||||
RESET_CONFIRM_PASSWORD_Y,
|
||||
'ValidPass1!A',
|
||||
);
|
||||
await page.locator('flt-glass-pane').click({
|
||||
position: { x: RESET_SUBMIT_X, y: RESET_SUBMIT_Y },
|
||||
force: true,
|
||||
});
|
||||
|
||||
await expect(page).toHaveURL(/\/ko\/signin$/);
|
||||
expect(capture.resetToken).toBe('reset-token-e2e');
|
||||
expect(capture.resetBody?.newPassword).toBe('ValidPass1!A');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user