From d71f16e56b93bd2e725c85ce294cc54e9b49a6e6 Mon Sep 17 00:00:00 2001
From: hided62
Date: Fri, 21 Aug 2026 17:15:16 +0000
Subject: [PATCH] =?UTF-8?q?fix(game-ui):=20=EA=B5=AD=EA=B0=80=20=EB=A9=94?=
=?UTF-8?q?=EC=8B=9C=EC=A7=80=20=ED=8E=B8=EC=A7=91=EA=B8=B0=20=EC=A0=95?=
=?UTF-8?q?=EB=A0=AC=EA=B3=BC=20=EB=AA=A9=EB=A1=9D=EC=9D=84=20=EB=B0=94?=
=?UTF-8?q?=EB=A1=9C=EC=9E=A1=EB=8A=94=EB=8B=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/game-api/test/nationHtml.test.ts | 3 +-
app/game-frontend/e2e/nationOffices.spec.ts | 76 ++++++-
.../src/components/ui/LegacyHtmlEditor.vue | 198 ++++++++++++++++--
.../src/views/NationStratFinanView.vue | 47 ++++-
4 files changed, 301 insertions(+), 23 deletions(-)
diff --git a/app/game-api/test/nationHtml.test.ts b/app/game-api/test/nationHtml.test.ts
index 2dbd1f73..6dae4b9c 100644
--- a/app/game-api/test/nationHtml.test.ts
+++ b/app/game-api/test/nationHtml.test.ts
@@ -73,7 +73,7 @@ describe('nation HTML purification', () => {
const source = [
'',
'방침',
- '
',
+ '
',
].join('');
const clean = purifyNationHtml(source);
@@ -83,6 +83,7 @@ describe('nation HTML purification', () => {
'style="font-family:Pretendard, sans-serif;font-size:22px;color:#123456;background-color:#fedcba"'
);
expect(clean).toContain('
');
+ expect(clean).toContain('class="custom-image-align-right"');
expect(clean).toContain(
'src="https://sam-image.hided.net/uploads/core2026/0123456789abcdef0123456789abcdef.webp"'
);
diff --git a/app/game-frontend/e2e/nationOffices.spec.ts b/app/game-frontend/e2e/nationOffices.spec.ts
index 9b0c8865..acf920c4 100644
--- a/app/game-frontend/e2e/nationOffices.spec.ts
+++ b/app/game-frontend/e2e/nationOffices.spec.ts
@@ -624,6 +624,21 @@ test('finance editor preserves Ref formatting controls and uploads images throug
expect(await editorFrame.evaluate((element) => getComputedStyle(element).backgroundColor)).toBe('rgba(0, 0, 0, 0)');
expect(await editor.evaluate((element) => getComputedStyle(element).backgroundColor)).toBe('rgba(0, 0, 0, 0)');
+ await editor.fill('첫 번째 항목');
+ await page.getByRole('button', { name: '번호 목록' }).click();
+ await expect(editor.locator('ol')).toHaveCSS('list-style-type', 'decimal');
+ await expect(editor.locator('li')).toContainText('첫 번째 항목');
+ await screenshot(page, 'core-finance-numbered-list-desktop.png');
+ await page.getByRole('button', { name: '번호 목록' }).click();
+
+ const alignmentIcons = await page
+ .getByRole('toolbar', { name: '서식' })
+ .getByRole('button', { name: /^(왼쪽|가운데|오른쪽) 정렬$/ })
+ .locator('svg')
+ .evaluateAll((icons) => icons.map((icon) => icon.innerHTML));
+ expect(alignmentIcons).toHaveLength(3);
+ expect(new Set(alignmentIcons).size).toBe(3);
+
await editor.fill('서식 검증');
await editor.press('Control+A');
await page.getByRole('combobox', { name: '글꼴', exact: true }).selectOption('Gungsuh, serif');
@@ -661,11 +676,27 @@ test('finance editor preserves Ref formatting controls and uploads images throug
await expect.poll(() => state.uploadDataUrl).toMatch(/^data:image\/png;base64,/);
await expect(editor.locator('hr')).toHaveCount(1);
- await expect(editor.locator('img')).toHaveAttribute(
+ const uploadedImage = editor.locator('img');
+ await expect(uploadedImage).toHaveAttribute(
'src',
'https://sam-image.hided.net/uploads/core2026/0123456789abcdef0123456789abcdef.webp'
);
+ await uploadedImage.click();
+ const imageToolbar = page.getByRole('toolbar', { name: '이미지 정렬' });
+ await expect(imageToolbar).toBeVisible();
+ await expect(imageToolbar.getByRole('button')).toHaveCount(5);
+ await screenshot(page, 'core-finance-image-toolbar-desktop.png');
+ await imageToolbar.getByRole('button', { name: '이미지 오른쪽 정렬' }).click();
+ await expect(uploadedImage).toHaveClass(/custom-image-align-right/);
+ await expect(uploadedImage).toHaveCSS('display', 'block');
+ await expect(uploadedImage).toHaveCSS('margin-right', '0px');
+
+ await page.getByRole('toolbar', { name: '서식' }).getByRole('button', { name: '가운데 정렬' }).click();
+ await expect(uploadedImage).toHaveClass(/custom-image-align-center/);
+ await uploadedImage.click();
+ await imageToolbar.getByRole('button', { name: '이미지 오른쪽 정렬' }).click();
+
const imageButton = page.getByRole('button', { name: '이미지', exact: true });
await imageButton.hover();
await expect(imageButton).toHaveCSS('border-color', 'rgb(157, 200, 240)');
@@ -691,11 +722,54 @@ test('finance editor preserves Ref formatting controls and uploads images throug
expect(state.noticeMutationInput).toContain('background-color: rgb(254, 220, 186)');
expect(state.noticeMutationInput).toContain('text-align: center');
expect(state.noticeMutationInput).toContain('
');
+ expect(state.noticeMutationInput).toContain('class="custom-image-align-right"');
expect(state.noticeMutationInput).toContain(
'https://sam-image.hided.net/uploads/core2026/0123456789abcdef0123456789abcdef.webp'
);
});
+test('recruitment editor keeps the Ref 870px content width in desktop and scales it to 500px on mobile', async ({
+ page,
+}) => {
+ await installFixture(page, { role: 'head', rate: 20 });
+ await page.setViewportSize({ width: 1000, height: 900 });
+ await gotoOffice(page, 'nation/finance');
+ await page.getByRole('button', { name: '임관 권유문 수정' }).click();
+
+ const form = page.locator('#scout-message-form');
+ const editorFrame = form.locator('.legacy-html-editor');
+ const desktop = await form.evaluate((element) => {
+ const frame = element.querySelector('.legacy-html-editor')!;
+ const formRect = element.getBoundingClientRect();
+ const frameRect = frame.getBoundingClientRect();
+ return {
+ formWidth: formRect.width,
+ frameWidth: frameRect.width,
+ leftInset: frameRect.left - formRect.left,
+ rightInset: formRect.right - frameRect.right,
+ };
+ });
+ expect(desktop.formWidth).toBe(1000);
+ expect(desktop.frameWidth).toBe(870);
+ expect(desktop.leftInset).toBe(130);
+ expect(desktop.rightInset).toBe(0);
+
+ await page.setViewportSize({ width: 500, height: 900 });
+ const mobile = await editorFrame.evaluate((element) => {
+ const rect = element.getBoundingClientRect();
+ return {
+ visualWidth: rect.width,
+ sourceWidth: element.clientWidth,
+ toolbarWidth: element.querySelector('[role="toolbar"]')!.getBoundingClientRect().width,
+ };
+ });
+ expect(mobile.sourceWidth).toBe(870);
+ expect(mobile.visualWidth).toBeCloseTo(500, 1);
+ expect(mobile.toolbarWidth).toBeCloseTo(500, 1);
+ expect(await page.locator('.page-finance').evaluate((element) => element.scrollWidth)).toBeLessThanOrEqual(500);
+ await screenshot(page, 'core-finance-scout-editor-mobile.png');
+});
+
test('finance adopts the server-purified notice and scout message before rendering the saved preview', async ({
page,
}) => {
diff --git a/app/game-frontend/src/components/ui/LegacyHtmlEditor.vue b/app/game-frontend/src/components/ui/LegacyHtmlEditor.vue
index 199866e7..9f68d68e 100644
--- a/app/game-frontend/src/components/ui/LegacyHtmlEditor.vue
+++ b/app/game-frontend/src/components/ui/LegacyHtmlEditor.vue
@@ -1,16 +1,17 @@