merge: 최신 main을 지도 계절 전환에 반영한다
# Conflicts: # app/game-frontend/src/components/main/MapViewer.vue
This commit is contained in:
@@ -431,13 +431,20 @@ test('nation and general directories preserve the fixed legacy Chromium geometry
|
||||
}
|
||||
});
|
||||
|
||||
test('general directory submits the legacy sort selector and keeps wounded/bonus rendering', async ({ page }) => {
|
||||
await install(page);
|
||||
test('general directory sorts the loaded rows locally in a stable three-state cycle', async ({ page }) => {
|
||||
const requestedOperations: string[] = [];
|
||||
await install(page, 'general', [], requestedOperations);
|
||||
await page.goto('general-list');
|
||||
await expect(page.locator('tbody tr[data-general-id]')).toHaveCount(2);
|
||||
await page.selectOption('#viewType', '8');
|
||||
await page.getByRole('button', { name: '정렬하기' }).click();
|
||||
await expect(page.locator('tbody tr[data-general-id]').first()).toHaveAttribute('data-general-id', '10');
|
||||
await page.getByRole('button', { name: '정렬하기' }).click();
|
||||
await expect(page.locator('tbody tr[data-general-id]').first()).toHaveAttribute('data-general-id', '20');
|
||||
await page.getByRole('button', { name: '정렬하기' }).click();
|
||||
await expect(page.locator('tbody tr[data-general-id]').first()).toHaveAttribute('data-general-id', '10');
|
||||
expect(requestedOperations.filter((operation) => operation === 'world.getGeneralDirectory')).toHaveLength(1);
|
||||
|
||||
await expect(page.locator('tbody tr[data-general-id="10"] .wounded').first()).toHaveText('81');
|
||||
await expect(page.locator('tbody tr[data-general-id="10"] .leadership-bonus')).toHaveText('+6');
|
||||
|
||||
@@ -489,10 +496,10 @@ test('directory sort controls stay legible in dark mode and sortable headers app
|
||||
expect(await submit.evaluate((element) => getComputedStyle(element).borderBottomWidth)).toBe('1px');
|
||||
await page.mouse.up();
|
||||
|
||||
await page.getByRole('button', { name: '삭턴 기준 정렬' }).click();
|
||||
await page.getByRole('button', { name: /^삭턴 내림차순/u }).click();
|
||||
await expect(select).toHaveValue('8');
|
||||
await expect(page.locator('th[aria-sort="ascending"]')).toContainText('삭턴');
|
||||
await expect(page.locator('tbody tr[data-general-id]').first()).toHaveAttribute('data-general-id', '20');
|
||||
await expect(page.locator('th[aria-sort="descending"]')).toContainText('삭턴');
|
||||
await expect(page.locator('tbody tr[data-general-id]').first()).toHaveAttribute('data-general-id', '10');
|
||||
await page.screenshot({ path: testInfo.outputPath('directory-sort-controls-desktop.png'), fullPage: true });
|
||||
|
||||
await page.setViewportSize({ width: 500, height: 844 });
|
||||
@@ -501,6 +508,82 @@ test('directory sort controls stay legible in dark mode and sortable headers app
|
||||
await page.screenshot({ path: testInfo.outputPath('directory-sort-controls-mobile.png'), fullPage: true });
|
||||
});
|
||||
|
||||
test('name and mixed-direction stable sorting reuse one response until explicit refresh', async ({ page }) => {
|
||||
const requestedOperations: string[] = [];
|
||||
await install(page, 'general', [], requestedOperations);
|
||||
await page.goto('general-list');
|
||||
|
||||
const rows = page.locator('tbody tr[data-general-id]');
|
||||
await page.getByRole('button', { name: /^이름 내림차순/u }).click();
|
||||
await expect(rows.first()).toHaveAttribute('data-general-id', '10');
|
||||
await page.getByRole('button', { name: /^이름 오름차순/u }).click();
|
||||
await expect(rows.first()).toHaveAttribute('data-general-id', '20');
|
||||
await page.getByRole('button', { name: /^이름 정렬 해제/u }).click();
|
||||
await expect(rows.first()).toHaveAttribute('data-general-id', '10');
|
||||
|
||||
await page.getByRole('button', { name: /^통솔 내림차순/u }).click();
|
||||
await page.getByRole('button', { name: /^무력 내림차순/u }).click();
|
||||
await expect(rows.first()).toHaveAttribute('data-general-id', '20');
|
||||
await expect(
|
||||
page.getByRole('columnheader').filter({ hasText: '통솔' }).locator('.legacy-sort-indicator')
|
||||
).toHaveText('▼2');
|
||||
await page.getByRole('button', { name: /^무력 오름차순/u }).click();
|
||||
await expect(rows.first()).toHaveAttribute('data-general-id', '10');
|
||||
expect(requestedOperations.filter((operation) => operation === 'world.getGeneralDirectory')).toHaveLength(1);
|
||||
|
||||
await page.getByRole('button', { name: '갱 신' }).click();
|
||||
await expect
|
||||
.poll(() => requestedOperations.filter((operation) => operation === 'world.getGeneralDirectory').length)
|
||||
.toBe(2);
|
||||
await expect(rows.first()).toHaveAttribute('data-general-id', '10');
|
||||
});
|
||||
|
||||
test('trait and injury explanations appear on pointer hover and keyboard focus', async ({ page }, testInfo) => {
|
||||
await install(page);
|
||||
await page.goto('general-list');
|
||||
|
||||
const personality = page.locator('[data-directory-tooltip="personality-10"]');
|
||||
await personality.hover();
|
||||
await expect(personality.getByRole('tooltip')).toContainText('성격 · 대담');
|
||||
await expect(personality.getByRole('tooltip')).toContainText('대담한 성격');
|
||||
|
||||
const domestic = page.locator('[data-directory-tooltip="special-domestic-10"]');
|
||||
await domestic.hover();
|
||||
await expect(domestic.getByRole('tooltip')).toContainText('내정 특기 · 상재');
|
||||
await expect(domestic.getByRole('tooltip')).toContainText('상업 특기');
|
||||
|
||||
const war = page.locator('[data-directory-tooltip="special-war-10"]');
|
||||
await war.focus();
|
||||
await expect(war.getByRole('tooltip')).toContainText('전투 특기 · 귀모');
|
||||
await expect(war.getByRole('tooltip')).toContainText('전투 특기');
|
||||
|
||||
const injury = page.locator('[data-directory-tooltip="injury-leadership-10"]');
|
||||
await injury.hover();
|
||||
await expect(injury.getByRole('tooltip')).toContainText('부상 10%');
|
||||
await expect(injury.getByRole('tooltip')).toContainText('원래 통솔 90 → 적용 81');
|
||||
const tooltipGeometry = await injury.getByRole('tooltip').evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
left: rect.left,
|
||||
right: window.innerWidth - rect.right,
|
||||
display: style.display,
|
||||
background: style.backgroundColor,
|
||||
color: style.color,
|
||||
fontSize: style.fontSize,
|
||||
};
|
||||
});
|
||||
expect(tooltipGeometry.left).toBeGreaterThanOrEqual(8);
|
||||
expect(tooltipGeometry.right).toBeGreaterThanOrEqual(8);
|
||||
expect(tooltipGeometry).toMatchObject({
|
||||
display: 'block',
|
||||
background: 'rgb(16, 16, 16)',
|
||||
color: 'rgb(245, 245, 245)',
|
||||
fontSize: '12.5px',
|
||||
});
|
||||
await page.screenshot({ path: testInfo.outputPath('directory-trait-injury-tooltips.png'), fullPage: true });
|
||||
});
|
||||
|
||||
test('npc directory reuses the dark sort controls and sorts from a table header', async ({ page }, testInfo) => {
|
||||
await install(page);
|
||||
await page.goto('npc-list');
|
||||
@@ -544,7 +627,8 @@ test('nation directory reuses only the public general-directory row on hover and
|
||||
await expect(preview.locator('[data-general-card-id]')).toHaveCount(1);
|
||||
await expect(preview.locator('[data-general-card-id="10"]')).toContainText('조조');
|
||||
await expect(preview.locator('[data-general-card-id="10"]')).toContainText('대담');
|
||||
await expect(preview.locator('[data-general-card-id="10"]')).toContainText('상재 / 귀모');
|
||||
await expect(preview.locator('[data-directory-tooltip="card-special-domestic-10"]')).toContainText('상재');
|
||||
await expect(preview.locator('[data-directory-tooltip="card-special-war-10"]')).toContainText('귀모');
|
||||
await expect(preview).not.toContainText('user-');
|
||||
await expect(preview).not.toContainText('secret');
|
||||
|
||||
@@ -687,8 +771,27 @@ test('nation and general directories rearrange for mobile and keep the tapped pr
|
||||
await expect(page.locator('.general-table')).toBeHidden();
|
||||
await expect(page.locator('.general-card-list')).toBeVisible();
|
||||
await expect(page.locator('.general-card-list [data-general-card-id]')).toHaveCount(2);
|
||||
await expect(page.locator('[data-general-card-id="10"]')).toContainText('상재 / 귀모');
|
||||
await expect(page.locator('[data-general-card-id="10"]')).toContainText('통솔81+6');
|
||||
await expect(page.locator('[data-directory-tooltip="card-special-domestic-10"]')).toContainText('상재');
|
||||
await expect(page.locator('[data-directory-tooltip="card-special-war-10"]')).toContainText('귀모');
|
||||
await expect(page.locator('[data-directory-tooltip="card-injury-leadership-10"] > .wounded')).toHaveText(
|
||||
'81'
|
||||
);
|
||||
await expect(page.locator('[data-general-card-id="10"] .leadership-bonus')).toHaveText('+6');
|
||||
const mobileInjury = page.locator('[data-directory-tooltip="card-injury-leadership-10"]');
|
||||
await mobileInjury.focus();
|
||||
await expect(mobileInjury.getByRole('tooltip')).toBeVisible();
|
||||
const mobileTooltipGeometry = await mobileInjury.getByRole('tooltip').evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
left: rect.left,
|
||||
right: window.innerWidth - rect.right,
|
||||
width: rect.width,
|
||||
innerWidth: window.innerWidth,
|
||||
};
|
||||
});
|
||||
expect(mobileTooltipGeometry.left).toBeGreaterThanOrEqual(8);
|
||||
expect(mobileTooltipGeometry.right).toBeGreaterThanOrEqual(8);
|
||||
expect(mobileTooltipGeometry.width).toBeLessThanOrEqual(mobileTooltipGeometry.innerWidth - 16);
|
||||
|
||||
const generalMetrics = await page.locator('.directory-page').evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
@@ -760,11 +863,12 @@ test('a reused image element falls back for each newly broken account icon', asy
|
||||
await expect.poll(() => icon.evaluate((element) => (element as HTMLImageElement).naturalWidth)).toBe(64);
|
||||
});
|
||||
|
||||
test('a failed resort retains the selected value and existing rows', async ({ page }) => {
|
||||
test('a failed explicit refresh retains the local sort selection and existing rows', async ({ page }) => {
|
||||
await install(page, 'error-after-load');
|
||||
await page.goto('general-list');
|
||||
await page.selectOption('#viewType', '8');
|
||||
await page.getByRole('button', { name: '정렬하기' }).click();
|
||||
await page.getByRole('button', { name: '갱 신' }).click();
|
||||
await expect(page.getByRole('alert')).toContainText('권한 확인 실패');
|
||||
await expect(page.locator('#viewType')).toHaveValue('8');
|
||||
await expect(page.locator('tbody tr[data-general-id]')).toHaveCount(2);
|
||||
|
||||
@@ -475,6 +475,27 @@ test('map keeps desktop hover navigation and lets touch users choose one-tap or
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await go(page, 'global-info');
|
||||
|
||||
const desktopOptionsTrigger = page.getByRole('button', { name: '지도 옵션' });
|
||||
await expect(desktopOptionsTrigger).toBeVisible();
|
||||
await expect(desktopOptionsTrigger).toHaveAttribute('aria-expanded', 'false');
|
||||
await expect(desktopOptionsTrigger).toHaveCSS('background-color', 'rgb(52, 92, 133)');
|
||||
await desktopOptionsTrigger.hover();
|
||||
await expect(desktopOptionsTrigger).toHaveCSS('background-color', 'rgb(40, 73, 105)');
|
||||
await expect(page.getByRole('button', { name: '도시명 표기 끄기' })).not.toBeVisible();
|
||||
await desktopOptionsTrigger.click();
|
||||
const cityNameToggle = page.getByRole('button', { name: '도시명 표기 끄기' });
|
||||
await expect(cityNameToggle).toBeVisible();
|
||||
await expect(desktopOptionsTrigger).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(page.locator('.map-options-menu .map-toggle')).toHaveCount(1);
|
||||
await page.keyboard.press('Tab');
|
||||
await desktopOptionsTrigger.focus();
|
||||
await expect(desktopOptionsTrigger).toHaveCSS('outline-style', 'solid');
|
||||
await page.screenshot({ path: testInfo.outputPath('desktop-map-options-open.png'), fullPage: true });
|
||||
await cityNameToggle.click();
|
||||
await expect(page.locator('.map-area .city-name')).toHaveCount(0);
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(desktopOptionsTrigger).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
const desktopCity = page.locator('.city-base').first();
|
||||
await expect(page.locator('.map-toggle-single-tap')).toHaveCount(0);
|
||||
await desktopCity.hover();
|
||||
@@ -508,20 +529,48 @@ test('map keeps desktop hover navigation and lets touch users choose one-tap or
|
||||
await install(mobilePage);
|
||||
await go(mobilePage, 'global-info');
|
||||
|
||||
const mobileOptionsTrigger = mobilePage.getByRole('button', { name: '지도 옵션' });
|
||||
await expect(mobileOptionsTrigger).toBeVisible();
|
||||
await expect(mobileOptionsTrigger).toHaveAttribute('aria-expanded', 'false');
|
||||
await mobileOptionsTrigger.tap();
|
||||
await expect(mobileOptionsTrigger).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(mobilePage.locator('.map-options-menu .map-toggle')).toHaveCount(2);
|
||||
|
||||
const twoTapButton = mobilePage.getByRole('button', { name: '두번 탭 해 도시 이동 끄기' });
|
||||
await expect(twoTapButton).toBeVisible();
|
||||
await expect(twoTapButton).toHaveAttribute('aria-pressed', 'false');
|
||||
const controlGeometry = await twoTapButton.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const controlGeometry = await mobilePage.locator('.map-controls').evaluate((element) => {
|
||||
const triggerRect = element.querySelector('.map-options-trigger')!.getBoundingClientRect();
|
||||
const menuRect = element.querySelector('.map-options-menu')!.getBoundingClientRect();
|
||||
const optionRects = Array.from(element.querySelectorAll('.map-options-menu .map-toggle')).map((option) => {
|
||||
const rect = option.getBoundingClientRect();
|
||||
return { top: rect.top, bottom: rect.bottom };
|
||||
});
|
||||
const mapRect = element.closest('.map-area')?.getBoundingClientRect();
|
||||
const style = getComputedStyle(element);
|
||||
const optionStyle = getComputedStyle(element.querySelector('.map-toggle')!);
|
||||
return {
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
mapRight: mapRect?.right,
|
||||
mapBottom: mapRect?.bottom,
|
||||
fontSize: style.fontSize,
|
||||
lineHeight: style.lineHeight,
|
||||
trigger: {
|
||||
right: triggerRect.right,
|
||||
bottom: triggerRect.bottom,
|
||||
top: triggerRect.top,
|
||||
},
|
||||
menu: {
|
||||
left: menuRect.left,
|
||||
right: menuRect.right,
|
||||
top: menuRect.top,
|
||||
bottom: menuRect.bottom,
|
||||
},
|
||||
optionRects,
|
||||
map: mapRect
|
||||
? {
|
||||
left: mapRect.left,
|
||||
right: mapRect.right,
|
||||
top: mapRect.top,
|
||||
bottom: mapRect.bottom,
|
||||
}
|
||||
: null,
|
||||
fontSize: optionStyle.fontSize,
|
||||
lineHeight: optionStyle.lineHeight,
|
||||
documentWidth: document.documentElement.scrollWidth,
|
||||
viewportWidth: document.documentElement.clientWidth,
|
||||
overflowing: Array.from(document.querySelectorAll<HTMLElement>('body *'))
|
||||
@@ -543,8 +592,20 @@ test('map keeps desktop hover navigation and lets touch users choose one-tap or
|
||||
overflowing: [],
|
||||
});
|
||||
expect(controlGeometry.documentWidth).toBeLessThanOrEqual(controlGeometry.viewportWidth + 1);
|
||||
expect(controlGeometry.mapRight! - controlGeometry.right).toBeCloseTo(4, 1);
|
||||
expect(controlGeometry.mapBottom! - controlGeometry.bottom).toBeCloseTo(4, 1);
|
||||
expect(controlGeometry.map).not.toBeNull();
|
||||
expect(controlGeometry.map!.right - controlGeometry.trigger.right).toBeCloseTo(4, 1);
|
||||
expect(controlGeometry.map!.bottom - controlGeometry.trigger.bottom).toBeCloseTo(4, 1);
|
||||
expect(controlGeometry.menu.left).toBeGreaterThanOrEqual(controlGeometry.map!.left);
|
||||
expect(controlGeometry.menu.right).toBeLessThanOrEqual(controlGeometry.map!.right);
|
||||
expect(controlGeometry.menu.top).toBeGreaterThanOrEqual(controlGeometry.map!.top);
|
||||
expect(controlGeometry.menu.bottom).toBeLessThan(controlGeometry.trigger.top);
|
||||
expect(controlGeometry.optionRects).toHaveLength(2);
|
||||
expect(controlGeometry.optionRects[0]!.bottom).toBeLessThanOrEqual(controlGeometry.optionRects[1]!.top);
|
||||
expect(controlGeometry.optionRects[1]!.bottom).toBeLessThanOrEqual(controlGeometry.menu.bottom);
|
||||
await mobilePage.screenshot({ path: testInfo.outputPath('mobile-map-options-open.png'), fullPage: true });
|
||||
|
||||
await mobilePage.locator('.map-area').tap({ position: { x: 10, y: 10 } });
|
||||
await expect(mobileOptionsTrigger).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
const mobileCities = mobilePage.locator('.city-base');
|
||||
await mobileCities.nth(0).tap();
|
||||
@@ -560,17 +621,19 @@ test('map keeps desktop hover navigation and lets touch users choose one-tap or
|
||||
await expect(mobilePage).toHaveURL(/\/current-city\?cityId=2$/u);
|
||||
|
||||
await go(mobilePage, 'global-info');
|
||||
await mobilePage.getByRole('button', { name: '지도 옵션' }).tap();
|
||||
await mobilePage.getByRole('button', { name: '두번 탭 해 도시 이동 끄기' }).click();
|
||||
const singleTapButton = mobilePage.getByRole('button', { name: '두번 탭 해 도시 이동 켜기' });
|
||||
await expect(singleTapButton).toHaveAttribute('aria-pressed', 'true');
|
||||
expect(await mobilePage.evaluate(() => localStorage.getItem('sam.toggleSingleTap'))).toBe('yes');
|
||||
|
||||
await mobilePage.reload();
|
||||
await expect(singleTapButton).toBeVisible();
|
||||
await expect(singleTapButton).not.toBeVisible();
|
||||
await mobilePage.locator('.city-base').nth(2).tap();
|
||||
await expect(mobilePage).toHaveURL(/\/current-city\?cityId=3$/u);
|
||||
|
||||
await go(mobilePage, 'global-info');
|
||||
await mobilePage.getByRole('button', { name: '지도 옵션' }).tap();
|
||||
await mobilePage.getByRole('button', { name: '두번 탭 해 도시 이동 켜기' }).click();
|
||||
expect(await mobilePage.evaluate(() => localStorage.getItem('sam.toggleSingleTap'))).toBe('no');
|
||||
await mobilePage.locator('.city-base').first().tap();
|
||||
|
||||
@@ -102,6 +102,9 @@ const myGeneral = (state: FixtureState) => ({
|
||||
dedication: 200,
|
||||
age: 30,
|
||||
turnTime: '2026-01-01 00:10:00',
|
||||
recentWar: '2026-01-01 00:00:00',
|
||||
defenceTrain: 80,
|
||||
killTurn: 6,
|
||||
crewTypeId: 1,
|
||||
crewTypeName: '보병',
|
||||
crewTypeInfo: state.richMyInfo
|
||||
@@ -277,7 +280,7 @@ const battleCenter = (state: FixtureState) => ({
|
||||
cityId: 1,
|
||||
turnTime: '2026-01-01 00:10:00',
|
||||
recentWar: '2026-01-01 00:00:00',
|
||||
warnum: 3,
|
||||
warnum: 8,
|
||||
stats: { leadership: 70, strength: 60, intelligence: 50 },
|
||||
experience: 100,
|
||||
dedication: 200,
|
||||
@@ -288,6 +291,8 @@ const battleCenter = (state: FixtureState) => ({
|
||||
train: 80,
|
||||
atmos: 90,
|
||||
age: 30,
|
||||
defenceTrain: 80,
|
||||
killTurn: 6,
|
||||
crewTypeId: 1,
|
||||
crewTypeName: '보병',
|
||||
equipment: { weapon: 'None', book: 'None', horse: 'None', item: 'None' },
|
||||
@@ -301,7 +306,8 @@ const battleCenter = (state: FixtureState) => ({
|
||||
statUpgradeLimit: 20,
|
||||
dex: [350, 1_375, 3_500, 7_125, 1_275_975],
|
||||
},
|
||||
battleStats: { kills: 1, deaths: 2, fire: 0, killCrew: 300, deathCrew: 100, dex: [] },
|
||||
serviceYears: 4,
|
||||
battleStats: { kills: 5, deaths: 3, fire: 12, killCrew: 12_345, deathCrew: 6_789, dex: [] },
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
@@ -323,6 +329,8 @@ const battleCenter = (state: FixtureState) => ({
|
||||
train: 60,
|
||||
atmos: 60,
|
||||
age: 20,
|
||||
defenceTrain: 80,
|
||||
killTurn: 4,
|
||||
crewTypeId: 1,
|
||||
crewTypeName: '보병',
|
||||
equipment: { weapon: 'None', book: 'None', horse: 'None', item: 'None' },
|
||||
@@ -336,6 +344,7 @@ const battleCenter = (state: FixtureState) => ({
|
||||
statUpgradeLimit: 20,
|
||||
dex: [0, 0, 0, 0, 0],
|
||||
},
|
||||
serviceYears: 1,
|
||||
battleStats: { kills: 0, deaths: 0, fire: 0, killCrew: 0, deathCrew: 0, dex: [] },
|
||||
},
|
||||
],
|
||||
@@ -1213,6 +1222,7 @@ test('내 정보&설정 keeps desktop density and becomes a 390px horizontal-ide
|
||||
await page.setViewportSize({ width: 1000, height: 900 });
|
||||
await page.goto('my-page');
|
||||
await expect(page.locator('.general-table')).toHaveAttribute('data-general-basic-card', '');
|
||||
await expect(page.locator('.general-table')).toHaveAttribute('data-general-information-panel', '');
|
||||
const myPageImages = await readGeneralPanelImages(page.locator('.general-table'));
|
||||
expect(myPageImages.map(({ width, height }) => ({ width, height }))).toEqual([
|
||||
{ width: 64, height: 64 },
|
||||
@@ -1220,11 +1230,32 @@ test('내 정보&설정 keeps desktop density and becomes a 390px horizontal-ide
|
||||
]);
|
||||
expect(myPageImages[0]?.backgroundImage).toContain('/icons/default.jpg');
|
||||
expect(myPageImages[1]?.backgroundImage).toContain('/game/crewtype1.png');
|
||||
await expect(page.locator('.legacy-general-details')).toContainText('계급 29품관');
|
||||
await expect(page.locator('.legacy-general-details')).toContainText('병종 보병');
|
||||
await expect(page.locator('.legacy-general-details')).toContainText('전투 8 · 계략 12 · 사관 4년');
|
||||
await expect(page.locator('.legacy-general-details')).toContainText('승률 62.50% · 승리 5 · 패배 3');
|
||||
await expect(page.locator('.legacy-general-details')).toContainText('살상률 181.84% · 사살 12,345 · 피살 6,789');
|
||||
await expect(page.locator('.general-table')).toContainText('병종보병');
|
||||
await expect(page.locator('.general-table')).toContainText('삭턴6 턴');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('계급29품관');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('전투8회');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('계략12');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('사관4년');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('승률62.50%');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('살상률181.84%');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('사살12,345');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('피살6,789');
|
||||
await expect(page.locator('.battle-general-extra__recent-value')).toHaveText('01-01 00:00');
|
||||
await expect(page.locator('.legacy-general-details')).toHaveCount(0);
|
||||
await expect(page.locator('.battle-general-extra > span')).toHaveText([
|
||||
'명성',
|
||||
'계급',
|
||||
'전투',
|
||||
'승리',
|
||||
'패배',
|
||||
'계략',
|
||||
'사관',
|
||||
'사살',
|
||||
'피살',
|
||||
'승률',
|
||||
'살상률',
|
||||
'최근 전투',
|
||||
]);
|
||||
await expect(page.locator('.item-group')).toContainText('명마');
|
||||
await expect(page.locator('#container')).not.toContainText('che_');
|
||||
await expect(page.locator('.title-row')).toContainText('내 정 보');
|
||||
@@ -2006,6 +2037,26 @@ test('감찰부 keeps the selector interaction and shows the permission error pa
|
||||
await expect(page.locator('.battle-general-card')).toContainText('병종보병');
|
||||
await expect(page.locator('.battle-general-card')).not.toContainText('che_');
|
||||
await expect(page.locator('.battle-general-card')).toHaveAttribute('data-general-basic-card', '');
|
||||
await expect(page.locator('.battle-general-card')).toHaveAttribute('data-general-information-panel', '');
|
||||
await expect(page.locator('.battle-general-card')).toContainText('삭턴6 턴');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('전투8회');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('사관4년');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('승률62.50%');
|
||||
await expect(page.locator('.battle-general-extra')).toContainText('살상률181.84%');
|
||||
await expect(page.locator('.battle-general-extra > span')).toHaveText([
|
||||
'명성',
|
||||
'계급',
|
||||
'전투',
|
||||
'승리',
|
||||
'패배',
|
||||
'계략',
|
||||
'사관',
|
||||
'사살',
|
||||
'피살',
|
||||
'승률',
|
||||
'살상률',
|
||||
'최근 전투',
|
||||
]);
|
||||
const battleImages = await readGeneralPanelImages(page.locator('.battle-general-card'));
|
||||
expect(battleImages).toHaveLength(2);
|
||||
expect(battleImages[0]?.backgroundImage).toContain('/icons/default.jpg');
|
||||
|
||||
@@ -4275,15 +4275,21 @@ test('seasonal map decodes the next background and crossfades it without remount
|
||||
await emitReadModelInvalidation(page, readModelInvalidation({ lobby: true, map: true }));
|
||||
await expect(map).toContainText('185年 4月');
|
||||
await expect(outgoingLayer).toHaveClass(/is-transitioning/u);
|
||||
await expect(outgoingLayer).toHaveCSS('transition-duration', '0.48s');
|
||||
let midpointOpacity = 0;
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
midpointOpacity = Number.parseFloat(
|
||||
await outgoingLayer.evaluate((element) => getComputedStyle(element).opacity)
|
||||
);
|
||||
return midpointOpacity > 0 && midpointOpacity < 1;
|
||||
},
|
||||
{ intervals: [16, 16, 16, 16, 16, 16], timeout: 350 }
|
||||
)
|
||||
.toBe(true);
|
||||
await expect(currentLayer.locator('img')).toHaveAttribute('src', /bg_summer\.jpg/u);
|
||||
await expect(outgoingLayer.locator('img')).toHaveAttribute('src', /bg_spring\.jpg/u);
|
||||
await page.waitForTimeout(180);
|
||||
|
||||
const midpointOpacity = Number.parseFloat(
|
||||
await outgoingLayer.evaluate((element) => getComputedStyle(element).opacity)
|
||||
);
|
||||
expect(midpointOpacity).toBeGreaterThan(0);
|
||||
expect(midpointOpacity).toBeLessThan(1);
|
||||
if (mapSeasonArtifactRoot) {
|
||||
await map.screenshot({ path: resolve(mapSeasonArtifactRoot, 'season-spring-to-summer-midpoint.png') });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user