사람 장수는 활성 전용 아이콘 ID를 명시한 경우에만 사용자 아이콘을 사용한다. 일반 생성, 선택 pool, Gateway 서버 동기화의 대표·후보 preset fallback을 제거하고 회귀 검증을 추가한다.
533 lines
24 KiB
TypeScript
533 lines
24 KiB
TypeScript
import path from 'node:path';
|
|
import { randomUUID } from 'node:crypto';
|
|
|
|
import { expect, test, type Page } from '@playwright/test';
|
|
import { encryptGameSessionToken } from '@sammo-ts/common/auth/gameToken';
|
|
import {
|
|
createGamePostgresConnector,
|
|
type GamePrisma,
|
|
type GamePrismaClient,
|
|
} from '@sammo-ts/infra';
|
|
|
|
const gameTokenSecret = process.env.SELECT_POOL_LIVE_GAME_SECRET;
|
|
const databaseUrl = process.env.SELECT_POOL_LIVE_DATABASE_URL;
|
|
const userId = process.env.SELECT_POOL_LIVE_USER_ID;
|
|
const profile = process.env.PLAYWRIGHT_GAME_PROFILE ?? 'hwe:903';
|
|
const hasLiveFixture = Boolean(gameTokenSecret && databaseUrl && userId);
|
|
const workspaceRoot =
|
|
process.env.SAMMO_WORKSPACE_ROOT ??
|
|
path.resolve(import.meta.dirname, '../../../../../sam_rebuild');
|
|
const defaultIcon = path.resolve(
|
|
process.env.SELECT_POOL_LIVE_DEFAULT_ICON ??
|
|
path.join(workspaceRoot, 'image/icons/default.jpg')
|
|
);
|
|
const walnutTexture = path.join(workspaceRoot, 'image/game/back_walnut.jpg');
|
|
const greenTexture = path.join(workspaceRoot, 'image/game/back_green.jpg');
|
|
const fixtureNationIds = [990_901, 990_902, 990_903];
|
|
|
|
interface AssetTracker {
|
|
userIconRequests: number;
|
|
}
|
|
|
|
const installSession = async (page: Page, tracker?: AssetTracker): Promise<void> => {
|
|
const now = new Date();
|
|
const gameToken = encryptGameSessionToken(
|
|
{
|
|
version: 1,
|
|
profile,
|
|
issuedAt: now.toISOString(),
|
|
expiresAt: new Date(now.getTime() + 3_600_000).toISOString(),
|
|
sessionId: `select-pool-live-${randomUUID()}`,
|
|
user: {
|
|
id: userId!,
|
|
username: 'select-pool-live',
|
|
displayName: '선택실사용자',
|
|
roles: ['user'],
|
|
legacyMemberNo: 42,
|
|
},
|
|
sanctions: {},
|
|
identity: {
|
|
kakaoVerified: true,
|
|
canCreateGeneral: true,
|
|
requiresKakaoVerification: false,
|
|
graceEndsAt: null,
|
|
},
|
|
},
|
|
gameTokenSecret!
|
|
);
|
|
await page.addInitScript(
|
|
({ token, gameProfile }) => {
|
|
if (!window.localStorage.getItem('sammo-game-token')) {
|
|
window.localStorage.setItem('sammo-game-token', token);
|
|
}
|
|
window.localStorage.setItem('sammo-game-profile', gameProfile);
|
|
},
|
|
{ token: gameToken, gameProfile: profile }
|
|
);
|
|
await page.addInitScript(() => {
|
|
const values = [1, 0];
|
|
Object.defineProperty(window.crypto, 'getRandomValues', {
|
|
configurable: true,
|
|
value: <T extends ArrayBufferView | null>(array: T): T => {
|
|
if (array && (array as Uint32Array).length > 0) {
|
|
(array as Uint32Array)[0] = values.shift() ?? 0;
|
|
}
|
|
return array;
|
|
},
|
|
});
|
|
});
|
|
await page.route('**/image/icons/**', (route) =>
|
|
route.fulfill({ path: defaultIcon, contentType: 'image/jpeg' })
|
|
);
|
|
await page.route('**/gateway/api/user-icons/**', (route) => {
|
|
if (tracker) {
|
|
tracker.userIconRequests += 1;
|
|
}
|
|
return route.fulfill({ status: 404, body: '' });
|
|
});
|
|
await page.route('**/image/game/back_walnut.jpg', (route) =>
|
|
route.fulfill({ path: walnutTexture, contentType: 'image/jpeg' })
|
|
);
|
|
await page.route('**/image/game/back_green.jpg', (route) =>
|
|
route.fulfill({ path: greenTexture, contentType: 'image/jpeg' })
|
|
);
|
|
};
|
|
|
|
const waitForPool = async (page: Page): Promise<void> => {
|
|
await expect(page.locator('.card-holder > .general-card')).toHaveCount(14);
|
|
await page.evaluate(async () => {
|
|
await document.fonts.ready;
|
|
});
|
|
};
|
|
|
|
test.describe('scenario 903 live selection pool', () => {
|
|
test.skip(!hasLiveFixture, 'live selection-pool token and database are required');
|
|
|
|
let db: GamePrismaClient;
|
|
let closeDb: (() => Promise<void>) | undefined;
|
|
|
|
test.beforeAll(async () => {
|
|
const connector = createGamePostgresConnector({ url: databaseUrl! });
|
|
await connector.connect();
|
|
db = connector.prisma;
|
|
closeDb = () => connector.disconnect();
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
await closeDb?.();
|
|
});
|
|
|
|
test('renders Ref-width desktop/mobile cards, tooltip, focus, and expiration states', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await page.clock.install({ time: new Date() });
|
|
const assetTracker: AssetTracker = { userIconRequests: 0 };
|
|
await installSession(page, assetTracker);
|
|
await page.setViewportSize({ width: 1200, height: 900 });
|
|
await page.goto('select-general');
|
|
await waitForPool(page);
|
|
await expect(page.locator('.server-info-table')).toContainText(
|
|
'현재 : 180年 1月 (5분 턴 서버)'
|
|
);
|
|
await expect(page.locator('.server-info-table')).toContainText(
|
|
'등록 장수 : 유저 0 / 500 명'
|
|
);
|
|
await expect(page.locator('.invitation-table')).toContainText('임관 권유 메시지');
|
|
|
|
const geometry = await page.evaluate(() => {
|
|
const root = document.querySelector<HTMLElement>('.select-pool-page')!;
|
|
const cards = Array.from(
|
|
document.querySelectorAll<HTMLElement>('.card-holder > .general-card')
|
|
);
|
|
const images = Array.from(
|
|
document.querySelectorAll<HTMLImageElement>('.card-holder .portrait img')
|
|
);
|
|
const selectionBody = document.querySelector<HTMLElement>('.selection-body')!;
|
|
const createSection = document.querySelector<HTMLElement>('.create-section')!;
|
|
const createBody = document.querySelector<HTMLElement>('.create-body')!;
|
|
const pageTitle = document.querySelector<HTMLElement>('.page-title')!;
|
|
const serverInfoTable =
|
|
document.querySelector<HTMLElement>('.server-info-table')!;
|
|
const invitationTable =
|
|
document.querySelector<HTMLElement>('.invitation-table')!;
|
|
const footerBack = document.querySelector<HTMLElement>('.footer-back')!;
|
|
const footerBanner = document.querySelector<HTMLElement>('.footer-banner')!;
|
|
const firstButton = document.querySelector<HTMLElement>(
|
|
'.card-holder .select-button'
|
|
)!;
|
|
return {
|
|
root: root.getBoundingClientRect().toJSON(),
|
|
pageTitle: pageTitle.getBoundingClientRect().toJSON(),
|
|
serverInfoTable: serverInfoTable.getBoundingClientRect().toJSON(),
|
|
invitationTable: invitationTable.getBoundingClientRect().toJSON(),
|
|
cards: cards.map((card) => card.getBoundingClientRect().toJSON()),
|
|
images: images.map((image) => ({
|
|
rect: image.getBoundingClientRect().toJSON(),
|
|
naturalWidth: image.naturalWidth,
|
|
naturalHeight: image.naturalHeight,
|
|
objectFit: getComputedStyle(image).objectFit,
|
|
})),
|
|
selectionBody: selectionBody.getBoundingClientRect().toJSON(),
|
|
createSection: createSection.getBoundingClientRect().toJSON(),
|
|
createBody: createBody.getBoundingClientRect().toJSON(),
|
|
footerBack: footerBack.getBoundingClientRect().toJSON(),
|
|
footerBanner: footerBanner.getBoundingClientRect().toJSON(),
|
|
firstButton: firstButton.getBoundingClientRect().toJSON(),
|
|
rootStyle: {
|
|
fontFamily: getComputedStyle(root).fontFamily,
|
|
fontSize: getComputedStyle(root).fontSize,
|
|
lineHeight: getComputedStyle(root).lineHeight,
|
|
backgroundImage: getComputedStyle(root).backgroundImage,
|
|
},
|
|
scrollWidth: document.documentElement.scrollWidth,
|
|
};
|
|
});
|
|
expect(geometry.root).toMatchObject({ x: 100, y: 8, width: 1000 });
|
|
expect(geometry.pageTitle).toMatchObject({ x: 100, y: 8, width: 1000 });
|
|
expect(Math.abs(geometry.pageTitle.height - 42.1875)).toBeLessThan(0.6);
|
|
expect(Math.abs(geometry.serverInfoTable.y - 50.1875)).toBeLessThan(0.6);
|
|
expect(Math.abs(geometry.serverInfoTable.height - 40.375)).toBeLessThan(0.6);
|
|
expect(Math.abs(geometry.invitationTable.x - 553)).toBeLessThan(0.6);
|
|
expect(Math.abs(geometry.invitationTable.y - 90.5625)).toBeLessThan(0.6);
|
|
expect(geometry.invitationTable.width).toBe(94);
|
|
expect(Math.abs(geometry.invitationTable.height - 20.1875)).toBeLessThan(0.1);
|
|
expect(geometry.rootStyle).toMatchObject({
|
|
fontSize: '14px',
|
|
lineHeight: '18.2px',
|
|
});
|
|
expect(geometry.rootStyle.fontFamily).toContain('Pretendard');
|
|
expect(geometry.rootStyle.backgroundImage).toContain('back_walnut.jpg');
|
|
expect(geometry.cards.every((card) => card.width === 127)).toBe(true);
|
|
expect(new Set(geometry.cards.map((card) => card.y)).size).toBe(2);
|
|
const shortestCard = Math.min(...geometry.cards.map((card) => card.height));
|
|
expect(Math.abs(shortestCard - 254.875)).toBeLessThan(0.6);
|
|
expect(
|
|
geometry.images.every(
|
|
(image, index) =>
|
|
image.rect.width === 64 &&
|
|
image.rect.height === 64 &&
|
|
Math.abs(
|
|
image.rect.x -
|
|
(geometry.cards[index]!.x +
|
|
(geometry.cards[index]!.width - image.rect.width) / 2)
|
|
) < 0.1
|
|
)
|
|
).toBe(true);
|
|
expect(
|
|
geometry.images.every(
|
|
(image) => image.naturalWidth > 0 && image.naturalHeight > 0
|
|
)
|
|
).toBe(true);
|
|
expect(geometry.images.every((image) => image.objectFit === 'fill')).toBe(true);
|
|
const fallbackImages = page.locator(
|
|
'.card-holder .portrait img[data-general-icon-fallback-source]'
|
|
);
|
|
await expect(fallbackImages).not.toHaveCount(0);
|
|
expect(assetTracker.userIconRequests).toBe(await fallbackImages.count());
|
|
expect(Math.abs(geometry.selectionBody.y - 130.9375)).toBeLessThan(0.6);
|
|
expect(Math.abs(geometry.createSection.height - 87.375)).toBeLessThan(0.6);
|
|
expect(geometry.firstButton.height).toBe(19);
|
|
expect(geometry.footerBanner.height).toBeCloseTo(20.1875, 3);
|
|
await expect(page.locator('.invitation-table tbody tr')).toHaveCount(0);
|
|
await expect(page.locator('.footer-banner')).toContainText(
|
|
'삼국지 모의전투 HiDCHe'
|
|
);
|
|
await expect(page.locator('.footer-banner a')).toHaveText('Credit');
|
|
|
|
const firstTrait = page.locator('.card-holder .trait-tooltip').first();
|
|
await firstTrait.hover();
|
|
await expect(firstTrait.getByRole('tooltip')).toBeVisible();
|
|
await page.screenshot({
|
|
path: testInfo.outputPath('select-general-desktop-hover.png'),
|
|
fullPage: true,
|
|
});
|
|
|
|
const firstButton = page.locator('.card-holder .select-button').first();
|
|
await firstButton.focus();
|
|
await expect(firstButton).toHaveCSS('outline-style', 'auto');
|
|
await expect(firstButton).toHaveCSS('outline-width', '1px');
|
|
await firstButton.hover();
|
|
await expect(firstButton).toHaveCSS('background-color', 'rgb(25, 25, 25)');
|
|
|
|
await page.setViewportSize({ width: 500, height: 900 });
|
|
await expect
|
|
.poll(() => page.evaluate(() => document.documentElement.scrollWidth))
|
|
.toBeGreaterThanOrEqual(1008);
|
|
await page.screenshot({
|
|
path: testInfo.outputPath('select-general-mobile.png'),
|
|
fullPage: true,
|
|
});
|
|
|
|
const validText = await page.locator('.selection-body small span').textContent();
|
|
expect(validText).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/);
|
|
const displayedExpiry = new Date(`${validText!.replace(' ', 'T')}+09:00`).getTime();
|
|
const delta = Math.max(displayedExpiry - Date.now(), 0);
|
|
await page.clock.fastForward(delta);
|
|
await expect(page.locator('.expired-text')).toHaveCount(0);
|
|
await page.clock.fastForward(2_000);
|
|
await expect(page.locator('.expired-text')).toHaveText('- 만료 -');
|
|
});
|
|
|
|
test('shuffles non-neutral invitation nations with Ref-style row content and colors', async ({
|
|
page,
|
|
}) => {
|
|
await db.nation.deleteMany({ where: { id: { in: fixtureNationIds } } });
|
|
await db.nation.createMany({
|
|
data: [
|
|
{
|
|
id: fixtureNationIds[0]!,
|
|
name: '테스트국A',
|
|
color: '#330000',
|
|
level: 1,
|
|
meta: { infoText: 'A 권유' },
|
|
},
|
|
{
|
|
id: fixtureNationIds[1]!,
|
|
name: '테스트국B',
|
|
color: '#FFFF00',
|
|
level: 1,
|
|
meta: { infoText: 'B 권유' },
|
|
},
|
|
{
|
|
id: fixtureNationIds[2]!,
|
|
name: '테스트국C',
|
|
color: '#000080',
|
|
level: 1,
|
|
meta: { infoText: 'C 권유' },
|
|
},
|
|
],
|
|
});
|
|
try {
|
|
await installSession(page);
|
|
await page.setViewportSize({ width: 1200, height: 900 });
|
|
await page.goto('select-general');
|
|
await waitForPool(page);
|
|
|
|
const rows = page.locator('.invitation-table tbody tr');
|
|
await expect(rows.locator('.invitation-nation')).toHaveText([
|
|
'테스트국C',
|
|
'테스트국A',
|
|
'테스트국B',
|
|
]);
|
|
await expect(rows.locator('.invitation-message')).toHaveText([
|
|
'C 권유',
|
|
'A 권유',
|
|
'B 권유',
|
|
]);
|
|
await expect(rows.nth(0)).toHaveCSS('background-color', 'rgb(0, 0, 128)');
|
|
await expect(rows.nth(1)).toHaveCSS('background-color', 'rgb(51, 0, 0)');
|
|
await expect(rows.nth(2)).toHaveCSS('background-color', 'rgb(255, 255, 0)');
|
|
await expect(rows.nth(0)).toHaveCSS('color', 'rgb(255, 255, 255)');
|
|
await expect(rows.nth(2)).toHaveCSS('color', 'rgb(0, 0, 0)');
|
|
const invitationGeometry = await page.locator('.invitation-table').evaluate((table) => {
|
|
const rect = table.getBoundingClientRect();
|
|
return { x: rect.x, width: rect.width };
|
|
});
|
|
expect(invitationGeometry).toEqual({ x: 100, width: 1000 });
|
|
} finally {
|
|
await db.nation.deleteMany({ where: { id: { in: fixtureNationIds } } });
|
|
}
|
|
});
|
|
|
|
test('creates, rejects cooldown, exposes MyPage action, and reselects through the live API', async ({
|
|
page,
|
|
}) => {
|
|
const dialogs: string[] = [];
|
|
const createClientRequestIds: string[] = [];
|
|
let injectCreateTimeout = true;
|
|
page.on('dialog', async (dialog) => {
|
|
dialogs.push(dialog.message());
|
|
await dialog.accept();
|
|
});
|
|
await page.route('**/trpc/join.selectPoolGeneral?batch=1', async (route) => {
|
|
const findClientRequestId = (value: unknown): string | undefined => {
|
|
if (!value || typeof value !== 'object') return undefined;
|
|
if (
|
|
'clientRequestId' in value &&
|
|
typeof value.clientRequestId === 'string'
|
|
) {
|
|
return value.clientRequestId;
|
|
}
|
|
for (const nested of Object.values(value)) {
|
|
const found = findClientRequestId(nested);
|
|
if (found) return found;
|
|
}
|
|
return undefined;
|
|
};
|
|
const clientRequestId = findClientRequestId(route.request().postDataJSON());
|
|
expect(clientRequestId).toMatch(/^[0-9a-f-]{36}$/);
|
|
createClientRequestIds.push(clientRequestId!);
|
|
if (!injectCreateTimeout) {
|
|
await route.continue();
|
|
return;
|
|
}
|
|
injectCreateTimeout = false;
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([
|
|
{
|
|
error: {
|
|
message:
|
|
'장수 선택 요청은 접수됐지만 처리 결과를 아직 확인하지 못했습니다. 같은 요청으로 다시 시도해 주세요.',
|
|
code: -32008,
|
|
data: {
|
|
code: 'TIMEOUT',
|
|
httpStatus: 408,
|
|
path: 'join.selectPoolGeneral',
|
|
},
|
|
},
|
|
},
|
|
]),
|
|
});
|
|
});
|
|
const assetTracker: AssetTracker = { userIconRequests: 0 };
|
|
await installSession(page, assetTracker);
|
|
await page.setViewportSize({ width: 1200, height: 900 });
|
|
await page.goto('select-general');
|
|
await waitForPool(page);
|
|
|
|
const candidateCards = page.locator('.card-holder > .general-card');
|
|
const fallbackCard = candidateCards
|
|
.filter({ has: page.locator('img[data-general-icon-fallback-source]') })
|
|
.first();
|
|
await expect(fallbackCard).toBeVisible();
|
|
const initialName = await fallbackCard.locator('h4').first().textContent();
|
|
const userIconRequestsBeforePreview = assetTracker.userIconRequests;
|
|
await fallbackCard.locator('.select-button').click();
|
|
await expect(page.locator('.selected-card')).toHaveCount(1);
|
|
await expect(
|
|
page.locator('.selected-card img[data-general-icon-fallback-source]')
|
|
).toBeVisible();
|
|
expect(assetTracker.userIconRequests).toBeGreaterThan(userIconRequestsBeforePreview);
|
|
await page.locator('.custom-form select').selectOption('che_안전');
|
|
await page.getByRole('button', { name: '다시입력' }).click();
|
|
await expect(page.locator('.custom-form select')).toHaveValue('Random');
|
|
await expect(page.locator('.selected-card')).toHaveCount(1);
|
|
await page.locator('.custom-form select').selectOption('che_안전');
|
|
await page.locator('#build-general').click();
|
|
await expect(page.getByTestId('game-toast')).toContainText(
|
|
'장수 생성에 실패했습니다: 장수 선택 요청은 접수됐지만 처리 결과를 아직 확인하지 못했습니다. 같은 요청으로 다시 시도해 주세요.'
|
|
);
|
|
await waitForPool(page);
|
|
const retryCard = page
|
|
.locator('.card-holder > .general-card')
|
|
.filter({ has: page.locator('h4', { hasText: initialName?.trim() ?? '' }) })
|
|
.first();
|
|
await retryCard.locator('.select-button').click();
|
|
await page.locator('.custom-form select').selectOption('che_안전');
|
|
await page.locator('#build-general').click();
|
|
const createDialog = page.getByRole('alertdialog', { name: '완료' });
|
|
await expect(createDialog).toContainText('선택한 장수로 생성했습니다.');
|
|
await createDialog.getByRole('button', { name: '확인' }).click();
|
|
await expect(page).toHaveURL(/\/hwe\/$/);
|
|
expect(dialogs.filter((message) => message === '이 장수로 생성할까요?')).toHaveLength(2);
|
|
expect(createClientRequestIds).toHaveLength(2);
|
|
expect(createClientRequestIds[1]).toBe(createClientRequestIds[0]);
|
|
|
|
const created = await db.general.findFirstOrThrow({ where: { userId } });
|
|
expect(created.name).toBe(initialName?.trim());
|
|
expect(created.personalCode).toBe('che_안전');
|
|
expect(created.specialCode).toMatch(/^che_event_/);
|
|
expect(created).toMatchObject({ picture: 'default.jpg', imageServer: 0 });
|
|
const createEvent = await db.inputEvent.findFirstOrThrow({
|
|
where: { actorUserId: userId, eventType: 'selectPoolCreate' },
|
|
orderBy: { sequence: 'desc' },
|
|
});
|
|
expect(createEvent).toMatchObject({ status: 'SUCCEEDED', attempts: 1 });
|
|
expect(createEvent.requestId).toMatch(
|
|
new RegExp(`^select-pool:${userId}:[0-9a-f-]{36}:create$`)
|
|
);
|
|
expect(
|
|
await page.evaluate(() =>
|
|
window.sessionStorage.getItem('sammo-select-pool-pending-action')
|
|
)
|
|
).toBeNull();
|
|
|
|
await page.goto('my-page');
|
|
const actionLink = page.locator('.select-general-link');
|
|
await expect(actionLink).toBeVisible();
|
|
await expect(actionLink.locator('..')).toContainText(
|
|
/다른 장수 선택\s*\(\d{4}-\d{2}-\d{2}/
|
|
);
|
|
await expect(actionLink).toHaveCSS('width', '160px');
|
|
await expect(actionLink).toHaveCSS('height', '30px');
|
|
|
|
dialogs.length = 0;
|
|
await page.goto('select-general');
|
|
await expect(page.getByTestId('game-toast')).toContainText(
|
|
'장수 선택 정보를 불러오지 못했습니다: 아직 다시 고를 수 없습니다'
|
|
);
|
|
await expect(page.locator('.error-text')).toHaveText('아직 다시 고를 수 없습니다');
|
|
|
|
const availableAt = '2026-07-29T00:00:00.000Z';
|
|
const cooldownRequestId = `select-pool-live-cooldown-${randomUUID()}`;
|
|
await db.inputEvent.create({
|
|
data: {
|
|
requestId: cooldownRequestId,
|
|
target: 'ENGINE',
|
|
eventType: 'patchGeneral',
|
|
payload: {
|
|
type: 'patchGeneral',
|
|
requestId: cooldownRequestId,
|
|
generalId: created.id,
|
|
patch: {
|
|
meta: {
|
|
next_change: availableAt,
|
|
nextChangeAt: availableAt,
|
|
},
|
|
},
|
|
} as GamePrisma.InputJsonValue,
|
|
},
|
|
});
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
(
|
|
await db.inputEvent.findUniqueOrThrow({
|
|
where: { requestId: cooldownRequestId },
|
|
})
|
|
).status
|
|
)
|
|
.toBe('SUCCEEDED');
|
|
|
|
dialogs.length = 0;
|
|
await page.reload();
|
|
await waitForPool(page);
|
|
const cards = page.locator('.card-holder > .general-card');
|
|
const names = await cards.locator('h4').allTextContents();
|
|
const targetIndex = names.findIndex((name) => name.trim() !== created.name);
|
|
expect(targetIndex).toBeGreaterThanOrEqual(0);
|
|
const targetName = names[targetIndex]!.trim();
|
|
await cards.nth(targetIndex).locator('.select-button').click();
|
|
const reselectDialog = page.getByRole('alertdialog', { name: '완료' });
|
|
await expect(reselectDialog).toContainText('선택한 장수로 변경했습니다.');
|
|
await reselectDialog.getByRole('button', { name: '확인' }).click();
|
|
await expect(page).toHaveURL(/\/hwe\/$/);
|
|
await expect.poll(() => dialogs).toContain(`이 장수를 선택할까요? : ${targetName}`);
|
|
|
|
await expect
|
|
.poll(async () => (await db.general.findUniqueOrThrow({ where: { id: created.id } })).name)
|
|
.toBe(targetName);
|
|
await expect
|
|
.poll(async () => {
|
|
const general = await db.general.findUniqueOrThrow({ where: { id: created.id } });
|
|
return { picture: general.picture, imageServer: general.imageServer };
|
|
})
|
|
.toEqual({ picture: 'default.jpg', imageServer: 0 });
|
|
const reselectEvent = await db.inputEvent.findFirstOrThrow({
|
|
where: { actorUserId: userId, eventType: 'selectPoolReselect' },
|
|
orderBy: { sequence: 'desc' },
|
|
});
|
|
expect(reselectEvent).toMatchObject({ status: 'SUCCEEDED', attempts: 1 });
|
|
expect(reselectEvent.requestId).toMatch(
|
|
new RegExp(`^select-pool:${userId}:[0-9a-f-]{36}:reselect$`)
|
|
);
|
|
expect(
|
|
await page.evaluate(() =>
|
|
window.sessionStorage.getItem('sammo-select-pool-pending-action')
|
|
)
|
|
).toBeNull();
|
|
});
|
|
});
|