feat: 장수 계략 명령 전체를 Ref 호환 이관
화계에 묶여 있던 공통 확률·난수·비용·성장 처리를 계략 기반 명령으로 분리한다. 선동·탈취·파괴·화계를 기본 프로필과 계략 분류에 등록하고 Ref 실제 실행, 능력치 상승, 모바일 Chromium 검증을 추가한다.
This commit is contained in:
@@ -136,7 +136,7 @@ describe('buildTurnCommandTable', () => {
|
||||
],
|
||||
군사: ['che_징병', 'che_모병', 'che_훈련', 'che_사기진작', 'che_출병', 'che_집합', 'che_소집해제'],
|
||||
인사: ['che_이동', 'che_인재탐색', 'che_귀환', 'che_임관', 'che_랜덤임관'],
|
||||
계략: ['che_화계'],
|
||||
계략: ['che_선동', 'che_탈취', 'che_파괴', 'che_화계'],
|
||||
국가: ['che_증여', 'che_헌납', 'che_물자조달', 'che_거병', 'che_건국', 'che_선양', 'che_해산'],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,6 +87,9 @@ const LEGACY_STAT_CHANGE_GENERAL_ACTIONS = new Set([
|
||||
'che_견문',
|
||||
'che_무작위건국',
|
||||
'che_화계',
|
||||
'che_선동',
|
||||
'che_파괴',
|
||||
'che_탈취',
|
||||
'che_집합',
|
||||
'cr_건국',
|
||||
'che_이동',
|
||||
|
||||
@@ -23,10 +23,15 @@ const GENERAL_REF_EDITOR_ACTIONS = [
|
||||
'che_징병',
|
||||
'che_출병',
|
||||
'che_농지개간',
|
||||
'che_선동',
|
||||
'che_탈취',
|
||||
'che_파괴',
|
||||
'che_화계',
|
||||
'che_증여',
|
||||
'che_장비매매',
|
||||
] as const;
|
||||
const GENERAL_REF_STRATEGY_ACTIONS = ['che_선동', 'che_탈취', 'che_파괴', 'che_화계'] as const;
|
||||
const GENERAL_REF_STRATEGY_ACTION_SET = new Set<string>(GENERAL_REF_STRATEGY_ACTIONS);
|
||||
const NATION_REF_EDITOR_ACTIONS = ['che_포상', 'che_발령', 'che_증축', 'che_필사즉생'] as const;
|
||||
|
||||
describe('default turn command profile AI coverage', () => {
|
||||
@@ -41,6 +46,9 @@ describe('default turn command profile AI coverage', () => {
|
||||
const profile = await loadTurnCommandProfile();
|
||||
|
||||
expect(profile.general).toEqual(expect.arrayContaining([...GENERAL_REF_EDITOR_ACTIONS]));
|
||||
expect(profile.general.filter((action) => GENERAL_REF_STRATEGY_ACTION_SET.has(action))).toEqual(
|
||||
GENERAL_REF_STRATEGY_ACTIONS
|
||||
);
|
||||
expect(profile.nation).toEqual(expect.arrayContaining([...NATION_REF_EDITOR_ACTIONS]));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ const inputOptions = {
|
||||
const commandTable = {
|
||||
general: [
|
||||
{
|
||||
category: '군사',
|
||||
category: '계략',
|
||||
values: [
|
||||
{
|
||||
key: 'che_화계',
|
||||
@@ -133,6 +133,26 @@ const commandTable = {
|
||||
},
|
||||
],
|
||||
},
|
||||
...[
|
||||
{ key: 'che_선동', name: '선동' },
|
||||
{ key: 'che_탈취', name: '탈취' },
|
||||
{ key: 'che_파괴', name: '파괴' },
|
||||
].map(({ key, name }) => ({
|
||||
key,
|
||||
name,
|
||||
reqArg: true,
|
||||
possible: true,
|
||||
status: 'needsInput',
|
||||
inputFields: [
|
||||
{
|
||||
key: 'destCityId',
|
||||
label: '대상 도시',
|
||||
kind: 'select',
|
||||
required: true,
|
||||
optionSource: 'cities',
|
||||
},
|
||||
],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -461,6 +481,32 @@ const install = async (page: Page, rejectGeneral = false) => {
|
||||
return requests;
|
||||
};
|
||||
|
||||
test('renders and accepts every Ref strategy command at mobile width', async ({ page }) => {
|
||||
await install(page);
|
||||
await page.setViewportSize({ width: 500, height: 900 });
|
||||
await page.goto('/');
|
||||
await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
|
||||
|
||||
const picker = page.getByTestId('command-picker');
|
||||
await picker.getByRole('button', { name: '계략', exact: true }).click();
|
||||
const strategies = [
|
||||
{ name: '선동', guidance: '선택한 도시에 선동을 실행합니다.' },
|
||||
{ name: '탈취', guidance: '선택한 도시에 탈취를 실행합니다.' },
|
||||
{ name: '파괴', guidance: '선택한 도시에 파괴를 실행합니다.' },
|
||||
{ name: '화계', guidance: '선택한 도시에 화계를 실행합니다.' },
|
||||
];
|
||||
for (const strategy of strategies) {
|
||||
const button = picker.getByRole('button', { name: strategy.name, exact: true });
|
||||
await expect(button).toBeVisible();
|
||||
await button.click();
|
||||
const form = picker.getByTestId('command-argument-form');
|
||||
await expect(form.getByTestId('command-argument-guidance')).toContainText(strategy.guidance);
|
||||
await expect(form.locator('select option')).toHaveCount(2);
|
||||
await picker.getByRole('button', { name: '명령 다시 선택', exact: true }).click();
|
||||
}
|
||||
await picker.screenshot({ path: test.info().outputPath('all-strategy-commands-mobile.png') });
|
||||
});
|
||||
|
||||
test('enters general and nation command arguments and sends exact values', async ({ page }) => {
|
||||
const requests = await install(page);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
|
||||
@@ -69,29 +69,36 @@ test('reserves an argument command in the real game API and reads it back from P
|
||||
try {
|
||||
await page.goto('/');
|
||||
await expect(page.getByRole('heading', { name: '전장 현황' })).toBeVisible();
|
||||
await page.getByRole('button', { name: '계략', exact: true }).click();
|
||||
await page.getByRole('button', { name: /화계/ }).click();
|
||||
const form = page.getByTestId('command-argument-form');
|
||||
await expect(form).toBeVisible();
|
||||
const citySelect = form.locator('select');
|
||||
const optionValues = await citySelect
|
||||
.locator('option')
|
||||
.evaluateAll((options) => options.map((option) => (option as HTMLOptionElement).value));
|
||||
const targetCityId = Number(optionValues.find((value) => Number(value) !== context.general.cityId));
|
||||
expect(targetCityId).toBeGreaterThan(0);
|
||||
await citySelect.selectOption(String(targetCityId));
|
||||
|
||||
const generalSection = page.locator('.reserved-section').filter({ hasText: '일반 예턴' });
|
||||
const lastTurn = generalSection.locator('.reserved-item').nth(29);
|
||||
await lastTurn.getByRole('button', { name: '배치' }).click();
|
||||
await expect(lastTurn.locator('.turn-action')).toHaveText('che_화계');
|
||||
const form = page.getByTestId('command-argument-form');
|
||||
for (const strategy of [
|
||||
{ key: 'che_선동', name: '선동' },
|
||||
{ key: 'che_탈취', name: '탈취' },
|
||||
{ key: 'che_파괴', name: '파괴' },
|
||||
{ key: 'che_화계', name: '화계' },
|
||||
]) {
|
||||
await page.getByRole('button', { name: '계략', exact: true }).click();
|
||||
await page.getByRole('button', { name: new RegExp(strategy.name) }).click();
|
||||
await expect(form).toBeVisible();
|
||||
const citySelect = form.locator('select');
|
||||
const optionValues = await citySelect
|
||||
.locator('option')
|
||||
.evaluateAll((options) => options.map((option) => (option as HTMLOptionElement).value));
|
||||
const targetCityId = Number(optionValues.find((value) => Number(value) !== context.general.cityId));
|
||||
expect(targetCityId).toBeGreaterThan(0);
|
||||
await citySelect.selectOption(String(targetCityId));
|
||||
|
||||
const persisted = (await game.turns.reserved.getGeneral.query({ generalId })).turns[29];
|
||||
expect(persisted).toEqual({
|
||||
index: 29,
|
||||
action: 'che_화계',
|
||||
args: { destCityId: targetCityId },
|
||||
});
|
||||
await lastTurn.getByRole('button', { name: '배치' }).click();
|
||||
await expect(lastTurn.locator('.turn-action')).toHaveText(strategy.key);
|
||||
|
||||
const persisted = (await game.turns.reserved.getGeneral.query({ generalId })).turns[29];
|
||||
expect(persisted).toEqual({
|
||||
index: 29,
|
||||
action: strategy.key,
|
||||
args: { destCityId: targetCityId },
|
||||
});
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: '국가:인사', exact: true }).click();
|
||||
await page.getByRole('button', { name: /포상/ }).click();
|
||||
|
||||
Reference in New Issue
Block a user