110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
|
|
import { describe, expect, test, vi } from "vitest";
|
||
|
|
import { normalizeAppUrl, requestActivityCheck } from "../extensions/thank-you-activity/src/activity-api";
|
||
|
|
|
||
|
|
describe("checkout extension activity api", () => {
|
||
|
|
test("normalizes only HTTPS app URLs", () => {
|
||
|
|
expect(normalizeAppUrl("https://app.example.com/")).toBe("https://app.example.com");
|
||
|
|
expect(normalizeAppUrl(" https://app.example.com/path/ ")).toBe("https://app.example.com/path");
|
||
|
|
expect(normalizeAppUrl("http://localhost:3000")).toBeNull();
|
||
|
|
expect(normalizeAppUrl("")).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test("posts order context with a fresh checkout session token", async () => {
|
||
|
|
const fetchImpl = vi.fn(async () =>
|
||
|
|
new Response(
|
||
|
|
JSON.stringify({
|
||
|
|
success: true,
|
||
|
|
redirectUrl: "https://activity.example.com",
|
||
|
|
buttonText: "Claim",
|
||
|
|
}),
|
||
|
|
{ status: 200 },
|
||
|
|
),
|
||
|
|
);
|
||
|
|
const getSessionToken = vi.fn(async () => "checkout-token");
|
||
|
|
|
||
|
|
const result = await requestActivityCheck({
|
||
|
|
appUrl: "https://app.example.com/",
|
||
|
|
order: {
|
||
|
|
id: "gid://shopify/Order/123",
|
||
|
|
name: "#1001",
|
||
|
|
},
|
||
|
|
locale: "en",
|
||
|
|
getSessionToken,
|
||
|
|
fetchImpl,
|
||
|
|
timeoutMs: 100,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(result).toEqual({
|
||
|
|
success: true,
|
||
|
|
redirectUrl: "https://activity.example.com",
|
||
|
|
buttonText: "Claim",
|
||
|
|
});
|
||
|
|
expect(getSessionToken).toHaveBeenCalledOnce();
|
||
|
|
expect(fetchImpl).toHaveBeenCalledWith(
|
||
|
|
"https://app.example.com/api/action/activity/check",
|
||
|
|
expect.objectContaining({
|
||
|
|
method: "POST",
|
||
|
|
cache: "no-store",
|
||
|
|
headers: {
|
||
|
|
Authorization: "Bearer checkout-token",
|
||
|
|
"Content-Type": "application/json",
|
||
|
|
},
|
||
|
|
body: JSON.stringify({
|
||
|
|
orderId: "gid://shopify/Order/123",
|
||
|
|
orderNumber: "#1001",
|
||
|
|
locale: "en",
|
||
|
|
}),
|
||
|
|
signal: expect.any(AbortSignal),
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("returns hidden state for non-2xx and invalid JSON responses", async () => {
|
||
|
|
const nonOk = await requestActivityCheck({
|
||
|
|
appUrl: "https://app.example.com",
|
||
|
|
order: { id: "gid://shopify/Order/123" },
|
||
|
|
locale: "en",
|
||
|
|
getSessionToken: async () => "checkout-token",
|
||
|
|
fetchImpl: async () => new Response(JSON.stringify({ success: true }), { status: 500 }),
|
||
|
|
timeoutMs: 100,
|
||
|
|
});
|
||
|
|
|
||
|
|
const invalidJson = await requestActivityCheck({
|
||
|
|
appUrl: "https://app.example.com",
|
||
|
|
order: { id: "gid://shopify/Order/123" },
|
||
|
|
locale: "en",
|
||
|
|
getSessionToken: async () => "checkout-token",
|
||
|
|
fetchImpl: async () => new Response("not json", { status: 200 }),
|
||
|
|
timeoutMs: 100,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(nonOk).toEqual({ success: false });
|
||
|
|
expect(invalidJson).toEqual({ success: false });
|
||
|
|
});
|
||
|
|
|
||
|
|
test("does not call the backend when app URL or order ID is unavailable", async () => {
|
||
|
|
const fetchImpl = vi.fn();
|
||
|
|
|
||
|
|
const invalidUrl = await requestActivityCheck({
|
||
|
|
appUrl: "http://localhost:3000",
|
||
|
|
order: { id: "gid://shopify/Order/123" },
|
||
|
|
locale: "en",
|
||
|
|
getSessionToken: async () => "checkout-token",
|
||
|
|
fetchImpl,
|
||
|
|
timeoutMs: 100,
|
||
|
|
});
|
||
|
|
const missingOrder = await requestActivityCheck({
|
||
|
|
appUrl: "https://app.example.com",
|
||
|
|
order: undefined,
|
||
|
|
locale: "en",
|
||
|
|
getSessionToken: async () => "checkout-token",
|
||
|
|
fetchImpl,
|
||
|
|
timeoutMs: 100,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(invalidUrl).toEqual({ success: false });
|
||
|
|
expect(missingOrder).toEqual({ success: false });
|
||
|
|
expect(fetchImpl).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
});
|