fix(message_bizppurio): 알림톡 연결 모달에서 조회 실패와 '승인 템플릿 0건'을 구분
자격증명 오류 등으로 카카오 템플릿 조회가 실패하면 드롭다운이 비면서 '승인된 템플릿이 없습니다'로 떠서 실제 0건과 구분되지 않던 문제를 수정한다. 데이터소스 fallback 에 load_failed 마커를 실어, 드롭다운이 비었을 때 조회 실패(설정 확인 안내)와 진짜 0건(승인 템플릿 없음)을 문구로 분기한다. 에러 토스트 재도입 없이 모달 내 인라인 문구로만 구분한다.
This commit is contained in:
1 parent
8aa395c3a1
commit
ca5c9a3438
4 files changed
+52
-3
No files matched your search
+15
-3
@@ -18,7 +18,7 @@
|
||||
},
|
||||
{
|
||||
"id": "bizppurioApprovedTemplates",
|
||||
"comment": "연결 모달 드롭다운 옵션(카카오 관리 API 위임). auto_fetch:false — 자격증명 미설정 시 카카오 API 422 가 전 탭에서 발화하던 문제 차단(결정 C). 연결 모달 열 때만 조회. errorHandling.default:suppress — 자격증명 미설정 시 422(카카오 API 요청 실패)를 조용히 무시한다(fallback 빈 목록 사용, 안내는 배너·'승인 템플릿 없음' 문구가 담당). 이 토스트를 막지 않으면 모달 열 때마다 에러 토스트가 뜬다.",
|
||||
"comment": "연결 모달 드롭다운 옵션(카카오 관리 API 위임). auto_fetch:false — 자격증명 미설정 시 카카오 API 422 가 전 탭에서 발화하던 문제 차단(결정 C). 연결 모달 열 때만 조회. errorHandling.default:suppress — 자격증명 미설정 시 422(카카오 API 요청 실패)를 조용히 무시한다(fallback 사용, 토스트 재도입 금지 — 모달 열 때마다 에러 토스트가 뜨던 회귀). 조회 실패와 '진짜 0건'을 화면에서 구분하기 위해 fallback.data.load_failed:true 마커를 싣는다 — 정상 응답에는 이 필드가 없으므로, 드롭다운이 비었을 때 load_failed 유무로 '불러오지 못함(자격증명 확인)' vs '승인 템플릿 없음'을 분기한다.",
|
||||
"type": "api",
|
||||
"endpoint": "/api/plugins/sirsoft-message_bizppurio/admin/notification-bindings/approved-templates",
|
||||
"method": "GET",
|
||||
@@ -31,7 +31,8 @@
|
||||
},
|
||||
"fallback": {
|
||||
"data": {
|
||||
"templates": []
|
||||
"templates": [],
|
||||
"load_failed": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,9 +250,20 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"comment": "드롭다운이 비었고 조회 실패(fallback 마커)일 때 — 키 오류 등을 '0건'과 구분해 설정 확인을 안내.",
|
||||
"type": "basic",
|
||||
"name": "P",
|
||||
"if": "{{(bizppurioApprovedTemplates?.data?.templates ?? []).length === 0}}",
|
||||
"if": "{{(bizppurioApprovedTemplates?.data?.templates ?? []).length === 0 && bizppurioApprovedTemplates?.data?.load_failed === true}}",
|
||||
"props": {
|
||||
"className": "text-xs text-red-600 dark:text-red-400 mt-1"
|
||||
},
|
||||
"text": "$t:sirsoft-message_bizppurio.binding.templates_load_failed"
|
||||
},
|
||||
{
|
||||
"comment": "드롭다운이 비었고 조회는 정상(load_failed 없음)일 때 — 실제 승인 템플릿 0건.",
|
||||
"type": "basic",
|
||||
"name": "P",
|
||||
"if": "{{(bizppurioApprovedTemplates?.data?.templates ?? []).length === 0 && !(bizppurioApprovedTemplates?.data?.load_failed)}}",
|
||||
"props": {
|
||||
"className": "text-xs text-amber-600 dark:text-amber-400 mt-1"
|
||||
},
|
||||
|
||||
+35
@@ -156,6 +156,41 @@ describe('binding UI — 연결 모달(우리 소유, 코어 편집 모달과
|
||||
});
|
||||
});
|
||||
|
||||
describe('binding UI — 드롭다운 조회 실패 vs 0건 구분(결함 3)', () => {
|
||||
const modal = findById(overlayRoot, 'modal_bizppurio_binding');
|
||||
|
||||
it('승인 템플릿 데이터소스 fallback 에 load_failed:true 마커가 있다', () => {
|
||||
const ds = ((overlay as { data_sources?: Array<Record<string, unknown>> }).data_sources ?? [])
|
||||
.find((d) => d.id === 'bizppurioApprovedTemplates');
|
||||
const fallback = (ds?.fallback as { data?: Record<string, unknown> })?.data ?? {};
|
||||
// 조회 실패 시 이 마커가 상태에 실려 '0건'과 구분된다. 정상 응답에는 이 필드가 없다.
|
||||
expect(fallback.load_failed).toBe(true);
|
||||
expect(Array.isArray(fallback.templates)).toBe(true);
|
||||
expect((fallback.templates as unknown[]).length).toBe(0);
|
||||
});
|
||||
|
||||
it('드롭다운이 비었을 때 조회 실패(load_failed)면 설정 확인 문구를 노출한다', () => {
|
||||
const raw = JSON.stringify(modal);
|
||||
// 조회 실패 분기: length===0 && load_failed===true → templates_load_failed
|
||||
expect(raw).toContain('binding.templates_load_failed');
|
||||
expect(raw).toContain('load_failed === true');
|
||||
});
|
||||
|
||||
it('드롭다운이 비었을 때 조회 정상(0건)이면 승인 템플릿 없음 문구를 노출한다', () => {
|
||||
const raw = JSON.stringify(modal);
|
||||
// 0건 분기: length===0 && !load_failed → no_approved_templates
|
||||
expect(raw).toContain('binding.no_approved_templates');
|
||||
expect(raw).toContain('!(bizppurioApprovedTemplates?.data?.load_failed)');
|
||||
});
|
||||
|
||||
it('두 문구는 상호배타 조건이라 동시에 뜨지 않는다(조회실패=빨강 / 0건=amber)', () => {
|
||||
const raw = JSON.stringify(modal);
|
||||
// 조회 실패 문구는 red, 0건 문구는 amber 로 시각 구분
|
||||
expect(raw).toContain('text-red-600');
|
||||
expect(raw).toContain('text-amber-600');
|
||||
});
|
||||
});
|
||||
|
||||
describe('binding UI — 코어 무오염 + i18n', () => {
|
||||
it('overlay·footer 어디에도 코어 편집 모달 저장 body(notification-templates PUT)를 건드리지 않는다', () => {
|
||||
const all = JSON.stringify(overlay) + JSON.stringify(footer);
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
"connected_template": "Connected Template",
|
||||
"none": "Not connected",
|
||||
"no_approved_templates": "No approved (sendable) alimtalk templates. Register and inspect a template first.",
|
||||
"templates_load_failed": "Failed to load the template list. Check the plugin settings (credentials).",
|
||||
"fallback_sms": "Fall back to SMS on failure",
|
||||
"fallback_hint": "If the alimtalk message fails, this notification's body is sent as an SMS instead.",
|
||||
"variables_hint": "Available variables (auto-substituted on send)",
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
"connected_template": "연결 템플릿",
|
||||
"none": "연결 안 함",
|
||||
"no_approved_templates": "발송 가능한(승인된) 알림톡 템플릿이 없습니다. 먼저 템플릿을 등록·검수하세요.",
|
||||
"templates_load_failed": "템플릿 목록을 불러오지 못했습니다. 플러그인 설정(자격증명)을 확인하세요.",
|
||||
"fallback_sms": "실패 시 SMS로 대체발송",
|
||||
"fallback_hint": "알림톡 발송이 실패하면 이 알림의 본문 내용이 문자(SMS)로 대신 발송됩니다.",
|
||||
"variables_hint": "제공 변수 (발송 시 자동 치환)",
|
||||
|
||||
Reference in new issue
Block a user