표현식 해석 로직이 엔진 전역에 복제되면서 복사본마다 기능 집합과 판정 기준이 갈라졌고, 같은 작성이 어느 렌더 경로를 타느냐에 따라 다른 결과를 냈다. 증상 대부분이 예외가 아니라 조용한 오답 또는 값 소실이라 재현 위치를 특정하기 어려웠다. 판정만 BindingShape 단일 출처로 모으고 실행은 지점별로 유지했다. 전면 통합은 회귀 위험이 크고, 갈라진 것은 실행이 아니라 판정이었기 때문이다. - 파이프 평가를 문자열 보간에 위임하면서 생긴 비대칭 3건 해소 (중괄호 노출 / _computed stale 전파 / 원본 타입 소실) - 파이프 분기 누락 8지점 보강 — 평가 지점 모집단을 스캔해 가드를 강제하는 회귀선 신설 - 단일 바인딩·복잡식 판정 14곳을 정본으로 치환, 빈 바인딩 봉인 - BINDING_PATTERN 정규식을 수동 스캐너로 교체, resolveObject 를 key 단위 예외 격리로 전환 - iteration + 항목 참조 if 가 목록 전체를 지우던 결함 해소, 반복 경로 기능 격차 정렬 - 템플릿 컴포넌트의 new Function 자체 평가기 제거 → G7Core.evaluateCondition 위임 - 컨텍스트에 식별자가 아닌 키가 하나라도 있으면 그 컨텍스트의 모든 식이 평가되지 못하던 결함 수정 - SEO 봇 렌더러도 같은 규칙으로 정렬 — React 만 고치면 봇 화면에서만 목록이 빈다 - audit walker 가 레이아웃의 74%(partial 루트 444/598)로 내려가지 못하던 사각 해소, 신규 룰 5종 등재
68 lines
3.3 KiB
TypeScript
68 lines
3.3 KiB
TypeScript
/**
|
|
* E2E: 반복 서브트리 진입 키의 `skipBindingKeys` 선언 정합 (engine-v1.56.1 / 템플릿)
|
|
*
|
|
* 배경: `cardColumns[].cellChildren` / `footerCells[].children` / `footerCardChildren` 은
|
|
* **반복 렌더 경로**(`renderItemChildren`)가 항목 컨텍스트와 함께 해석하는 자리다. 따라서
|
|
* 컴포넌트 선언(`components.json`)의 `skipBindingKeys` 에 그 키가 들어 있어야 props 선평가를
|
|
* 건너뛴다. 선언이 빠지거나 이름이 틀리면(P5 이전 상태: `cardChildren` 오기, `footerCells`·
|
|
* `footerCardChildren` 누락) 항목 변수가 없는 시점에 표현식이 먼저 평가되어
|
|
* **셀이 비거나 `{{...}}` 원본이 화면에 남는다**. 예외는 발생하지 않는다.
|
|
*
|
|
* 계획서 「브라우저 검증」의 "skipBindingKeys 키 변동은 반복 서브트리 전부가 영향권 →
|
|
* 대표 화면 시각 확인 필수" 항목을 재현 가능한 형태로 고정한다. 정적 검사(audit 룰
|
|
* `component-skipbinding-keys-complete`)는 **선언과 구현의 일치**만 보므로, 실제 화면에서
|
|
* 값이 채워지는지는 여기서만 잡힌다.
|
|
*
|
|
* 대상은 `cardColumns` 를 실제로 쓰면서 데이터가 항상 존재하는 화면을 골랐다
|
|
* (모듈·플러그인은 번들 확장이 있어 목록이 비지 않는다).
|
|
*
|
|
* @scenario repeat_subtree_card_columns_render
|
|
* @effects item_values_rendered_no_raw_binding
|
|
*/
|
|
import { test, expect, issueToken, authenticatePage } from '../fixtures/auth';
|
|
|
|
const SCREENS = [
|
|
{
|
|
name: '모듈 목록 (카드 뷰)',
|
|
path: '/admin/modules',
|
|
permissions: ['core.modules.read'],
|
|
/** 번들 모듈이 반드시 하나는 노출된다 */
|
|
expectText: '이커머스',
|
|
},
|
|
{
|
|
name: '플러그인 목록 (카드 뷰)',
|
|
path: '/admin/plugins',
|
|
permissions: ['core.plugins.read'],
|
|
expectText: 'CKEditor',
|
|
},
|
|
] as const;
|
|
|
|
test.describe('반복 서브트리 — cardColumns 항목이 실제 값으로 렌더된다', () => {
|
|
for (const screen of SCREENS) {
|
|
// @scenario repeat_subtree_card_columns_render
|
|
// @effects item_values_rendered_no_raw_binding
|
|
test(`${screen.name}: 카드 셀이 채워지고 원본 바인딩이 남지 않는다`, async ({ page }) => {
|
|
test.setTimeout(120_000);
|
|
|
|
const token = issueToken(...screen.permissions);
|
|
await authenticatePage(page, token);
|
|
|
|
await page.goto(screen.path, { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
|
|
|
// ① 목록이 실제로 그려질 때까지 기다린다. 이 대기 없이 아래 부재 단언만 두면
|
|
// 화면이 비어 있어도 "원본 바인딩 없음" 으로 조용히 통과한다(공허한 통과).
|
|
await expect(page.getByText(screen.expectText).first()).toBeVisible({ timeout: 60_000 });
|
|
|
|
// ② 반복 서브트리(cardColumns[].cellChildren) 안의 표현식이 해석되지 않은 채
|
|
// 남으면 화면에 `{{` 가 그대로 보인다.
|
|
const bodyText = (await page.locator('body').innerText()) ?? '';
|
|
expect(bodyText).not.toContain('{{');
|
|
|
|
// ③ 번역 면제 마커(Unicode Noncharacter)도 화면으로 새지 않아야 한다
|
|
// — 반복 경로의 raw 마커 unwrap 회귀선(engine-v1.56.3).
|
|
expect(bodyText).not.toContain('');
|
|
expect(bodyText).not.toContain('');
|
|
});
|
|
}
|
|
});
|