merge: 동적 한국어 조사 처리 변경을 main에 반영한다
This commit is contained in:
@@ -340,6 +340,10 @@ docs에서 확인해 주세요. 존재하지 않는 명령을 오래된 report
|
|||||||
- Vue component는 PascalCase, composable은 `useX`, 변수/함수는 camelCase,
|
- Vue component는 PascalCase, composable은 `useX`, 변수/함수는 camelCase,
|
||||||
type/class는 PascalCase를 사용해 주세요.
|
type/class는 PascalCase를 사용해 주세요.
|
||||||
- 한국어 domain identifier와 설명은 의미가 더 명확할 때 유지해 주세요.
|
- 한국어 domain identifier와 설명은 의미가 더 명확할 때 유지해 주세요.
|
||||||
|
- toast·dialog·aria-label·로그처럼 사용자에게 노출하는 동적 한국어 문구에 조사를
|
||||||
|
붙일 때는 `@sammo-ts/common/util/JosaUtil`의 `put()` 또는 `pick()`을 사용해 주세요.
|
||||||
|
`` `${value}을(를)` ``, `` `${value}이(가)` ``, `` `${value}(으)로` `` 같은 병기 표기를
|
||||||
|
실제 제품 문구에 남기지 말고, 받침 있는 값과 없는 값의 최종 표시 문구를 검증해 주세요.
|
||||||
- action/command/전투 코드에는 한국 독자가 side effect와 ref 근거를 이해할
|
- action/command/전투 코드에는 한국 독자가 side effect와 ref 근거를 이해할
|
||||||
수 있는 주석을 남기되 코드의 반복 설명은 피해 주세요.
|
수 있는 주석을 남기되 코드의 반복 설명은 피해 주세요.
|
||||||
- 기능 이관과 무관한 대규모 formatting/refactor를 같은 변경에 섞지 말아 주세요.
|
- 기능 이관과 무관한 대규모 formatting/refactor를 같은 변경에 섞지 말아 주세요.
|
||||||
|
|||||||
@@ -1781,6 +1781,28 @@ test('내 정보에서 사람 장수의 등록 전콘을 골라 변경한다', a
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('내 정보 아이템 파기 확인은 이름의 받침에 맞는 조사를 쓴다', async ({ page }) => {
|
||||||
|
const state: FixtureState = {
|
||||||
|
permission: 'member',
|
||||||
|
myset: 1,
|
||||||
|
richMyInfo: true,
|
||||||
|
settingMutations: [],
|
||||||
|
accessPages: [],
|
||||||
|
};
|
||||||
|
const prompts: string[] = [];
|
||||||
|
page.on('dialog', async (dialog) => {
|
||||||
|
prompts.push(dialog.message());
|
||||||
|
await dialog.dismiss();
|
||||||
|
});
|
||||||
|
await install(page, state);
|
||||||
|
await page.goto('my-page');
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: '명마', exact: true }).click();
|
||||||
|
await page.getByRole('button', { name: '효경전', exact: true }).click();
|
||||||
|
|
||||||
|
expect(prompts).toEqual(['명마를 버리시겠습니까?', '효경전을 버리시겠습니까?']);
|
||||||
|
});
|
||||||
|
|
||||||
test('장수 생성에서 등록 전콘을 골라 생성 요청에 전달한다', async ({ page }) => {
|
test('장수 생성에서 등록 전콘을 골라 생성 요청에 전달한다', async ({ page }) => {
|
||||||
const firstIconId = '3f804277-584f-4f44-b39c-9ecf40d1ed31';
|
const firstIconId = '3f804277-584f-4f44-b39c-9ecf40d1ed31';
|
||||||
const secondIconId = 'f6af46a2-809a-481d-b66d-0f7bbb706780';
|
const secondIconId = 'f6af46a2-809a-481d-b66d-0f7bbb706780';
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ test('암행부 행을 도시별로 나누고 수뇌의 인사부 즉시 임명
|
|||||||
return { borderTopWidth: style.borderTopWidth, backgroundColor: style.backgroundColor };
|
return { borderTopWidth: style.borderTopWidth, backgroundColor: style.backgroundColor };
|
||||||
});
|
});
|
||||||
expect(disabledStyle).toEqual({ borderTopWidth: '0px', backgroundColor: 'rgba(0, 0, 0, 0)' });
|
expect(disabledStyle).toEqual({ borderTopWidth: '0px', backgroundColor: 'rgba(0, 0, 0, 0)' });
|
||||||
const appointButton = page.getByRole('button', { name: '장료을(를) 허창 태수로 임명' });
|
const appointButton = page.getByRole('button', { name: '장료를 허창 태수로 임명' });
|
||||||
await appointButton.hover();
|
await appointButton.hover();
|
||||||
expect(await appointButton.evaluate((button) => getComputedStyle(button).cursor)).toBe('pointer');
|
expect(await appointButton.evaluate((button) => getComputedStyle(button).cursor)).toBe('pointer');
|
||||||
await appointButton.focus();
|
await appointButton.focus();
|
||||||
@@ -406,6 +406,7 @@ test('암행부 행을 도시별로 나누고 수뇌의 인사부 즉시 임명
|
|||||||
await page.screenshot({ path: testInfo.outputPath('nation-city-integrated-desktop.png'), fullPage: true });
|
await page.screenshot({ path: testInfo.outputPath('nation-city-integrated-desktop.png'), fullPage: true });
|
||||||
await appointButton.click();
|
await appointButton.click();
|
||||||
await expect.poll(() => state.appointmentInputs).toEqual([{ destGeneralId: 21, destCityId: 1, officerLevel: 4 }]);
|
await expect.poll(() => state.appointmentInputs).toEqual([{ destGeneralId: 21, destCityId: 1, officerLevel: 4 }]);
|
||||||
|
await expect(page.getByTestId('game-toast')).toContainText('장료를 허창 태수로 임명했습니다.');
|
||||||
await expect(page.locator('.city[data-city-id="1"] .officer-4-value')).toHaveText('장료');
|
await expect(page.locator('.city[data-city-id="1"] .officer-4-value')).toHaveText('장료');
|
||||||
await expect(page.locator('.city[data-city-id="1"] .officer-4-value')).toHaveClass(/effective-officer/u);
|
await expect(page.locator('.city[data-city-id="1"] .officer-4-value')).toHaveClass(/effective-officer/u);
|
||||||
await expect(page.locator('.city[data-city-id="1"] tr[data-general-id="21"] .mode-4')).toBeDisabled();
|
await expect(page.locator('.city[data-city-id="1"] tr[data-general-id="21"] .mode-4')).toBeDisabled();
|
||||||
@@ -425,7 +426,7 @@ test('수뇌 대상은 재확인하고 일반 장수에게는 임명 버튼을
|
|||||||
await page.getByRole('button', { name: '암행부 연동' }).click();
|
await page.getByRole('button', { name: '암행부 연동' }).click();
|
||||||
await page.getByRole('button', { name: '인사부 연동' }).click();
|
await page.getByRole('button', { name: '인사부 연동' }).click();
|
||||||
|
|
||||||
const chiefButton = page.getByRole('button', { name: '순욱을(를) 허창 태수로 임명' });
|
const chiefButton = page.getByRole('button', { name: '순욱을 허창 태수로 임명' });
|
||||||
expect(await chiefButton.evaluate((button) => getComputedStyle(button).color)).toBe('rgb(255, 0, 0)');
|
expect(await chiefButton.evaluate((button) => getComputedStyle(button).color)).toBe('rgb(255, 0, 0)');
|
||||||
page.once('dialog', async (dialog) => {
|
page.once('dialog', async (dialog) => {
|
||||||
expect(dialog.message()).toBe('수뇌입니다. 임명할까요?');
|
expect(dialog.message()).toBe('수뇌입니다. 임명할까요?');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { JosaUtil } from '@sammo-ts/common/util/JosaUtil';
|
||||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||||
import SortableStringList from '../components/ui/SortableStringList';
|
import SortableStringList from '../components/ui/SortableStringList';
|
||||||
import { trpc } from '../utils/trpc';
|
import { trpc } from '../utils/trpc';
|
||||||
@@ -365,7 +366,7 @@ const dieOnPrestart = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const dropItem = (item: { key: ItemSlotKey; slotName: string; displayName: string | null; code: string | null }) =>
|
const dropItem = (item: { key: ItemSlotKey; slotName: string; displayName: string | null; code: string | null }) =>
|
||||||
confirmMutation(`${item.displayName ?? item.slotName}을(를) 버리시겠습니까?`, () =>
|
confirmMutation(`${JosaUtil.put(item.displayName ?? item.slotName, '을')} 버리시겠습니까?`, () =>
|
||||||
trpc.general.dropItem.mutate({ itemType: item.key })
|
trpc.general.dropItem.mutate({ itemType: item.key })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||||
|
import { JosaUtil } from '@sammo-ts/common/util/JosaUtil';
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { formatReservedCommandBrief } from '../components/command/reservedCommandBrief';
|
import { formatReservedCommandBrief } from '../components/command/reservedCommandBrief';
|
||||||
@@ -59,6 +60,8 @@ const sortOptions = [
|
|||||||
'규모',
|
'규모',
|
||||||
].map((label, index) => ({ value: index + 1, label }));
|
].map((label, index) => ({ value: index + 1, label }));
|
||||||
const officerLabels: Record<OfficerLevel, string> = { 4: '태수', 3: '군사', 2: '종사' };
|
const officerLabels: Record<OfficerLevel, string> = { 4: '태수', 3: '군사', 2: '종사' };
|
||||||
|
const appointmentDescription = (city: City, general: SecretGeneral, level: OfficerLevel): string =>
|
||||||
|
`${JosaUtil.put(general.name, '을')} ${city.name} ${JosaUtil.put(officerLabels[level], '으로')} 임명`;
|
||||||
const generalsForCity = (cityId: number) => data.value?.generals.filter((general) => general.cityId === cityId) ?? [];
|
const generalsForCity = (cityId: number) => data.value?.generals.filter((general) => general.cityId === cityId) ?? [];
|
||||||
const secretGeneralsForCity = (cityId: number) =>
|
const secretGeneralsForCity = (cityId: number) =>
|
||||||
secretData.value?.generals.filter((general) => general.cityId === cityId) ?? [];
|
secretData.value?.generals.filter((general) => general.cityId === cityId) ?? [];
|
||||||
@@ -262,7 +265,7 @@ const appointCityOfficer = async (city: City, general: SecretGeneral, level: Off
|
|||||||
destCityId: city.id,
|
destCityId: city.id,
|
||||||
officerLevel: level,
|
officerLevel: level,
|
||||||
});
|
});
|
||||||
showSuccessToast(`${general.name}을(를) ${city.name} ${officerLabels[level]}로 임명했습니다.`);
|
showSuccessToast(`${appointmentDescription(city, general, level)}했습니다.`);
|
||||||
try {
|
try {
|
||||||
await refreshIntegratedData();
|
await refreshIntegratedData();
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
@@ -652,7 +655,7 @@ onMounted(async () => {
|
|||||||
:disabled="
|
:disabled="
|
||||||
!canAppoint(city.id, general.id, level) || pendingAppointment !== ''
|
!canAppoint(city.id, general.id, level) || pendingAppointment !== ''
|
||||||
"
|
"
|
||||||
:aria-label="`${general.name}을(를) ${city.name} ${officerLabels[level]}로 임명`"
|
:aria-label="appointmentDescription(city, general, level)"
|
||||||
@click="appointCityOfficer(city, general, level)"
|
@click="appointCityOfficer(city, general, level)"
|
||||||
>
|
>
|
||||||
{{ officerLabels[level].slice(0, 1) }}
|
{{ officerLabels[level].slice(0, 1) }}
|
||||||
|
|||||||
Reference in New Issue
Block a user