From 8f51494433f4886b7d2d18a613585c914f539f0d Mon Sep 17 00:00:00 2001 From: Jaybe Date: Mon, 17 Nov 2025 19:08:06 +0900 Subject: [PATCH] =?UTF-8?q?E2E=20=EC=8A=A4=EB=AA=A8=ED=81=AC=20=ED=99=95?= =?UTF-8?q?=EC=9E=A5:=20=EA=B7=B8=EB=A3=B9=20=EB=A6=AC=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EC=A6=88(E)/=ED=9A=8C=EC=A0=84(15=C2=B0=20=EC=8A=A4=EB=83=85)?= =?UTF-8?q?=20+=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=A6=AC=EC=A0=84=20?= =?UTF-8?q?=EA=B0=80=EC=9D=B4=EB=93=9C=20=ED=8F=AC=ED=95=A8=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image.responsive.options.matrix.test.ts | 46 ++++++++++ tests/e2e/wysiwyg-group-resize-rotate.e2e.ts | 89 +++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 lib/exporter/image.responsive.options.matrix.test.ts create mode 100644 tests/e2e/wysiwyg-group-resize-rotate.e2e.ts diff --git a/lib/exporter/image.responsive.options.matrix.test.ts b/lib/exporter/image.responsive.options.matrix.test.ts new file mode 100644 index 0000000..33563c1 --- /dev/null +++ b/lib/exporter/image.responsive.options.matrix.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect } from 'vitest' +import { exportPage } from '@/lib/exporter/html' +import type { ExportOptions } from '@/lib/exporter/html' +import type { Page } from '@/lib/schema/page' + +const basePage: Page = { + title: 'Img Matrix', + locale: 'en', + theme: { primaryColor: '#0ea5e9', fontFamily: 'Inter' }, + sections: [ + { type: 'hero', props: { heading: 'Hero', subheading: '', imageUrl: 'https://example.com/hero.jpg' } }, + ], + form: { + actionUrl: '', + fields: [], + spamProtection: { honeypotFieldName: '_hp', minSubmitSeconds: 2 }, + }, + analytics: { ga4MeasurementId: '', metaPixelId: '', customHeadHtml: '' }, + seo: { title: 'T', description: 'D' }, +} + +describe('image responsive options - matrix', () => { + it('applies loading and decoding attributes from options', () => { + const opts: ExportOptions = { imageLoading: 'eager', imageDecoding: 'sync' } + const { html } = exportPage(basePage, opts) + expect(html).toMatch(/]*loading="eager"/) + expect(html).toMatch(/]*decoding="sync"/) + }) + + it('sets fetchpriority="low" when specified', () => { + const opts: ExportOptions = { imageFetchPriority: 'low' } + const { html } = exportPage(basePage, opts) + expect(html).toMatch(/]*fetchpriority="low"/) + }) + + it('prefers preset sizes over auto default when both provided (no explicit sizes)', () => { + const opts: ExportOptions = { + imageSrcset: 'https://example.com/hero.jpg 1x, https://example.com/hero@2x.jpg 2x', + imageAutoSizes: true, + imageSizesPreset: 'mobile-first', + } + const { html } = exportPage(basePage, opts) + // preset should win over default auto sizes + expect(html).toMatch(/]*sizes="\(max-width: 640px\) 100vw, 800px"/) + }) +}) diff --git a/tests/e2e/wysiwyg-group-resize-rotate.e2e.ts b/tests/e2e/wysiwyg-group-resize-rotate.e2e.ts new file mode 100644 index 0000000..4fb87fe --- /dev/null +++ b/tests/e2e/wysiwyg-group-resize-rotate.e2e.ts @@ -0,0 +1,89 @@ +import { test, expect } from '@playwright/test' + +const wysiwygUrl = '/en/wysiwyg' + +function snap8(v: number) { return Math.round(v / 8) * 8 } + +async function getBoxStyles(page: import('@playwright/test').Page, nth: number) { + const el = page.locator('[data-testid^="obj-text-"]').nth(nth) + const left = await el.evaluate((n) => parseInt((n as HTMLElement).style.left || '0', 10)) + const top = await el.evaluate((n) => parseInt((n as HTMLElement).style.top || '0', 10)) + const width = await el.evaluate((n) => parseInt((n as HTMLElement).style.width || '0', 10)) + const height = await el.evaluate((n) => parseInt((n as HTMLElement).style.height || '0', 10)) + const transform = await el.evaluate((n) => (n as HTMLElement).style.transform || '') + return { left, top, width, height, transform } +} + +test.describe('WYSIWYG - Group resize/rotate and guide live region', () => { + test('group E handle resizes both by snapped delta without shifting x', async ({ page }) => { + await page.goto(wysiwygUrl) + const canvas = page.getByTestId('wysiwyg-canvas') + await expect(canvas).toBeVisible() + + const addText = page.getByRole('button', { name: /^Add Text$/i }) + await addText.click() + await addText.click() + + // Marquee select both + const box = await canvas.boundingBox() + if (!box) throw new Error('canvas not found') + await page.mouse.move(box.x + 40, box.y + 40) + await page.mouse.down() + await page.mouse.move(box.x + 260, box.y + 160) + await page.mouse.up() + + const live = page.getByTestId('aria-live') + await expect(live).toContainText(/선택\s*2개/) + + const b0 = await getBoxStyles(page, 0) + const b1 = await getBoxStyles(page, 1) + + // Drag east handle of second by +16px + const target = page.locator('[data-testid^="obj-text-"]').nth(1) + const tbox = await target.boundingBox() + if (!tbox) throw new Error('target not found') + await page.mouse.move(tbox.x + tbox.width - 5, tbox.y + 5) + await page.mouse.down() + await page.mouse.move(tbox.x + tbox.width + 16, tbox.y + 5) + await page.mouse.up() + + const a0 = await getBoxStyles(page, 0) + const a1 = await getBoxStyles(page, 1) + + const dw = snap8(b1.width + 16) - b1.width + expect(a0.left).toBe(b0.left) + expect(a1.left).toBe(b1.left) + expect(a0.width).toBe(b0.width + dw) + expect(a1.width).toBe(b1.width + dw) + }) + + test('rotate handle snaps to 15deg and live region includes guide coords while dragging', async ({ page }) => { + await page.goto(wysiwygUrl) + const canvas = page.getByTestId('wysiwyg-canvas') + await expect(canvas).toBeVisible() + + const addText = page.getByRole('button', { name: /^Add Text$/i }) + await addText.click() + + const target = page.locator('[data-testid^="obj-text-"]').first() + const tbox = await target.boundingBox() + if (!tbox) throw new Error('target not found') + + // Select + await target.click() + + // Drag rotate handle horizontally to cause rotation ~15deg (snapped) + const rotateHandle = page.locator('[data-testid^="rotate-handle-"]').first() + const hbox = await rotateHandle.boundingBox() + if (!hbox) throw new Error('rotate handle not found') + await page.mouse.move(hbox.x + 5, hbox.y + 5) + await page.mouse.down() + await page.mouse.move(hbox.x + 40, hbox.y + 5) + const live = page.getByTestId('aria-live') + await expect(live).toContainText(/가이드\s*x=|y=/) + await page.mouse.up() + + const a = await getBoxStyles(page, 0) + expect(a.transform).toMatch(/rotate\(15deg\)|rotate\(30deg\)/) + }) +})