fix(vendor-bundle): 코어 빌드 무한 행 해결 + 이커머스 알림 라벨 raw 노출 수정
이커머스 알림 정의 화면에서 order_pending_deposit·order_delivered 두 알림이 라벨 대신 raw 다국어 키로 표시되던 문제를 수정. settings.json 3로케일에 라벨 추가 + 렌더 회귀 테스트 + 브라우저 실측. vendor-bundle 시스템 결함 5종 해결: - (A/B) manifest 해시 기준을 활성이 아닌 출력(_bundled) 디렉토리로 통일해 확장 업데이트가 composer_json_sha_mismatch 순환에 빠지던 문제 해소 - (C) 코어 빌드 시 composer autoload dump 의 async 손자 프로세스가 부모 파이프 핸들을 상속·점유해 무한 대기하던 교착을, stdout/stderr 를 파이프 아닌 파일 descriptor 로 받아 원천 차단 (파이프=행/파일=완주 A/B 실측으로 확정) - (D) 빌드 실패 시 원본 번들 유실 방지 — 임시파일 성공 후 원자적 교체 - (E) Windows .bat 실행 시 bypass_shell 통일 코어 7.0.4 / 이커머스·언어팩 1.0.3 bump.
This commit is contained in:
1 parent
a34a03eb40
commit
13e65131f4
30 files changed
+620
-79
No files matched your search
@@ -4,6 +4,12 @@
|
||||
형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)를 따르며,
|
||||
[Semantic Versioning](https://semver.org/lang/ko/)을 준수합니다.
|
||||
|
||||
## [1.0.3] - 2026-07-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- 쇼핑몰 환경설정 > 알림 설정에서 "무통장 입금 안내"와 "배송 완료" 두 알림의 이름이 사람이 읽을 수 없는 내부 문자열로 표시되던 문제를 수정했습니다. 이제 다른 알림과 동일하게 이름이 표시되며, 알림을 기본값으로 되돌릴 때 뜨는 확인창과 알림 편집 화면에서도 함께 바로잡혔습니다.
|
||||
|
||||
## [1.0.2] - 2026-07-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "modules/sirsoft-ecommerce",
|
||||
"description": "Ecommerce module for Gnuboard7",
|
||||
"type": "library",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"ko": "이커머스",
|
||||
"en": "Ecommerce"
|
||||
},
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"license": "MIT",
|
||||
"description": {
|
||||
"ko": "그누보드7 이커머스 모듈 - 상품, 주문, 결제 관리",
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@g7/sirsoft-ecommerce",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@g7/sirsoft-ecommerce",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"devDependencies": {
|
||||
"jsdom": "^27.4.0",
|
||||
"typescript": "^5.3.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@g7/sirsoft-ecommerce",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"description": "그누보드7 이커머스 모듈 프론트엔드 에셋",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// e2e:allow 라벨 데이터(settings.json) + Vitest 렌더 회귀 테스트만 변경 — 레이아웃 동작/구조 무변경. 알림 정의 화면의 실제 렌더 경로($t: 동적 키 해석)는 본 render 테스트가 커버하고, 화면 수준 검증은 별도 브라우저 실측(handoff §10)으로 확인.
|
||||
/**
|
||||
* @file notificationDefinitionTypeLabels.test.tsx
|
||||
* @description 이커머스 알림 정의 화면 — 타입 라벨 다국어 키 raw 노출 회귀 가드.
|
||||
*
|
||||
* 배경: `order_pending_deposit`, `order_delivered` 두 알림 타입이 module.php 에는
|
||||
* 정의됐으나 라벨을 읽는 settings.json 의 `notification_definitions.types` 에 이름이
|
||||
* 등록되지 않아, 알림 정의 화면에서
|
||||
* `sirsoft-ecommerce.admin.settings.notification_definitions.types.order_pending_deposit`
|
||||
* 같은 raw 키가 그대로 노출됐다.
|
||||
*
|
||||
* 본 테스트는 _tab_notification_definitions.json 이 사용하는 실제 바인딩
|
||||
* `$t:sirsoft-ecommerce.admin.settings.notification_definitions.types.{{def.type}}` 을
|
||||
* 재현하고, **실제 settings.json** 라벨을 주입해 두 타입이 raw 키가 아닌 사람이 읽는
|
||||
* 라벨로 렌더되는지 검증한다. settings.json 에서 어느 한 키가 제거되면 이 테스트가
|
||||
* 다시 red 로 떨어져 회귀를 차단한다.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { describe, it, expect, afterEach, vi } from 'vitest';
|
||||
import {
|
||||
createLayoutTest,
|
||||
createMockComponentRegistryWithBasics,
|
||||
screen,
|
||||
type MockComponentRegistry,
|
||||
} from '@core/template-engine/__tests__/utils/layoutTestUtils';
|
||||
|
||||
import settingsKo from '../../../lang/partial/ko/admin/settings.json';
|
||||
import settingsEn from '../../../lang/partial/en/admin/settings.json';
|
||||
|
||||
// 실제 화면(_tab_notification_definitions.json line ~371)의 타입 라벨 바인딩을 그대로 축약 재현.
|
||||
// 알림 목록 항목마다 H4 에 `$t:...types.{{def.type}}` 로 이름을 표시한다.
|
||||
const LABEL_BINDING =
|
||||
'$t:sirsoft-ecommerce.admin.settings.notification_definitions.types.{{def.type}}';
|
||||
|
||||
function makeLayout() {
|
||||
return {
|
||||
name: 'notification_definition_type_labels_probe',
|
||||
version: '1.0.0',
|
||||
components: [
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'Div',
|
||||
children: [
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'Div',
|
||||
iteration: {
|
||||
source: '{{ecommerceNotificationDefinitions?.data?.data ?? []}}',
|
||||
item_var: 'def',
|
||||
index_var: 'defIdx',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'H4',
|
||||
text: LABEL_BINDING,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// 실제 서빙 데이터를 모사한 알림 정의 목록 — 문제의 두 타입 포함.
|
||||
const DEFINITIONS = [
|
||||
{ id: 1, type: 'order_pending_deposit', templates: [], variables: [] },
|
||||
{ id: 2, type: 'order_delivered', templates: [], variables: [] },
|
||||
];
|
||||
|
||||
// settings.json 은 `notification_definitions` 를 admin.settings 하위가 아닌 파일 루트에 둔다.
|
||||
// 화면 바인딩은 `sirsoft-ecommerce.admin.settings.notification_definitions...` 네임스페이스를
|
||||
// 쓰므로, 주입 시 admin.settings 아래로 매핑한다(활성 언어팩 로더의 병합 결과와 동일 구조).
|
||||
function buildTranslations(settings: Record<string, any>) {
|
||||
return {
|
||||
'sirsoft-ecommerce': {
|
||||
admin: {
|
||||
settings: settings,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let registry: MockComponentRegistry;
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('알림 정의 타입 라벨 — 다국어 키 raw 노출 회귀 가드', () => {
|
||||
it('ko: order_pending_deposit / order_delivered 가 라벨로 렌더된다 (raw 키 미노출)', async () => {
|
||||
registry = createMockComponentRegistryWithBasics();
|
||||
const { render, cleanup } = createLayoutTest(makeLayout(), {
|
||||
componentRegistry: registry as any,
|
||||
locale: 'ko',
|
||||
translations: buildTranslations(settingsKo),
|
||||
initialData: {
|
||||
ecommerceNotificationDefinitions: { data: { data: DEFINITIONS } },
|
||||
},
|
||||
});
|
||||
|
||||
await render();
|
||||
|
||||
expect(screen.getByText('무통장 입금 안내')).toBeInTheDocument();
|
||||
expect(screen.getByText('배송 완료')).toBeInTheDocument();
|
||||
|
||||
// raw 키가 화면에 남아있으면 회귀
|
||||
expect(
|
||||
screen.queryByText(/notification_definitions\.types\.order_pending_deposit/),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(/notification_definitions\.types\.order_delivered/),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('en: order_pending_deposit / order_delivered 가 라벨로 렌더된다 (raw 키 미노출)', async () => {
|
||||
registry = createMockComponentRegistryWithBasics();
|
||||
const { render, cleanup } = createLayoutTest(makeLayout(), {
|
||||
componentRegistry: registry as any,
|
||||
locale: 'en',
|
||||
translations: buildTranslations(settingsEn),
|
||||
initialData: {
|
||||
ecommerceNotificationDefinitions: { data: { data: DEFINITIONS } },
|
||||
},
|
||||
});
|
||||
|
||||
await render();
|
||||
|
||||
expect(screen.getByText('Bank Transfer Payment Guide')).toBeInTheDocument();
|
||||
expect(screen.getByText('Order Delivered')).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.queryByText(/notification_definitions\.types\.order_pending_deposit/),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(/notification_definitions\.types\.order_delivered/),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('라벨 제거 시 raw 키가 노출됨 — 회귀 가드가 실제로 결함을 감지한다', async () => {
|
||||
// 이 테스트는 위 두 가드가 "우연히 통과"하는 것이 아님을 증명한다:
|
||||
// settings.json 에서 두 타입 라벨을 제거하면 화면에 raw 키가 그대로 노출된다.
|
||||
registry = createMockComponentRegistryWithBasics();
|
||||
const stripped = JSON.parse(JSON.stringify(settingsKo));
|
||||
delete stripped.notification_definitions.types.order_pending_deposit;
|
||||
delete stripped.notification_definitions.types.order_delivered;
|
||||
|
||||
const { render, cleanup } = createLayoutTest(makeLayout(), {
|
||||
componentRegistry: registry as any,
|
||||
locale: 'ko',
|
||||
translations: buildTranslations(stripped),
|
||||
initialData: {
|
||||
ecommerceNotificationDefinitions: { data: { data: DEFINITIONS } },
|
||||
},
|
||||
});
|
||||
|
||||
await render();
|
||||
|
||||
expect(screen.queryByText('무통장 입금 안내')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/notification_definitions\.types\.order_pending_deposit/),
|
||||
).toBeInTheDocument();
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('settings.json(ko/en) 의 types 에 두 신규 타입 라벨이 존재한다', () => {
|
||||
const koTypes = (settingsKo as any).notification_definitions.types;
|
||||
const enTypes = (settingsEn as any).notification_definitions.types;
|
||||
|
||||
expect(koTypes.order_pending_deposit).toBe('무통장 입금 안내');
|
||||
expect(koTypes.order_delivered).toBe('배송 완료');
|
||||
expect(enTypes.order_pending_deposit).toBe('Bank Transfer Payment Guide');
|
||||
expect(enTypes.order_delivered).toBe('Order Delivered');
|
||||
});
|
||||
});
|
||||
@@ -447,7 +447,9 @@
|
||||
"save_error": "Failed to save notification settings.",
|
||||
"types": {
|
||||
"order_confirmed": "Order Confirmed",
|
||||
"order_pending_deposit": "Bank Transfer Payment Guide",
|
||||
"order_shipped": "Order Shipped",
|
||||
"order_delivered": "Order Delivered",
|
||||
"order_completed": "Purchase Completed",
|
||||
"order_cancelled": "Order Cancelled",
|
||||
"new_order_admin": "New Order (Admin)",
|
||||
|
||||
@@ -447,7 +447,9 @@
|
||||
"save_error": "알림 설정 저장에 실패했습니다.",
|
||||
"types": {
|
||||
"order_confirmed": "주문 확인",
|
||||
"order_pending_deposit": "무통장 입금 안내",
|
||||
"order_shipped": "배송 시작",
|
||||
"order_delivered": "배송 완료",
|
||||
"order_completed": "구매 확정",
|
||||
"order_cancelled": "주문 취소",
|
||||
"new_order_admin": "신규 주문 (관리자)",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"generated_at": "2026-07-10T12:05:27+00:00",
|
||||
"generated_at": "2026-07-10T20:22:38+00:00",
|
||||
"generator": "g7 vendor-bundle:build",
|
||||
"target": "module:sirsoft-ecommerce",
|
||||
"composer_json_sha256": "aa8f74063b7f9cd969cb4effb120e3c50bb944a4d3b2e16fae7c31bd8232fce8",
|
||||
"composer_json_sha256": "5195d789bc9edd0b5140c42aeedd7c87be67c7252b477a879ff90607b4787935",
|
||||
"composer_lock_sha256": "876ca9c2273a33baff878d25050a567018a946412db053930a7f548c4add595d",
|
||||
"zip_sha256": "ef77515e4d920a52a8adba1e6d7e310389c6f3f459586d3cd5c24749f24d7fb0",
|
||||
"zip_sha256": "c1767e8eca61163667e9289a3963454bab92806ce4e8a3651777f1e1aea14081",
|
||||
"zip_size": 435549,
|
||||
"package_count": 1,
|
||||
"php_requirement": "^8.2",
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user