Files
core2026/app/gateway-frontend/e2e/fresh-accounts.spec.ts
T

107 lines
4.2 KiB
TypeScript

import { readFile } from 'node:fs/promises';
import { expect, test, type Browser, type TestInfo } from '@playwright/test';
const SAMPLE_ICON = Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAvElEQVR4nOXOMQEAIAzAsHqaFrQgn8nIwZE/zbnvZ+mAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YCWDmjpgJYOaOmAlg5o6YC2Elzh0mBKLrgAAAAASUVORK5CYII=',
'base64'
);
const requiredEnv = (name: string): string => {
const value = process.env[name]?.trim();
if (!value) throw new Error(`${name} is required`);
return value;
};
const readPassword = async (account: 'user_a' | 'user_b'): Promise<string> =>
(await readFile(`${requiredEnv('SAMMO_LIFECYCLE_SECRET_ROOT')}/${account}_password`, 'utf8')).trim();
const registerAccount = async (
browser: Browser,
testInfo: TestInfo,
account: {
username: string;
passwordFile: 'user_a' | 'user_b';
displayName: string;
uploadIcon: boolean;
}
): Promise<void> => {
const context = await browser.newContext({
ignoreHTTPSErrors: true,
colorScheme: 'dark',
viewport: { width: 1280, height: 900 },
});
const page = await context.newPage();
const password = await readPassword(account.passwordFile);
await page.goto('/gateway/signup');
await expect(page.getByRole('heading', { name: '회원가입' })).toBeVisible();
await page.getByLabel('계정명').fill(account.username);
await page.getByLabel('비밀번호', { exact: true }).fill(password);
await page.getByLabel('비밀번호 확인').fill(password);
await page.getByLabel('닉네임').fill(account.displayName);
const requiredAgreements = page.locator('.agreement-row input[type="checkbox"]');
await requiredAgreements.nth(0).check();
await requiredAgreements.nth(1).check();
await page.getByRole('button', { name: '가입', exact: true }).click();
let registeredNow = true;
try {
await expect(page).toHaveURL(/\/gateway\/lobby\?welcome=local$/, { timeout: 5_000 });
} catch {
registeredNow = false;
await expect(page.getByRole('alert')).toBeVisible();
await page.goto('/gateway/');
await page.getByLabel('계정명').fill(account.username);
await page.getByLabel('비밀번호').fill(password);
await page.getByRole('button', { name: '로그인', exact: true }).click();
await expect(page).toHaveURL(/\/gateway\/lobby$/);
}
await page.goto('/gateway/account');
await expect(page.locator('#account-table')).toContainText(account.username);
await expect(page.locator('#account-table')).toContainText(account.displayName);
if (account.uploadIcon) {
if (registeredNow) {
await page.locator('input[type="file"]').setInputFiles({
name: 'sample-user-icon.png',
mimeType: 'image/png',
buffer: SAMPLE_ICON,
});
await page.getByRole('button', { name: '아이콘 변경', exact: true }).click();
await expect(page.getByTestId('icon-server-modal')).toBeVisible({ timeout: 30_000 });
}
const storedIcon = page.locator('img[alt="전용 아이콘"]');
await expect(storedIcon).toHaveCount(1);
await expect(storedIcon).toHaveAttribute(
'src',
/^https:\/\/sam-image\.hided\.net\/icons\/users\/core2026\/[a-f0-9]{32}\.png$/
);
if (registeredNow) {
await page.getByTestId('icon-server-close').click();
}
}
await page.screenshot({
path: testInfo.outputPath(`${account.username}-account.png`),
fullPage: true,
});
await context.close();
};
test('two local users register and the first uploads a remote user icon', async ({ browser }, testInfo) => {
test.setTimeout(120_000);
await registerAccount(browser, testInfo, {
username: 'guiusera',
passwordFile: 'user_a',
displayName: 'GUI사용자A',
uploadIcon: true,
});
await registerAccount(browser, testInfo, {
username: 'guiuserb',
passwordFile: 'user_b',
displayName: 'GUI사용자B',
uploadIcon: false,
});
});