Files
WonderQ-Project/WonderQ-MiniAPP/tests/data.test.ts
2026-08-11 19:19:11 +08:00

382 lines
12 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { fallbackData, formatCampaignPrice, formatHotelPrice, formatProductCardPrice, getSearchProducts, normalizeApiProduct, normalizeDemandRecommendation, normalizeRouteSections, normalizeSiteConfig, productKey, stripTitle } from "@/lib/data";
describe("WonderQ shared data helpers", () => {
it("keeps fallback products searchable by Guizhou aliases", () => {
const results = getSearchProducts(fallbackData.products, "荔波小七孔");
expect(results.length).toBeGreaterThanOrEqual(2);
expect(results.some((product) => product.title.includes("荔波") || product.tags.includes("荔波"))).toBe(true);
});
it("normalizes API products to the miniapp product shape", () => {
const product = normalizeApiProduct(
{
id: "api-product-42",
sourceId: null,
title: "测试贵州线路",
subtitle: "后台短标题",
priceAmount: 128000,
tags: ["小包团"],
coverImage: null,
destination: { id: "dest-1", name: "贵阳" },
},
3,
);
expect(product.id).toBe(42);
expect(product.price).toBe("128000");
expect(product.image).toBe("/assets/guizhou/libo-xiaoqikong.jpg");
expect(product.subtitle).toBe("后台短标题");
expect(product.destinationName).toBe("贵阳");
expect(productKey(product)).toBe("api-product-42");
});
it("formats product card price without wan conversion", () => {
expect(formatProductCardPrice("57500")).toBe("¥57500起/人");
});
it("normalizes public site map image to app data", () => {
const siteConfig = normalizeSiteConfig(
{
map: [{ id: "map-1", image: "https://cdn.example.test/guizhou-map.webp" }],
},
fallbackData,
);
expect(siteConfig.mapImage).toBe("https://cdn.example.test/guizhou-map.webp");
});
it("normalizes destination page hero and region modules", () => {
const siteConfig = normalizeSiteConfig(
{
destinationHero: [
{
id: "destination-hero-1",
title: "后台主视觉标题",
kicker: "后台小标题",
image: "https://cdn.example.test/destination-hero.jpg",
isActive: true,
sortOrder: 0,
},
],
destinationRegions: [
{
id: "region-b",
label: "黔南",
keyword: "黔南荔波",
spots: "荔波小七孔、茂兰",
isActive: true,
sortOrder: 2,
},
{
id: "region-a",
label: "贵阳/安顺",
keyword: "",
spots: "甲秀楼、黄果树",
isActive: true,
sortOrder: 1,
},
],
},
fallbackData,
);
expect(siteConfig.destinationHero).toMatchObject({
id: "destination-hero-1",
title: "后台主视觉标题",
kicker: "后台小标题",
image: "https://cdn.example.test/destination-hero.jpg",
});
expect(siteConfig.destinationRegions.map((item) => item.label)).toEqual(["贵阳/安顺", "黔南"]);
expect(siteConfig.destinationRegions[0].keyword).toBe("贵阳/安顺");
expect(siteConfig.destinationRegions[1].keyword).toBe("黔南荔波");
});
it("normalizes public hotel and vehicle modules", () => {
const siteConfig = normalizeSiteConfig(
{
hotelGroups: [
{
id: "hotel-b",
title: "野奢酒店升级",
description: "",
image: "",
isActive: true,
sortOrder: 2,
},
{
id: "hotel-a",
title: "经典酒店精选",
description: "接口酒店描述",
image: "https://cdn.example.test/hotel.jpg",
coverImage: "https://cdn.example.test/hotel-cover.jpg",
priceAmount: 68000,
priceUnit: "起/晚",
tags: ["温泉", "亲子"],
status: "published",
isActive: true,
sortOrder: 1,
},
],
vehicleOptions: [
{
id: "vehicle-1",
title: "7座舒适商务车",
description: "接口用车描述",
image: "https://cdn.example.test/vehicle.jpg",
isActive: true,
sortOrder: 0,
},
],
},
fallbackData,
);
expect(siteConfig.hotelGroups.map((item) => item.title)).toEqual(["经典酒店精选", "野奢酒店升级"]);
expect(siteConfig.hotelGroups[0]).toMatchObject({
id: "hotel-a",
description: "接口酒店描述",
image: "https://cdn.example.test/hotel-cover.jpg",
coverImage: "https://cdn.example.test/hotel-cover.jpg",
priceAmount: 68000,
priceUnit: "起/晚",
tags: ["温泉", "亲子"],
});
expect(siteConfig.hotelGroups[0].coverImage).toBe("https://cdn.example.test/hotel-cover.jpg");
expect(formatHotelPrice(siteConfig.hotelGroups[0].priceAmount, siteConfig.hotelGroups[0].priceUnit)).toBe("¥68000起/晚");
expect(formatHotelPrice(null, null)).toBe("¥107500起/晚");
expect(siteConfig.hotelGroups[1].image).toBe(fallbackData.hotelGroups[1].image);
expect(siteConfig.vehicleOptions[0]).toMatchObject({
id: "vehicle-1",
title: "7座舒适商务车",
description: "接口用车描述",
image: "https://cdn.example.test/vehicle.jpg",
});
});
it("normalizes demand page modules and recommendation product order", () => {
const siteConfig = normalizeSiteConfig(
{
demandHero: [
{
id: "demand-hero-1",
kicker: "后台 3 步",
title: "后台需求标题",
description: "后台需求说明",
steps: ["提交", "沟通", "确认"],
isActive: true,
sortOrder: 0,
},
],
demandFeatureCards: [
{
id: "feature-b",
title: "住宿",
description: "后台住宿说明",
isActive: true,
sortOrder: 2,
},
{
id: "feature-a",
title: "动线",
description: "后台动线说明",
isActive: true,
sortOrder: 1,
},
],
demandForm: [
{
id: "form-1",
destinationLabel: "想去哪里",
destinationPlaceholder: "填写目的地",
phoneLabel: "联系微信",
phonePlaceholder: "微信号 / 手机号",
noteLabel: "补充偏好",
notePlaceholder: "人数、日期、酒店",
submitLabel: "提交给管家",
chips: ["荔波", "西江"],
isActive: true,
},
],
},
fallbackData,
);
const products = [
normalizeApiProduct({ id: "product-a", sourceId: 1, title: "线路 A", tags: [] }, 0),
normalizeApiProduct({ id: "product-b", sourceId: 2, title: "线路 B", tags: [] }, 1),
];
const recommendation = normalizeDemandRecommendation(
[
{
id: "recommendation-1",
title: "后台热门",
subtitle: "后台副文案",
productIds: ["product-b", "product-a"],
isActive: true,
sortOrder: 0,
},
],
products,
fallbackData.demandRecommendation,
);
expect(siteConfig.demandHero.title).toBe("后台需求标题");
expect(siteConfig.demandHero.steps).toEqual(["提交", "沟通", "确认"]);
expect(siteConfig.demandFeatureCards.map((item) => item.title)).toEqual(["动线", "住宿"]);
expect(siteConfig.demandForm).toMatchObject({
destinationLabel: "想去哪里",
submitLabel: "提交给管家",
chips: ["荔波", "西江"],
});
expect(recommendation.title).toBe("后台热门");
expect(recommendation.products.map((product) => product.title)).toEqual(["线路 B", "线路 A"]);
});
it("falls back to local demand page data when config is missing or productIds do not match", () => {
const siteConfig = normalizeSiteConfig({}, fallbackData);
const recommendation = normalizeDemandRecommendation(
[
{
id: "recommendation-empty",
title: "无匹配推荐",
productIds: ["missing-product"],
isActive: true,
},
],
fallbackData.products,
fallbackData.demandRecommendation,
);
expect(siteConfig.demandHero).toBe(fallbackData.demandHero);
expect(siteConfig.demandFeatureCards).toBe(fallbackData.demandFeatureCards);
expect(siteConfig.demandForm).toBe(fallbackData.demandForm);
expect(recommendation).toBe(fallbackData.demandRecommendation);
});
it("normalizes public campaigns to home deal cards", () => {
const siteConfig = normalizeSiteConfig(
{
campaigns: [
{
id: "campaign-1",
slug: "classic",
title: "经典打卡特惠",
description: "接口活动描述",
coverImage: "https://cdn.example.test/campaign.jpg",
priceAmount: 57500,
priceUnit: "起/人",
},
],
},
fallbackData,
);
expect(siteConfig.campaigns).toHaveLength(1);
expect(siteConfig.campaigns[0]).toMatchObject({
id: "campaign-1",
title: "经典打卡特惠",
campaignType: "经典打卡特惠",
image: "https://cdn.example.test/campaign.jpg",
description: "接口活动描述",
priceAmount: 57500,
priceUnit: "起/人",
tags: ["经典首游", "小包团优惠"],
});
expect(formatCampaignPrice(siteConfig.campaigns[0].priceAmount, siteConfig.campaigns[0].priceUnit, siteConfig.campaigns[0].priceText)).toBe("¥57500起/人");
});
it("normalizes public route sections by productIds order", () => {
const products = [
normalizeApiProduct(
{
id: "product-a",
sourceId: 1,
title: "线路 A",
tags: [],
},
0,
),
normalizeApiProduct(
{
id: "product-b",
sourceId: 2,
title: "线路 B",
tags: [],
},
1,
),
];
const sections = normalizeRouteSections(
[
{
id: "route-section-001",
title: "后台精选线路",
subtitle: "后台副文案",
productIds: ["product-b", "product-a"],
isActive: true,
},
],
products,
fallbackData.routeSections,
);
expect(sections).toHaveLength(1);
expect(sections[0].title).toBe("后台精选线路");
expect(sections[0].products.map((product) => product.title)).toEqual(["线路 B", "线路 A"]);
});
it("skips inactive route sections and sections without matched products", () => {
const products = [
normalizeApiProduct(
{
id: "product-visible",
sourceId: 1,
title: "线路 A",
tags: [],
},
0,
),
];
const sections = normalizeRouteSections(
[
{
id: "route-section-inactive",
title: "停用分组",
productIds: ["product-visible"],
isActive: false,
},
{
id: "route-section-empty",
title: "无匹配商品分组",
productIds: ["missing-product"],
isActive: true,
},
{
id: "route-section-active",
title: "有效分组",
productIds: ["product-visible"],
isActive: true,
},
],
products,
fallbackData.routeSections,
);
expect(sections).toHaveLength(1);
expect(sections[0].id).toBe("route-section-active");
expect(sections[0].products.map((product) => product.title)).toEqual(["线路 A"]);
});
it("falls back to local route sections when public route sections are empty", () => {
expect(normalizeRouteSections([], fallbackData.products, fallbackData.routeSections)).toBe(fallbackData.routeSections);
});
it("strips source brackets from product display titles", () => {
expect(stripTitle("【贵州】黄果树·荔波小七孔5日")).toBe("贵州黄果树");
});
});