merge: 장수 생성 버튼과 성공 알림 보강
This commit is contained in:
@@ -8,6 +8,8 @@ const operationNames = (route: Route) =>
|
||||
type FixtureState = {
|
||||
mapRequests: number;
|
||||
generalRequests: number;
|
||||
created?: boolean;
|
||||
createRequests?: number;
|
||||
};
|
||||
|
||||
const installFixture = async (page: Page, state: FixtureState): Promise<void> => {
|
||||
@@ -28,7 +30,12 @@ const installFixture = async (page: Page, state: FixtureState): Promise<void> =>
|
||||
const results = operations.map((operation) => {
|
||||
if (operation === 'auth.status') return response({ ok: true });
|
||||
if (operation === 'lobby.info') {
|
||||
return response({ myGeneral: null, year: 180, month: 4, turnTerm: 5 });
|
||||
return response({
|
||||
myGeneral: state.created ? { id: 9, name: '생성장수' } : null,
|
||||
year: 180,
|
||||
month: 4,
|
||||
turnTerm: 5,
|
||||
});
|
||||
}
|
||||
if (operation === 'join.getConfig') {
|
||||
return response({
|
||||
@@ -133,6 +140,14 @@ const installFixture = async (page: Page, state: FixtureState): Promise<void> =>
|
||||
},
|
||||
]);
|
||||
}
|
||||
if (operation === 'join.createGeneral') {
|
||||
state.created = true;
|
||||
state.createRequests = (state.createRequests ?? 0) + 1;
|
||||
return response({ generalId: 9 });
|
||||
}
|
||||
if (operation === 'world.getState') {
|
||||
return response({ config: { npcMode: 0 } });
|
||||
}
|
||||
return response({});
|
||||
});
|
||||
await route.fulfill({
|
||||
@@ -158,12 +173,48 @@ test('prioritizes core general fields and keeps context and inheritance progress
|
||||
await expect(page.getByLabel('성격')).toBeVisible();
|
||||
await expect(page.locator('.create-form').getByLabel('통솔')).toHaveValue('55');
|
||||
const statActions = page.getByRole('group', { name: '능력치 빠른 설정' });
|
||||
await expect(statActions.getByRole('button')).toHaveText([
|
||||
'랜덤형',
|
||||
'통솔무력형',
|
||||
'통솔지력형',
|
||||
'무력지력형',
|
||||
]);
|
||||
await expect(statActions.getByRole('button')).toHaveText(['랜덤형', '통솔무력형', '통솔지력형', '무력지력형']);
|
||||
const randomButton = statActions.getByRole('button', { name: '랜덤형', exact: true });
|
||||
const defaultButtonStyle = await randomButton.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
height: rect.height,
|
||||
backgroundColor: style.backgroundColor,
|
||||
color: style.color,
|
||||
fontSize: style.fontSize,
|
||||
fontWeight: style.fontWeight,
|
||||
cursor: style.cursor,
|
||||
};
|
||||
});
|
||||
expect(defaultButtonStyle).toEqual({
|
||||
height: 40,
|
||||
backgroundColor: 'rgb(0, 88, 44)',
|
||||
color: 'rgb(255, 255, 255)',
|
||||
fontSize: '14px',
|
||||
fontWeight: '700',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
await randomButton.hover();
|
||||
await expect
|
||||
.poll(() => randomButton.evaluate((element) => getComputedStyle(element).backgroundColor))
|
||||
.toBe('rgb(0, 109, 55)');
|
||||
await page.screenshot({ path: testInfo.outputPath('join-stat-actions-hover-desktop.png'), fullPage: true });
|
||||
await randomButton.focus();
|
||||
await expect(randomButton).toBeFocused();
|
||||
await expect.poll(() => randomButton.evaluate((element) => getComputedStyle(element).outlineStyle)).toBe('solid');
|
||||
const randomButtonBox = await randomButton.boundingBox();
|
||||
expect(randomButtonBox).not.toBeNull();
|
||||
await page.mouse.move(
|
||||
randomButtonBox!.x + randomButtonBox!.width / 2,
|
||||
randomButtonBox!.y + randomButtonBox!.height / 2
|
||||
);
|
||||
await page.mouse.down();
|
||||
await expect
|
||||
.poll(() => randomButton.evaluate((element) => getComputedStyle(element).backgroundColor))
|
||||
.toBe('rgb(0, 69, 35)');
|
||||
await page.screenshot({ path: testInfo.outputPath('join-stat-actions-active-desktop.png'), fullPage: true });
|
||||
await page.mouse.up();
|
||||
const setRandomValues = async (values: number[]) => {
|
||||
await page.evaluate((nextValues) => {
|
||||
let index = 0;
|
||||
@@ -268,8 +319,55 @@ test('keeps the primary creation flow readable without horizontal overflow on mo
|
||||
flowWidth: 366,
|
||||
tabsWidth: 352,
|
||||
});
|
||||
const mobileStatButtons = await page
|
||||
.getByRole('group', { name: '능력치 빠른 설정' })
|
||||
.getByRole('button')
|
||||
.evaluateAll((buttons) =>
|
||||
buttons.map((button) => {
|
||||
const rect = button.getBoundingClientRect();
|
||||
return { width: rect.width, height: rect.height };
|
||||
})
|
||||
);
|
||||
expect(mobileStatButtons).toHaveLength(4);
|
||||
expect(mobileStatButtons.every(({ width, height }) => width >= 160 && height === 40)).toBe(true);
|
||||
await expect(page.locator('.advanced-options')).not.toHaveAttribute('open');
|
||||
await page.getByRole('tab', { name: '임관 권유' }).focus();
|
||||
await expect(page.getByRole('tab', { name: '임관 권유' })).toBeFocused();
|
||||
await page.screenshot({ path: testInfo.outputPath('join-layout-mobile.png'), fullPage: true });
|
||||
});
|
||||
|
||||
test('shows the creation success dialog exactly once before navigating home', async ({ page }, testInfo) => {
|
||||
const state: FixtureState = { mapRequests: 0, generalRequests: 0 };
|
||||
await installFixture(page, state);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto('join');
|
||||
|
||||
await page.locator('.create-form').getByRole('button', { name: '장수 생성', exact: true }).click();
|
||||
|
||||
const successDialog = page.getByRole('alertdialog', { name: '완료' });
|
||||
await expect(successDialog).toHaveCount(1);
|
||||
await expect(successDialog).toContainText('장수를 생성했습니다!');
|
||||
await expect(successDialog.getByRole('button', { name: '확인' })).toBeFocused();
|
||||
await expect(page).toHaveURL(/\/join(?:\?.*)?$/);
|
||||
expect(state.createRequests).toBe(1);
|
||||
|
||||
const dialogGeometry = await successDialog.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
left: rect.left,
|
||||
right: window.innerWidth - rect.right,
|
||||
width: rect.width,
|
||||
documentWidth: document.documentElement.scrollWidth,
|
||||
};
|
||||
});
|
||||
expect(dialogGeometry.left).toBeGreaterThanOrEqual(12);
|
||||
expect(dialogGeometry.right).toBeGreaterThanOrEqual(12);
|
||||
expect(dialogGeometry.width).toBeLessThanOrEqual(366);
|
||||
expect(dialogGeometry.documentWidth).toBe(390);
|
||||
await page.screenshot({ path: testInfo.outputPath('join-create-success-dialog-mobile.png'), fullPage: true });
|
||||
|
||||
await successDialog.getByRole('button', { name: '확인' }).click();
|
||||
await expect(successDialog).toHaveCount(0);
|
||||
await expect(page).toHaveURL(new RegExp(`${gameBasePath}/?$`));
|
||||
expect(state.createRequests).toBe(1);
|
||||
});
|
||||
|
||||
@@ -494,6 +494,7 @@ const submitJoin = async () => {
|
||||
clientRequestId: pending.clientRequestId,
|
||||
});
|
||||
clearPendingJoin(pending);
|
||||
await showDialog({ kind: 'success', message: '장수를 생성했습니다!' });
|
||||
await session.refreshGeneralStatus();
|
||||
if (session.hasGeneral) {
|
||||
await router.push({ name: 'home' });
|
||||
@@ -1378,14 +1379,32 @@ onUnmounted(() => {
|
||||
.stat-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.stat-actions button {
|
||||
border: 1px solid rgba(201, 164, 90, 0.3);
|
||||
padding: 4px 8px;
|
||||
font-size: 0.75rem;
|
||||
flex: 1 1 140px;
|
||||
min-height: 40px;
|
||||
border: 1px solid #004f28;
|
||||
border-radius: 3px;
|
||||
background: #00582c;
|
||||
padding: 8px 14px;
|
||||
color: #fff;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.stat-actions button:hover {
|
||||
border-color: #005f30;
|
||||
background: #006d37;
|
||||
}
|
||||
|
||||
.stat-actions button:active {
|
||||
border-color: #003d1f;
|
||||
background: #004523;
|
||||
}
|
||||
|
||||
.stat-summary {
|
||||
@@ -1503,6 +1522,7 @@ onUnmounted(() => {
|
||||
|
||||
.context-tabs button:focus-visible,
|
||||
.advanced-options > summary:focus-visible,
|
||||
.stat-actions button:focus-visible,
|
||||
.form-actions button:focus-visible {
|
||||
outline: 2px solid #f0d58f;
|
||||
outline-offset: -2px;
|
||||
|
||||
Reference in New Issue
Block a user