Files
Gnuboard7/tests/scenarios/page-settings.yaml
T
HeuJung 42eb21320b fix(ecommerce,core): 현금영수증 UI 보완 + 하네스 결함 6건 + 확장점 룰 사각 제거
현금영수증/환불계좌 기능(S3)의 감사 보완과, 그 과정에서 발견한 하네스 결함을
같은 세션에서 일괄 처리한다. 지시에 따라 무관 산출물(namuwiki 문서 초안 포함)도
단일 커밋으로 묶는다.

- 현금영수증 발급 이력 접기/펼치기(W-3) + 결제행별 독립 토글 + aria 상태
- vendor-bundle manifest 해시 재생성 — composer.json version bump 미반영분 정합
- 시나리오 매니페스트 거짓 경로 정정(A/B) + 미작성 부채 :test-files 정직 표기(C)
- layout-* 룰의 resources/extensions 검사 사각 제거 + 확장점 전용 root 스키마 분기
- validate-test-scenario.cjs: 맵형 test_files 크래시 + fallback 파서 covers 오검출 수정
- tosspayments 확장점 setState target 규약 위반 수정
2026-08-06 12:36:31 +09:00

226 lines
19 KiB
YAML

# audit:allow test-scenario-coverage:test-files reason: 페이지 설정은 편집기 모달(8탭 셸/제목 배너/SEO 값 칸/타겟 picker — dnd-kit·합성 이벤트·포털 모달 의존)이라 Playwright 자동화 부적합(text-inline-binding.yaml 동일 정책). 입력 조합·후속 효과 cross product 는 단위 RTL(PageSettingsModal/PageTitleBanner/DataChipValueInput 14 + computed-inheritance 등)이 전수 커버하고, 캔버스 라이브 반영·저장→DB 영속→새로고침 복원·실사용자화면 렌더는 Chrome MCP §공통 검증 매트릭스(L0~L7)로 라이브 실측. SEO 값 칸 키화 0·이중인코딩 0 은 tinker DB 실측이 SSoT.
# test-files 면제 사유: page-settings/PageTitleBanner.test.tsx 미작성 (#383 이월)
feature: 레이아웃 편집기 페이지 설정 모달 (8탭 셸 + 제목 배너 + 캔버스 dataKey/격리/타겟 picker + SEO 값 칸 표현식·칩 확대)
phase: 8
session: S10-1
issue: 383
axes:
# 셸 라우팅·고급 배지·진입점 (~2.2)
active_tab: [meta, seo, init, overlay, computed, state, error, data]
advanced_badge: [none, init_advanced_count, computed_advanced_count, total_summary]
entry_point: [toolbar_button, banner_settings_gear]
toolbar_state: [route_active, base_active, modal_active, extension_disabled, iteration_item_disabled, no_selection_disabled]
# 제목 배너 4상태
banner_state: [title_plaintext_editable, title_i18n_key_editable, title_missing_fallback_readonly, title_expression_advanced_readonly]
banner_edit: [double_click_enter_commit, esc_cancel, plaintext_creates_key, existing_key_updates_value]
banner_mode_visibility: [route_visible, extension_hidden, iteration_item_hidden]
# 기본 정보
meta_field: [title, description, icon, editor_label, permissions]
# 자동 계산 상속
computed_authoring: [preset_9, custom_3step, advanced_preserve]
computed_inheritance: [parent_only, child_only, both_distinct_keys, same_key_route_override, three_level]
computed_inherit_op: [view_source_badge, edit_parent_override, revert_to_common, same_name_add, child_only_patch]
# 캔버스 dataKey/격리
core_datakey: [static_value, binding_degraded, empty_removes_key, dot_path_allowed]
isolated_scope: [toggle_on_seeds_scopeid, toggle_off_removes, scopeid_search_candidate, scopeid_free_input, duplicate_notice]
# 타겟 picker 캔버스 pick
target_picker: [manual_input, canvas_pick_request, canvas_pick_picked, canvas_pick_id_not_assigned_ignored, canvas_pick_cancel, request_id_mismatch_ignored, host_modal_minimize_restore]
# SEO extensions 결선
seo_extensions_supply: [fetch_to_availableExtensions, empty_degrade]
# 에러 처리
error_handler_kind: [none, showErrorPage, navigate, openModal, toast, setState, sequence, parallel]
# 데이터
data_readonly_section: [global_headers, scripts, empty_hint]
# SEO 값 칸 표현식/칩 확대 (표현식 분해 트리 → 값 전용 필드 일반화④)
# 키화 0(값 필드는 $t:custom.* 미생성) + 칩 시각화(데이터연결/설정참조 raw 코드 비노출)
seo_value_field: [og_image, tw_image, og_image_alt] # 값 전용 칸(DataChipValueInput)
seo_text_field: [search_title, search_desc, og_title, og_desc] # 텍스트 칸(I18nTextField, 키화 유지)
seo_value_shape: [plain, data_binding, settings_ref, single_conditional, multi_segment, fallback]
seo_value_op: [type_plaintext, type_binding_focus_retained, blur_to_chip, fx_to_expression_tree, change_data, to_fallback]
# 모듈 자동값 → 연결 칩(역산출). 모듈 seoOgDefaultMeta/seoStructuredDataMeta 가
# 데이터 경로(expr)+라벨을 공급 → 자동값 행을 "상품 이름" 연결 칩으로 표시 + 다른 데이터로 교체.
seo_auto_chip_field: [og_image, og_image_alt, structured_name, structured_offers_price] # 자동값 칩 대상
seo_auto_chip_op: [render_link_chip_with_label, replace_to_override, fallback_plaintext_when_no_meta]
# 자동 계산(computed)·로딩 화면(overlay)·SEO 보조
# 텍스트 칸도 "단순 데이터 연동" 가능하도록 DataChipValueInput/I18nTextField 로 승격. 값/경로 칸은
# 데이터 칩(키화 0), 사용자 표시 문구(spinner)는 다국어 키화. 식별자(id/키이름/숫자인덱스)는 평문.
chip_expand_field:
- computed_preset_param # ComputedForm preset 파라미터(화면상태경로/주소파라미터/기본값)
- computed_sum_field # CustomComputedBuilder 합산 필드
- computed_tooptions_field # toOptions value/label 필드
- computed_nth_prop # nth 속성(경로)
- computed_literal_value # literal 값
- computed_firstof_fallback # firstOf 기본값
- computed_condition_field # 조건 필드
- computed_condition_value # 조건 값
- overlay_spinner_text # 로딩 화면 안내 문구(I18nTextField=다국어 키화)
- seo_og_site_name # og.site_name/locale 등 textRow 7칸(DataChipValueInput)
chip_expand_value_shape: [plain, data_binding, single_conditional, settings_ref]
chip_expand_op: [type_plaintext_no_keyify, search_pick_data_chip, fx_to_expression_tree, keyify_for_display_text_only]
exclusions:
- { active_tab: meta, advanced_badge: init_advanced_count, reason: "기본 정보 탭엔 미환원 항목 개념 없음" }
- { toolbar_state: extension_disabled, banner_mode_visibility: route_visible, reason: "extension 모드는 배너 미표시·버튼 비활성 동시" }
- { computed_inheritance: child_only, computed_inherit_op: revert_to_common, reason: "순수 자식 키는 되돌릴 부모 값 없음(route-override 만 되돌리기)" }
- { core_datakey: binding_degraded, meta_field: title, reason: "바인딩 디그레이드 dataKey 는 읽기전용 — 다른 meta 편집과 직교(중복 조합 배제)" }
- { target_picker: manual_input, seo_extensions_supply: fetch_to_availableExtensions, reason: "직접 입력 picker 는 SEO extensions 결선과 무관 축(직교 배제)" }
# SEO 값 칸 표현식 확대 — 값 칸/텍스트 칸은 컴포넌트 자체가 다름(상호 배제), op 는 칸 종류별로 제한
- { seo_value_field: og_image, seo_text_field: search_title, reason: "값 칸(DataChipValueInput)과 텍스트 칸(I18nTextField)은 서로 다른 컴포넌트 — 한 칸이 동시에 둘일 수 없음(상호 배제)" }
- { seo_value_op: fx_to_expression_tree, seo_value_field: og_image, reason: "fx(표현식으로 바꾸기) 토글은 텍스트 칸 전용 진입점 — 값 칸은 이미 표현식 1급(자동 분해), fx 버튼 비노출" }
- { seo_value_op: change_data, seo_value_shape: plain, reason: "데이터 바꾸기는 단일 바인딩 칩 상태에서만 노출 — 평문은 검색/표현식/기본값 어포던스만" }
- { seo_value_shape: settings_ref, seo_value_op: type_binding_focus_retained, reason: "설정참조는 데이터 검색이 아닌 직접 타이핑/붙여넣기 — focus 유지 회귀는 데이터 바인딩 입력 축에서 대표 검증" }
# 묶음③ 자동 칩 — fallback_plaintext op 는 메타 미선언 키 전용(라벨 칩 op 와 상호 배제)
- { seo_auto_chip_op: fallback_plaintext_when_no_meta, seo_auto_chip_field: structured_offers_price, reason: "메타 미선언 폴백은 임의 키에서 동일 — 대표 1축(og_image)에서만 검증(직교 중복 배제)" }
- { seo_auto_chip_op: replace_to_override, seo_auto_chip_field: structured_name, reason: "구조화 자동 블록은 OFF 미리보기(읽기전용 칩 표시)만 — '다른 데이터로 바꾸기'(override 진입)는 og 키 전용 어포던스" }
- { seo_auto_chip_op: replace_to_override, seo_auto_chip_field: structured_offers_price, reason: "구조화 자동 블록은 OFF 미리보기 읽기전용 — override 진입은 og 키 전용" }
# 전 입력칸 데이터 칩 확대 — 칸 종류별 키화 정책 상호 배제
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_preset_param, reason: "다국어 키화는 사용자 표시 문구(spinner) 전용 — 자동 계산 값/경로 칸은 값 전용(키화 0)" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_sum_field, reason: "동일 — computed 값/경로 칸은 키화 0" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_tooptions_field, reason: "동일" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_nth_prop, reason: "동일" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_literal_value, reason: "동일" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_firstof_fallback, reason: "동일" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_condition_field, reason: "동일" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: computed_condition_value, reason: "동일" }
- { chip_expand_op: keyify_for_display_text_only, chip_expand_field: seo_og_site_name, reason: "og.site_name 은 값 전용(DataChipValueInput, 키화 0) — 키화는 spinner 전용" }
- { chip_expand_op: fx_to_expression_tree, chip_expand_value_shape: plain, reason: "fx(표현식 진입)는 표현식 가능 형태 전용 — 순수 평문은 검색/표현식 어포던스만" }
- { chip_expand_op: search_pick_data_chip, chip_expand_value_shape: settings_ref, reason: "설정참조는 직접 타이핑 — 데이터 검색 칩과 직교(대표 1형태에서 검증)" }
effects:
# 셸 라우팅/배지
- shell_routes_eight_tabs_meta_seo_init_overlay_computed_state_error_data
- shell_tab_click_switches_active_panel
- shell_advanced_badge_counts_unreduced_init_and_computed_items
- shell_advanced_summary_sums_total_when_positive
- shell_no_save_button_patches_immediately_via_patchDocumentRaw
- shell_title_resolves_metaTitle_else_editorLabel_else_path
- shell_close_x_and_footer_call_onClose
# 진입점
- toolbar_page_settings_button_replaces_legacy_data_button
- toolbar_page_settings_active_only_route_base_modal_with_selectedRoute
- toolbar_page_settings_disabled_extension_and_iteration_item
- data_source_crud_absorbed_into_data_tab_no_loss
# 제목 배너
- banner_title_plaintext_editable_double_click_enter
- banner_plaintext_commit_creates_custom_key_and_substitutes_dollar_t
- banner_existing_key_commit_updates_locale_value_keeps_key
- banner_missing_title_falls_back_editor_label_then_path_readonly
- banner_expression_title_readonly_with_advanced_badge
- banner_hidden_in_extension_and_iteration_item_modes
- banner_settings_gear_opens_same_modal_as_toolbar
# 자동 계산 상속 (D-2a/D-2b)
- computed_source_map_distinguishes_route_override_from_route_and_base
- computed_route_override_shows_promoted_badge_overridden
- computed_route_override_revert_button_removes_child_definition_only
- computed_base_source_shows_shared_badge
- computed_pure_child_route_no_badge_no_revert
- computed_parent_edit_shows_override_notice
# 캔버스 dataKey
- core_datakey_widget_sanitizes_identifier_allows_dot_path
- core_datakey_binding_value_degrades_readonly
- core_datakey_empty_removes_node_key_via_nodeKey_apply
- core_datakey_registered_in_core_widgets
- capability_form_div_container_opt_in_datakey_both_templates
# 격리 영역
- isolated_scope_control_mounted_in_property_modal_props_tab
- isolated_scope_toggle_on_seeds_scopeid_off_removes_both_keys
- isolated_scopeid_candidates_built_from_layout_scopes_and_initIsolated
- isolated_scopeid_duplicate_shows_notice_allows_share
# 타겟 picker 캔버스 pick
- target_picker_manual_input_always_works
- target_picker_pick_request_event_fired_and_minimizes_host_modal
- target_picker_picked_event_same_request_id_calls_onChange_and_restores
- target_picker_cancelled_reply_no_onChange_restores_modal
- target_picker_request_id_mismatch_ignored
- canvas_overlay_receives_pick_request_returns_node_id_on_click
- canvas_overlay_pick_id_not_assigned_node_click_ignored
- canvas_overlay_pick_esc_cancels_with_banner
- canvas_overlay_pick_mode_does_not_alter_normal_selection_via_ref
# SEO extensions 결선 (D-4b)
- seo_extensions_fetched_and_passed_to_availableExtensions
# 후보 풀 공용 (D-1 prep)
- candidate_pools_shared_between_shell_and_canvas_overlay
- page_sample_context_derived_from_same_inputs_as_binding_candidates
# SEO 값 칸 표현식/칩 확대 (→ 값 전용 필드④)
- seo_value_field_uses_data_chip_value_input_not_i18n_text_field
- seo_value_field_creates_no_translation_key_on_plaintext_or_binding
- seo_value_plaintext_typing_focus_retained_no_mode_switch_until_blur
- seo_value_binding_blur_renders_link_chip_with_change_data_affordance
- seo_value_settings_ref_renders_link_chip_no_raw_code_exposed
- seo_value_single_conditional_decomposes_into_conditional_value_tree
- seo_value_multi_segment_renders_segmented_value_editor
- seo_value_leaf_input_recurses_data_chip_value_input_not_keyifying
- seo_value_persists_expression_verbatim_in_layout_content_no_double_encode
- seo_value_reloads_and_restores_as_chip_not_raw_text
- seo_text_field_keeps_i18n_keyification_regression_zero
- seo_text_field_fx_toggle_enters_expression_tree_static_conditional_fallback_data
# 묶음③ 모듈 자동값 → 연결 칩(역산출)
- module_declares_seo_default_meta_data_path_and_label_for_og_and_structured
- preview_response_carries_source_expr_and_label_on_module_sourced_rows
- seo_auto_value_renders_link_chip_with_label_not_resolved_plaintext
- seo_auto_value_replace_button_fills_source_expr_as_layout_override
- seo_auto_chip_falls_back_to_plaintext_when_module_declares_no_meta
- structured_auto_block_rows_render_link_chip_from_auto_meta
- meta_method_keys_are_subset_of_operational_declaration_keys
# 자동 계산/로딩/SEO 보조 칸
- computed_value_path_fields_use_data_chip_value_input_no_keyify
- computed_param_passes_binding_candidates_from_page_settings_modal
- computed_plaintext_and_path_input_preserved_regression_zero
- computed_nth_index_and_firstof_candidates_stay_plaintext_by_design
- overlay_spinner_text_uses_i18n_text_field_with_expression_tree_and_keyify
- seo_og_textrow_seven_fields_use_data_chip_value_input_inheritance_wrapper_kept
- seo_og_textrow_plaintext_and_binding_both_patch_no_keyify
# 설정 참조($*_settings:) raw 노출 + [✎수정] 모드 raw 노출 결함 수정
- settings_ref_core_module_plugin_render_as_link_chip_not_raw_in_all_chip_inputs
- i18n_text_field_renders_settings_ref_as_chip_with_edit_toggle_no_raw
- data_chip_value_edit_mode_uses_chip_editor_not_raw_input_with_done_button
- data_chip_value_single_binding_and_fallback_and_settings_edit_all_route_to_chip_editor
- seo_vars_editor_uses_data_chip_value_input_value_only_no_keyification
test_files:
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/PageSettingsModal.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/PageTitleBanner.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/computed-inheritance.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/ComputedForm.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/CoreDataKeyControl.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/ComponentTargetPicker.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/property-controls/IsolatedScopeControl.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/spec/isolatedScopeUtils.test.ts
- resources/js/core/template-engine/layout-editor/__tests__/spec/coreProps.dataKey.test.ts
- resources/js/core/template-engine/layout-editor/__tests__/components/EditorToolbar.pageSettings.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/spec/candidatePools.test.ts
- resources/js/core/template-engine/layout-editor/__tests__/layouts/page-settings-roundtrip.test.tsx
- tests/Unit/Services/LayoutServiceMergeComputedTest.php
- tests/Feature/Api/Admin/PageSettingsEditorEndpointsTest.php
- tests/Feature/Api/Admin/LayoutControllerTopLevelPatchTest.php
# SEO 값 칸 표현식/칩 확대 (→ 값 전용 필드)
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/DataChipValueInput.test.tsx
# 자동 계산/로딩 화면 데이터칩 승격
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/CustomComputedBuilder.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/TransitionOverlayForm.test.tsx
# ($*_settings:) 칩 + [✎수정] 칩 편집기 + SeoVars 값전용 교체
- resources/js/core/template-engine/layout-editor/__tests__/spec/inlineBindingUtils.test.ts
- resources/js/core/template-engine/layout-editor/__tests__/components/prop-i18n-text-field.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/SeoVarsEditor.test.tsx
# 묶음③ 모듈 자동값 → 연결 칩(역산출)
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/SeoOgForm.test.tsx
- resources/js/core/template-engine/layout-editor/__tests__/components/page-settings/SeoStructuredDataEditor.test.tsx
- tests/Feature/Seo/SeoOgPreviewServiceModuleMetaTest.php
- modules/_bundled/sirsoft-ecommerce/tests/Unit/EcommerceModuleSeoTest.php
- modules/_bundled/sirsoft-board/tests/Unit/BoardModuleSeoTest.php
notes: |
S10-1 세션 D 통합 — 8탭 셸(PageSettingsModal) + 제목 배너(PageTitleBanner) + 진입점
(EditorToolbar ⚙페이지 설정, ⚙데이터 흡수) + computed route-override 승격배지/되돌리기 +
캔버스 dataKey(core-datakey 위젯)/격리 영역(IsolatedScopeControl)/타겟 picker pick 모드 +
SEO extensions 결선. 셸/배너/picker 는 prop 주도 RTL(Provider hook mock)로 라우팅·핸드셰이크
격리 검증. 캔버스 pick 핸드셰이크는 ComponentTargetPicker RTL 이 이벤트 계약(request/picked/
cancelled/requestId 짝)을 잠그고, EditorCanvasOverlay 수신측은 합성 클릭 의존이라 Chrome MCP
매트릭스(별도 세션)로 라이브 실측. 백엔드 __computedSource 'route-override' 3값 구분은
LayoutServiceMergeComputedTest, extensions 키는 PageSettingsEditorEndpointsTest 가 SSoT.
S10-1 잔여 #1 — 표현식 분해 트리를 SEO 값 전용 칸으로 확대(④#5). 값 칸
(og.image/tw.image/og.image_alt)은 DataChipValueInput(키화 0, 데이터연결·설정참조는 칩 시각화,
단일 조건식→ConditionalValueEditor, 다조각→SegmentedValueEditor, leaf 는 DataChipValueInput 재귀
주입으로 키화 없이 분해). 텍스트 칸(검색 제목/설명·og/tw 제목·설명)은 I18nTextField 유지(평문→$t:custom
키화 회귀 0, fx 토글로 표현식 트리 진입). 라이브 회귀 1건 발견·수정: 빈 값 칸에 {{ 타이핑 순간
칩 모드 전환→입력칸 unmount→나머지 유실 → editing(focus) 가드 추가(포커스 중 평문 유지, blur 시만 전환),
회귀 테스트 ⑦. Chrome MCP 매트릭스 L0~L7 라이브 실측 + PUT 페이로드 meta.seo.og.image=
"{{users.data.thumbnail}}" 영속·DB content 이중인코딩 0·새로고침 칩 복원·custom_translations 0건 tinker 확인.