refactor(e2e): organize fixtures by feature

This commit is contained in:
2026-07-16 11:49:58 +08:00
parent 764bb5a862
commit e730799aa5
28 changed files with 521 additions and 1335 deletions
+20 -199
View File
@@ -1,31 +1,11 @@
import type { Page } from "@playwright/test";
import {
apiEnvelope,
chatSendResponse,
createPaymentOrderResponse,
createChatSendResponse,
createPaidImageHistoryResponse,
createWeeklyLimitChatSendResponse,
e2eEmailUser,
emptyChatHistoryResponse,
emailLoginResponse,
guestLoginResponse,
insufficientCreditsUnlockImageResponse,
insufficientCreditsUnlockVoiceResponse,
paidImageChatSendResponse,
paidImageUnlockHistoryResponse,
paidVoiceHistoryResponse,
paidPaymentOrderStatusResponse,
paymentPlansResponse,
tipPaymentPlansResponse,
refreshedEmailLoginResponse,
refreshedGuestLoginResponse,
userEntitlementsResponse,
vipStatusResponse,
} from "./test-data";
import { registerAuthMocks } from "./api/auth";
import { registerChatMocks } from "./api/chat";
import { registerPaymentMocks } from "./api/payment";
import { registerUserMocks } from "./api/user";
interface MockCoreApisOptions {
export interface MockCoreApisOptions {
chatLimitTriggerAt?: number;
chatSendTokenRefreshFlow?: boolean;
paidImageFlow?: boolean;
@@ -33,180 +13,21 @@ interface MockCoreApisOptions {
paidVoiceInsufficientCreditsFlow?: boolean;
}
export async function mockCoreApis(
page: Page,
options: MockCoreApisOptions = {},
): Promise<void> {
const {
chatLimitTriggerAt,
chatSendTokenRefreshFlow = false,
paidImageFlow = false,
paidImageInsufficientCreditsFlow = false,
paidVoiceInsufficientCreditsFlow = false,
} = options;
let sendCount = 0;
let hasExpiredChatSend = false;
let paidImageRequested = false;
let paidImageUnlocked = false;
export async function mockCoreApis(page: Page, options: MockCoreApisOptions = {}) {
const chatOptions = {
chatLimitTriggerAt: options.chatLimitTriggerAt,
chatSendTokenRefreshFlow: options.chatSendTokenRefreshFlow ?? false,
paidImageFlow: options.paidImageFlow ?? false,
paidImageInsufficientCreditsFlow: options.paidImageInsufficientCreditsFlow ?? false,
paidVoiceInsufficientCreditsFlow: options.paidVoiceInsufficientCreditsFlow ?? false,
};
const chatState = { hasExpiredChatSend: false, paidImageRequested: false, paidImageUnlocked: false };
await page.route("**/api/auth/guest", async (route) => {
const response =
chatSendTokenRefreshFlow && hasExpiredChatSend
? refreshedGuestLoginResponse
: guestLoginResponse;
await route.fulfill({ json: apiEnvelope(response) });
});
await page.route("**/api/auth/login", async (route) => {
if (paidImageFlow) {
paidImageRequested = true;
}
await route.fulfill({ json: apiEnvelope(emailLoginResponse) });
});
await page.route("**/api/auth/refresh", async (route) => {
await route.fulfill({ json: apiEnvelope(refreshedEmailLoginResponse) });
});
await page.route("**/api/auth/logout", async (route) => {
await route.fulfill({ json: apiEnvelope(null) });
});
await page.route("**/api/user/profile", async (route) => {
await route.fulfill({ json: apiEnvelope(e2eEmailUser) });
});
await page.route("**/api/user/entitlements", async (route) => {
await route.fulfill({ json: apiEnvelope(userEntitlementsResponse) });
});
await page.route("**/api/chat/history**", async (route) => {
const response =
paidVoiceInsufficientCreditsFlow
? paidVoiceHistoryResponse
: paidImageInsufficientCreditsFlow
? createPaidImageHistoryResponse(false)
: 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 : "";
if (chatSendTokenRefreshFlow && !hasExpiredChatSend) {
hasExpiredChatSend = true;
await route.fulfill({
status: 401,
json: {
message: "expired",
},
});
return;
}
const response =
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) });
});
await page.route("**/api/chat/unlock-private", async (route) => {
const response =
paidVoiceInsufficientCreditsFlow
? insufficientCreditsUnlockVoiceResponse
: paidImageInsufficientCreditsFlow
? insufficientCreditsUnlockImageResponse
: paidImageFlow && paidImageRequested && !paidImageUnlocked
? insufficientCreditsUnlockImageResponse
: {
unlocked: true,
content: "Unlocked message",
reason: "ok",
creditBalance: 100,
creditsCharged: 0,
requiredCredits: 0,
shortfallCredits: 0,
lockDetail: {
locked: false,
showContent: true,
showUpgrade: false,
reason: null,
hint: null,
detail: null,
},
};
await route.fulfill({ json: apiEnvelope(response) });
});
await page.route("**/api/payment/plans**", async (route) => {
await route.fulfill({ json: apiEnvelope(paymentPlansResponse) });
});
await page.route("**/api/payment/tip-plans**", async (route) => {
await route.fulfill({ json: apiEnvelope(tipPaymentPlansResponse) });
});
await page.route("**/api/payment/create-order", async (route) => {
await route.fulfill({ json: apiEnvelope(createPaymentOrderResponse) });
});
await page.route("**/api/payment/order-status**", async (route) => {
if (paidImageFlow && paidImageRequested) {
paidImageUnlocked = true;
}
await route.fulfill({ json: apiEnvelope(paidPaymentOrderStatusResponse) });
});
await page.route("**/api/payment/vip-status**", async (route) => {
await route.fulfill({ json: apiEnvelope(vipStatusResponse) });
await registerAuthMocks(page, {
chatSendTokenRefreshFlow: chatOptions.chatSendTokenRefreshFlow,
isChatSendTokenExpired: () => chatState.hasExpiredChatSend,
});
await registerUserMocks(page);
await registerChatMocks(page, chatOptions, chatState);
await registerPaymentMocks(page);
}