Files
Gnuboard7/tests/Playwright/specs/layout-editor/default-node-design.spec.ts
T
2026-07-01 10:30:32 +09:00

82 lines
4.4 KiB
TypeScript

/**
* Layout Editor — 팔레트 defaultNode 공통 디자인 className 반영 회귀 가드.
*
* 배경:
* - 팔레트에서 컴포넌트를 추가하면 editor-spec.json 의 defaultNode 가 캔버스 트리에 삽입된다.
* - 이전에는 defaultNode 가 placeholder(inline style / dashed border) 수준이라 추가된 요소가
* 템플릿의 실제 Tailwind 디자인과 동떨어졌다. 본 작업에서 defaultNode.props.className 을
* 운영 레이아웃의 대표 패턴으로 채웠다.
* - 본 spec 은 편집기에서 Button / Input 을 추가했을 때 캔버스에 렌더된 요소가 placeholder 가
* 아닌 공통 디자인 className 으로 렌더되는지 검증한다. (Vitest 는 JSON+격리 렌더, 본 spec 은
* 실제 편집기 chrome + 캔버스 통합 렌더 — feedback_wysiwyg_preview_user_page_parity)
*
* 두 번들 템플릿 모두 검증 (T8 양쪽 변종):
* - sirsoft-basic: Button → rounded-lg, Input → border 패턴
* - sirsoft-admin_basic: Button → bg-blue-600, Input → "input" 유틸 클래스
*
* @scenario palette_open + default_node_source:editorSpecDefaultNode
* @effects palette_default_node_className_replicated_into_inserted_node
* @effects palette_default_node_className_reflects_template_design_system_tokens
* @effects palette_default_node_button_omits_variant_size_uses_className_only
*/
import { test, expect, issueToken, authenticatePage } from '../../fixtures/auth';
type Probe = { template: string; component: string; token: string };
const probes: Probe[] = [
{ template: 'sirsoft-basic', component: 'Button', token: 'rounded-lg' },
{ template: 'sirsoft-basic', component: 'Input', token: 'border' },
{ template: 'sirsoft-admin_basic', component: 'Button', token: 'bg-blue-600' },
{ template: 'sirsoft-admin_basic', component: 'Input', token: 'input' },
];
test.describe('@layout-editor defaultNode 공통 디자인 반영', () => {
for (const { template, component, token } of probes) {
test(`${template} — ${component} 추가 시 캔버스에 "${token}" className 반영`, async ({ page }) => {
const tokenAuth = issueToken('core.templates.layouts.edit');
await authenticatePage(page, tokenAuth);
await page.goto(`/admin/layout-editor/${template}`);
await page.waitForLoadState('domcontentloaded', { timeout: 30_000 });
// 로케일 비종속 — testid 로 "요소 추가" 버튼을 잡는다 (편집기 chrome 은 admin 로케일에 따라 ko/en 모두 가능)
const addElementBtn = page.getByTestId('g7le-toolbar-add-element');
await addElementBtn.waitFor({ state: 'visible', timeout: 30_000 });
// 라우트 트리에서 실제 편집 가능한 leaf 라우트를 선택해야 팔레트/캔버스가 렌더된다.
// redirect 라우트(badge-redirect 동반)는 레이아웃이 없어 "Select a route..." 안내만 뜨므로 제외.
// route 트리 path 형식이 템플릿마다 달라 ?route= 파라미터 대신 트리 항목을 직접 클릭.
const editableRouteItem = page
.getByTestId('g7le-route-tree-item')
.filter({ hasNot: page.getByTestId('g7le-route-tree-badge-redirect') })
.first();
await editableRouteItem.waitFor({ state: 'visible', timeout: 30_000 });
await editableRouteItem.click();
await expect(addElementBtn).toBeEnabled({ timeout: 30_000 });
// 캔버스 내 해당 className 요소의 삽입 전 개수 (거짓 양성 회피 — 증가분을 검증)
// token 은 단순 클래스명이라 CSS.escape 불필요 (테스트 본문은 Node 컨텍스트 — CSS 전역 없음)
const countSelector = `[data-testid="g7le-preview-canvas"] .${token}`;
const before = await page.locator(countSelector).count();
// 팔레트 열고 대상 컴포넌트 추가
await addElementBtn.click();
const paletteItem = page.getByTestId(`g7le-palette-item-${component}`);
await paletteItem.waitFor({ state: 'visible', timeout: 10_000 });
await paletteItem.click();
// 삽입 후 캔버스에 해당 className 요소가 증가해야 함 (defaultNode className 복제)
await expect
.poll(
async () => page.locator(countSelector).count(),
{
message: `${component} 추가 후 "${token}" className 요소가 캔버스에서 증가해야 함 (defaultNode className 복제)`,
timeout: 15_000,
},
)
.toBeGreaterThan(before);
});
}
});