feat(auth): add Messenger topup handoff entry
Docker Image / Build and Push Docker Image (push) Successful in 2m9s
Docker Image / Build and Push Docker Image (push) Successful in 2m9s
This commit is contained in:
@@ -12,7 +12,7 @@ export interface MockCoreApisOptions {
|
||||
paidImageFlow?: boolean;
|
||||
paidImageInsufficientCreditsFlow?: boolean;
|
||||
paidVoiceInsufficientCreditsFlow?: boolean;
|
||||
psidLoginFlow?: boolean;
|
||||
topUpHandoffFlow?: boolean;
|
||||
}
|
||||
|
||||
export async function mockCoreApis(page: Page, options: MockCoreApisOptions = {}) {
|
||||
@@ -22,14 +22,14 @@ export async function mockCoreApis(page: Page, options: MockCoreApisOptions = {}
|
||||
paidImageFlow: options.paidImageFlow ?? false,
|
||||
paidImageInsufficientCreditsFlow: options.paidImageInsufficientCreditsFlow ?? false,
|
||||
paidVoiceInsufficientCreditsFlow: options.paidVoiceInsufficientCreditsFlow ?? false,
|
||||
psidLoginFlow: options.psidLoginFlow ?? false,
|
||||
topUpHandoffFlow: options.topUpHandoffFlow ?? false,
|
||||
};
|
||||
const chatState = { hasExpiredChatSend: false, paidImageRequested: false, paidImageUnlocked: false };
|
||||
|
||||
await registerAuthMocks(page, {
|
||||
chatSendTokenRefreshFlow: chatOptions.chatSendTokenRefreshFlow,
|
||||
isChatSendTokenExpired: () => chatState.hasExpiredChatSend,
|
||||
psidLoginFlow: chatOptions.psidLoginFlow,
|
||||
topUpHandoffFlow: chatOptions.topUpHandoffFlow,
|
||||
});
|
||||
await registerCharacterMocks(page);
|
||||
await registerUserMocks(page);
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
import { apiEnvelope } from "../data/common";
|
||||
import { emailLoginResponse, guestLoginResponse, psidLoginResponse, refreshedEmailLoginResponse, refreshedGuestLoginResponse } from "../data/auth";
|
||||
import { emailLoginResponse, guestLoginResponse, refreshedEmailLoginResponse, refreshedGuestLoginResponse, topUpHandoffResponse } from "../data/auth";
|
||||
|
||||
export interface AuthMockState {
|
||||
chatSendTokenRefreshFlow: boolean;
|
||||
isChatSendTokenExpired: () => boolean;
|
||||
psidLoginFlow: boolean;
|
||||
topUpHandoffFlow: boolean;
|
||||
}
|
||||
|
||||
export async function registerAuthMocks(page: Page, state: AuthMockState) {
|
||||
await page.route("**/api/auth/login/facebook/psid", async (route) => {
|
||||
if (!state.psidLoginFlow) {
|
||||
await page.route("**/api/auth/handoff/topup/consume", async (route) => {
|
||||
if (!state.topUpHandoffFlow) {
|
||||
await route.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({ json: apiEnvelope(psidLoginResponse) });
|
||||
await route.fulfill({ json: apiEnvelope(topUpHandoffResponse) });
|
||||
});
|
||||
|
||||
await page.route("**/api/auth/guest", async (route) => {
|
||||
|
||||
+12
-10
@@ -50,14 +50,16 @@ export const refreshedEmailLoginResponse = {
|
||||
refreshToken: "e2e-refreshed-email-refresh-token",
|
||||
};
|
||||
|
||||
export const psidLoginResponse = {
|
||||
token: "e2e-psid-guest-token",
|
||||
refreshToken: "",
|
||||
matchedBy: "psid",
|
||||
fbAsid: "",
|
||||
fbPsid: "e2e-facebook-psid",
|
||||
hasCompleteFacebookIdentity: false,
|
||||
isGuest: true,
|
||||
user: e2eUser,
|
||||
userId: e2eUser.id,
|
||||
export const topUpHandoffResponse = {
|
||||
token: "e2e-messenger-token",
|
||||
refreshToken: "e2e-messenger-refresh-token",
|
||||
loginStatus: "facebookMessenger",
|
||||
merged: false,
|
||||
user: {
|
||||
...e2eEmailUser,
|
||||
id: "user_e2e_messenger",
|
||||
username: "E2E Messenger User",
|
||||
email: "messenger_hash@messenger.cozsweet.invalid",
|
||||
loginProvider: "facebook_messenger",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,49 +1,74 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { mockCoreApis } from "@e2e/fixtures/api-mocks";
|
||||
import {
|
||||
clearBrowserState,
|
||||
defaultCharacterChatUrl,
|
||||
} from "@e2e/fixtures/test-helpers";
|
||||
import { clearBrowserState } from "@e2e/fixtures/test-helpers";
|
||||
|
||||
const psid = "e2e-facebook-psid";
|
||||
const handoffToken = "e2e-handoff-token-that-is-at-least-32-characters";
|
||||
|
||||
test.beforeEach(async ({ baseURL, context, page }) => {
|
||||
await clearBrowserState(context, page, baseURL);
|
||||
await mockCoreApis(page, { psidLoginFlow: true });
|
||||
await mockCoreApis(page, { topUpHandoffFlow: true });
|
||||
});
|
||||
|
||||
test("user enters from an external PSID link and becomes a guest", async ({
|
||||
test("user consumes a top-up handoff and opens credit packages as a formal Messenger user", async ({
|
||||
page,
|
||||
}) => {
|
||||
const psidLoginRequestPromise = page.waitForRequest(
|
||||
"**/api/auth/login/facebook/psid",
|
||||
const handoffRequestPromise = page.waitForRequest(
|
||||
"**/api/auth/handoff/topup/consume",
|
||||
);
|
||||
|
||||
await page.goto(`/external-entry?target=chat&psid=${psid}`);
|
||||
await page.goto(
|
||||
`/external-entry?target=topup&handoffToken=${encodeURIComponent(handoffToken)}`,
|
||||
);
|
||||
|
||||
const psidLoginRequest = await psidLoginRequestPromise;
|
||||
expect(psidLoginRequest.method()).toBe("POST");
|
||||
expect(psidLoginRequest.postDataJSON()).toMatchObject({
|
||||
psid,
|
||||
bindToGuest: true,
|
||||
});
|
||||
expect(psidLoginRequest.postDataJSON().deviceId).toEqual(expect.any(String));
|
||||
const handoffRequest = await handoffRequestPromise;
|
||||
expect(handoffRequest.method()).toBe("POST");
|
||||
expect(handoffRequest.postDataJSON()).toEqual({ handoffToken });
|
||||
|
||||
await expect(page).toHaveURL(defaultCharacterChatUrl);
|
||||
await expect(page.getByRole("textbox", { name: "Message" })).toBeVisible();
|
||||
await expect(page).toHaveURL("/subscription?type=topup");
|
||||
await expect(
|
||||
page.getByRole("button", { name: /Pay and Top Up/i }),
|
||||
).toBeVisible();
|
||||
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(() => ({
|
||||
loginProvider: localStorage.getItem("cozsweet:login_provider"),
|
||||
guestToken: localStorage.getItem("cozsweet:guest_token"),
|
||||
psid: localStorage.getItem("cozsweet:psid"),
|
||||
loginToken: localStorage.getItem("cozsweet:login_token"),
|
||||
refreshToken: localStorage.getItem("cozsweet:refresh_token"),
|
||||
})),
|
||||
)
|
||||
.toMatchObject({
|
||||
loginProvider: "guest",
|
||||
guestToken: "e2e-psid-guest-token",
|
||||
psid,
|
||||
loginProvider: "facebookMessenger",
|
||||
loginToken: "e2e-messenger-token",
|
||||
refreshToken: "e2e-messenger-refresh-token",
|
||||
});
|
||||
});
|
||||
|
||||
test("invalid top-up handoff does not log in and is removed from the URL", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route("**/api/auth/handoff/topup/consume", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 410,
|
||||
json: {
|
||||
detail: {
|
||||
code: "HANDOFF_EXPIRED",
|
||||
message: "充值链接已过期",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto(
|
||||
`/external-entry?target=topup&handoffToken=${encodeURIComponent(handoffToken)}`,
|
||||
);
|
||||
|
||||
await expect(page).toHaveURL("/external-entry?target=topup");
|
||||
await expect(
|
||||
page.getByText(/invalid or has expired/i),
|
||||
).toBeVisible();
|
||||
await expect
|
||||
.poll(() => page.evaluate(() => localStorage.getItem("cozsweet:login_provider")))
|
||||
.not.toBe("facebookMessenger");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user