o2o-site-AEO/solution/site/src/sections/stay-guide.test.tsx
김성경 a093921b62 feat(backend,site): NOL 수집 및 숙박 안내 구조화와 지역 콘텐츠 연동
수집한 객실·이용 정보가 발행 화면에 연결되지 않던 경로를 보완하고, 숙소 소개와 지역 맛집 표시를 개선한다.

- NOL 브라우저 수집 어댑터와 수집·반영 스크립트 추가
- 크롤링 fact 즉시 노출 및 직접 입력·정정값 보호
- 이용안내 항목별 구조화와 기존 표 연결, 원문 UI 비표시
- 군산 한일옥 고정 등록과 지역 맛집 탐색·보강 경로 추가
- 숙소 소개 요약, 히어로 문구, 지역 콘텐츠·목업 표시 개선

검증: 작업 트리 기준 site 타입·린트·빌드 및 안내 렌더링 테스트 통과, PC·모바일 화면 확인. 스테이징 diff 공백 검사 통과. 사용자 요청에 따라 현재 스테이징된 55개 파일만 포함하며 미스테이징 문서·테스트 등은 제외.
2026-09-14 19:40:04 +09:00

72 lines
3.6 KiB
TypeScript

import {expect, it} from 'vitest';
import {renderToStaticMarkup} from 'react-dom/server';
import {sanitizePayloadForPublish, FactStatus, SourceType} from '@o2o/shared';
import {SiteProvider} from '@site/lib/site-context';
import {MOONLIGHT_STAY_PAYLOAD} from '@site/fixtures/moonlight-stay';
import {EssentialInfoSection} from './EssentialInfoSection';
function fixture(confirmed = true) {
const payload = structuredClone(MOONLIGHT_STAY_PAYLOAD);
payload.facts = [];
payload.links = [{
channel: 1, title: 'NOL', url: 'https://nol.yanolja.com/stay/domestic/10068088', confirmed,
stayGuide: {
policy: '체크인 15:00 체크아웃 11:00\n전 연령 동일 1인당 2만원',
service: '주방\n욕조',
reservation: '- 반려동물 입실금지\n<script>alert(1)</script>',
},
}];
return payload;
}
it('renders guides without facts, before booking actions, preserving line breaks and escaping HTML', () => {
const payload = fixture();
const html = renderToStaticMarkup(<SiteProvider payload={payload}><EssentialInfoSection /></SiteProvider>);
expect(html).toContain('예약 전 확인');
expect(html).toContain('체크인 15:00 체크아웃 11:00\n전 연령 동일 1인당 2만원');
expect(html).toContain('주방\n욕조');
expect(html).toContain('반려동물 입실금지');
expect(html).toContain('whitespace-pre-line');
expect(html).toContain('&lt;script&gt;');
expect(html).not.toContain('<script>');
expect(html).not.toContain('모든 항목이 사업자 확인');
expect(html.indexOf('반려동물 입실금지')).toBeLessThan(html.indexOf('예약은 아래로'));
});
it('does not render or embed guides from unconfirmed links', () => {
const payload = fixture(false);
const html = renderToStaticMarkup(<SiteProvider payload={payload}><EssentialInfoSection /></SiteProvider>);
expect(html).toBe('');
expect(JSON.stringify(sanitizePayloadForPublish(payload))).not.toContain('반려동물 입실금지');
});
it('supports older payloads without guides', () => {
const payload = fixture();
delete payload.links[0].stayGuide;
expect(renderToStaticMarkup(<SiteProvider payload={payload}><EssentialInfoSection /></SiteProvider>)).toBe('');
});
it('keeps structured rows and restrictions without the original disclosure or source link', () => {
const payload = fixture();
payload.facts = [{key: 'check_in_time', label: '체크인 시간', value: '16:00',
scope: 'place', type: 'time', status: FactStatus.CORRECTED, sourceType: SourceType.OWNER,
critical: true, required: true}];
payload.links[0].stayGuide!.fields = [
{key: 'check_in_time', label: '체크인 시간', value: '15:00', group: 'rules'},
{key: 'extra_person_fee', label: '인원 추가 요금', value: '20,000원', note: '1인당 · 전 연령 동일', group: 'rules'},
{key: 'cooking_allowed', label: '취사', value: '가능', note: '생선구이 조리금지', group: 'facilities'},
];
const html = renderToStaticMarkup(<SiteProvider payload={payload}><EssentialInfoSection /></SiteProvider>);
expect(html.match(/체크인 시간/g)).toHaveLength(1);
expect(html).toContain('16:00');
expect(html).toContain('20,000원');
expect(html).toContain('1인당 · 전 연령 동일');
expect(html).toContain('생선구이 조리금지');
expect(html).not.toContain('<details');
expect(html).not.toContain('NOL 이용안내 · 시설 원문');
expect(html).not.toContain('NOL 안내 원문');
expect(html).not.toContain('체크인 15:00 체크아웃 11:00');
expect(html).toContain('반려동물 입실금지');
expect(html).toContain('예약은 아래로');
});