test(e2e): add paid media flow coverage

This commit is contained in:
2026-06-30 17:59:07 +08:00
parent e75865865f
commit 6aac9f326f
5 changed files with 514 additions and 7 deletions
+65 -7
View File
@@ -5,11 +5,14 @@ import {
chatSendResponse,
createPaymentOrderResponse,
createChatSendResponse,
createPaidImageHistoryResponse,
createWeeklyLimitChatSendResponse,
e2eEmailUser,
emptyChatHistoryResponse,
emailLoginResponse,
guestLoginResponse,
paidImageChatSendResponse,
paidImageUnlockHistoryResponse,
paidPaymentOrderStatusResponse,
paymentPlansResponse,
userEntitlementsResponse,
@@ -18,20 +21,27 @@ import {
interface MockCoreApisOptions {
chatLimitTriggerAt?: number;
paidImageFlow?: boolean;
}
export async function mockCoreApis(
page: Page,
options: MockCoreApisOptions = {},
): Promise<void> {
const { chatLimitTriggerAt } = options;
const { chatLimitTriggerAt, paidImageFlow = false } = options;
let sendCount = 0;
let paidImageRequested = false;
let paidImageUnlocked = false;
await page.route("**/api/auth/guest", async (route) => {
await route.fulfill({ json: apiEnvelope(guestLoginResponse) });
});
await page.route("**/api/auth/login", async (route) => {
if (paidImageFlow) {
paidImageRequested = true;
}
await route.fulfill({ json: apiEnvelope(emailLoginResponse) });
});
@@ -48,18 +58,62 @@ export async function mockCoreApis(
});
await page.route("**/api/chat/history**", async (route) => {
await route.fulfill({ json: apiEnvelope(emptyChatHistoryResponse) });
const response =
paidImageFlow && paidImageRequested
? createPaidImageHistoryResponse(paidImageUnlocked)
: emptyChatHistoryResponse;
await route.fulfill({ json: apiEnvelope(response) });
});
await page.route("**/api/chat/send", async (route) => {
sendCount += 1;
const requestBody = route.request().postDataJSON() as
| { message?: unknown }
| undefined;
const message =
typeof requestBody?.message === "string" ? requestBody.message : "";
const response =
chatLimitTriggerAt != null && sendCount >= chatLimitTriggerAt
? createWeeklyLimitChatSendResponse(sendCount, chatLimitTriggerAt)
: chatLimitTriggerAt != null
? createChatSendResponse(sendCount)
: chatSendResponse;
paidImageFlow && message.includes("给我发图片")
? paidImageChatSendResponse
: chatLimitTriggerAt != null && sendCount >= chatLimitTriggerAt
? createWeeklyLimitChatSendResponse(sendCount, chatLimitTriggerAt)
: chatLimitTriggerAt != null
? createChatSendResponse(sendCount)
: chatSendResponse;
if (paidImageFlow && message.includes("给我发图片")) {
paidImageRequested = true;
paidImageUnlocked = false;
}
await route.fulfill({ json: apiEnvelope(response) });
});
await page.route("**/api/chat/unlock-history", async (route) => {
if (paidImageFlow && paidImageRequested) {
paidImageUnlocked = true;
}
const response =
paidImageFlow && paidImageRequested
? paidImageUnlockHistoryResponse
: {
unlocked: true,
reason: "no_locked_messages",
totalLocked: 0,
unlockedCount: 0,
privateCount: 0,
imageCount: 0,
voiceCount: 0,
requiredCredits: 0,
currentCredits: 0,
remainingCredits: 0,
shortfallCredits: 0,
costsByMessage: {},
messageIds: [],
};
await route.fulfill({ json: apiEnvelope(response) });
});
@@ -73,6 +127,10 @@ export async function mockCoreApis(
});
await page.route("**/api/payment/order-status**", async (route) => {
if (paidImageFlow && paidImageRequested) {
paidImageUnlocked = true;
}
await route.fulfill({ json: apiEnvelope(paidPaymentOrderStatusResponse) });
});
+80
View File
@@ -127,6 +127,86 @@ export function createWeeklyLimitChatSendResponse(
};
}
export const paidImageMessageId = "msg_photo_paywall_001";
export const paidImageUrl =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=";
export const paidImageChatSendResponse = {
reply: "I can show you that photo after you activate VIP.",
audioUrl: "",
messageId: paidImageMessageId,
isGuest: true,
timestamp: 1_782_180_725_000,
image: {
type: "elio_schedule",
url: paidImageUrl,
},
lockDetail: {
locked: true,
showContent: true,
showUpgrade: true,
reason: "image",
hint: "Activate VIP to unlock the full photo.",
detail: null,
},
};
export function createPaidImageHistoryResponse(unlocked: boolean) {
return {
messages: [
{
role: "assistant",
type: "image",
content: unlocked
? ""
: "I can show you that photo after you activate VIP.",
id: paidImageMessageId,
created_at: "2026-06-30T00:00:00.000Z",
audioUrl: null,
image: {
type: "elio_schedule",
url: paidImageUrl,
},
lockDetail: unlocked
? {
locked: false,
showContent: true,
showUpgrade: false,
reason: null,
hint: null,
detail: null,
}
: paidImageChatSendResponse.lockDetail,
},
],
total: 1,
limit: 50,
offset: 0,
isVip: unlocked,
privateFreeLimit: 0,
privateUsedToday: 0,
privateCanViewFree: false,
};
}
export const paidImageUnlockHistoryResponse = {
unlocked: true,
reason: "ok",
totalLocked: 1,
unlockedCount: 1,
privateCount: 0,
imageCount: 1,
voiceCount: 0,
requiredCredits: 0,
currentCredits: 0,
remainingCredits: 0,
shortfallCredits: 0,
costsByMessage: {
[paidImageMessageId]: 0,
},
messageIds: [paidImageMessageId],
};
export const paymentPlansResponse = {
plans: [
{