import { isAmdEnv, isRequireJsEnv } from "../lib/amd-utils"; import { constants, FL_NAMESPACE } from "../constants"; import assetLoader from "@braintree/asset-loader"; import loadAxo from "../loadAxoScript"; import { AxoSupportedPlatforms } from "../types"; import { btModulesLoadConfig } from "../lib/safeLoadBtModule/types"; jest.mock("@braintree/asset-loader", () => { return { loadScript: jest.fn(), loadStylesheet: jest.fn(), }; }); jest.mock("../lib/amd-utils"); const mockIsAmdEnv = isAmdEnv as jest.MockedFn; const mockIsRequireJsEnv = isRequireJsEnv as jest.MockedFn< typeof isRequireJsEnv >; describe("loadAxoScript", () => { let testContext; const version = "3.96"; const originalWindow = window; const originalPerformance = performance; beforeEach(() => { testContext = {}; testContext.mockLoadResponse = { metadata: { localeUrl: "https://www.paypalobjects.com/connect-boba/locales/", }, }; testContext.client = { getVersion: () => { return version; }, }; // Mock performance API Object.defineProperty(globalThis, "performance", { value: { mark: jest.fn(), measure: jest.fn(), }, }); }); afterEach(() => { Object.defineProperty(globalThis, "window", { value: originalWindow, }); Object.defineProperty(globalThis, "performance", { value: originalPerformance, }); }); describe("Unhappy path", () => { it("rejects promise if platform is invalid", async () => { const promise = loadAxo({ // @ts-expect-error for test purposes platform: "Invalid Platform", btSdkVersion: version, minified: true, }); await expect(promise).rejects.toEqual( new Error("unsupported axo platform") ); }); }); describe("in a non-AMD environment", () => { beforeAll(() => { mockIsAmdEnv.mockReturnValue(false); }); it("should call loadScript with the minified URL when specified", async () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: true, }); expect(promise).resolves.toStrictEqual(testContext.mockLoadResponse); expect(assetLoader.loadScript).toHaveBeenCalled(); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ src: `${constants.CDNX_PROD}/${constants.AXO_ASSET_PATH}/${constants.AXO_ASSET_NAME.minified}.js`, }) ); }); it("should load the unminified URL when specified", () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: false, }); expect(promise).resolves.toStrictEqual(testContext.mockLoadResponse); expect(assetLoader.loadScript).toHaveBeenCalled(); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ src: `${constants.CDNX_PROD}/${constants.AXO_ASSET_PATH}/${constants.AXO_ASSET_NAME.unminified}.js`, }) ); }); it("should default to the minified script when no environment is specified", () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, }); expect(promise).resolves.toStrictEqual(testContext.mockLoadResponse); expect(assetLoader.loadScript).toHaveBeenCalled(); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ src: `${constants.CDNX_PROD}/${constants.AXO_ASSET_PATH}/${constants.AXO_ASSET_NAME.minified}.js`, }) ); }); it("should load the bundle override URL when specified", () => { const bundleId = "2bc4343c92655"; const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, metadata: { bundleIdOverride: bundleId }, }); expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `https://cdn-${bundleId}.static.engineering.dev.paypalinc.com/${constants.LOCALE_PATH}`, }, }); expect(assetLoader.loadScript).toHaveBeenCalled(); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ src: `https://cdn-${bundleId}.static.engineering.dev.paypalinc.com/${constants.AXO_ASSET_PATH}/${constants.AXO_ASSET_NAME.minified}.js`, }) ); }); it("should call loadScript with the minified URL and bundle override URL when specified", async () => { const bundleId = "9df9369c92655"; const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: true, metadata: { bundleIdOverride: bundleId }, }); expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `https://cdn-${bundleId}.static.engineering.dev.paypalinc.com/${constants.LOCALE_PATH}`, }, }); expect(assetLoader.loadScript).toHaveBeenCalled(); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ src: `https://cdn-${bundleId}.static.engineering.dev.paypalinc.com/${constants.AXO_ASSET_PATH}/${constants.AXO_ASSET_NAME.minified}.js`, }) ); }); it("should load Bt hcf and axo when platform is bt", () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: true, }); expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `${constants.CDNX_PROD}/${constants.LOCALE_PATH}`, }, }); expect(assetLoader.loadScript).toHaveBeenCalledTimes(2); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ id: `hcf-${version}`, src: `https://js.braintreegateway.com/web/${version}/js/hosted-fields.min.js`, }) ); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ src: `${constants.CDNX_PROD}/${constants.AXO_ASSET_PATH}/${constants.AXO_ASSET_NAME.minified}.js`, }) ); }); it("should load Bt client, hcf and Axo when platform is ppcp", () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.PPCP, btSdkVersion: version, minified: true, }); expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `${constants.CDNX_PROD}/${constants.LOCALE_PATH}`, }, }); expect(assetLoader.loadScript).toHaveBeenCalledTimes(3); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ id: `client-${version}`, src: `https://js.braintreegateway.com/web/${version}/js/client.min.js`, }) ); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ id: `hcf-${version}`, src: `https://js.braintreegateway.com/web/${version}/js/hosted-fields.min.js`, }) ); expect(assetLoader.loadScript).toHaveBeenCalledWith( expect.objectContaining({ src: `${constants.CDNX_PROD}/${constants.AXO_ASSET_PATH}/${constants.AXO_ASSET_NAME.minified}.js`, }) ); }); }); describe("in an AMD environment", () => { const originalRequire = require; let mockRequire: jest.Mock; beforeEach(() => { mockIsAmdEnv.mockReturnValue(true); mockIsRequireJsEnv.mockReturnValue(false); mockRequire = jest.fn().mockImplementation((deps, cb) => { cb(); }); Object.defineProperty(globalThis, "require", { value: mockRequire, writable: true, }); }); afterEach(() => { Object.defineProperty(globalThis, "require", { value: originalRequire, }); }); describe("with Require.js specifically", () => { let mockRequireJs; let mockRequireJsConfig; beforeEach(() => { mockIsRequireJsEnv.mockReturnValue(true); mockRequireJs = jest.fn(); mockRequireJsConfig = jest.fn(); mockRequireJs.config = mockRequireJsConfig; Object.defineProperty(globalThis, "requirejs", { value: mockRequireJs, writable: true, }); }); afterEach(() => { Object.defineProperty(globalThis, "requirejs", { value: undefined, }); }); it("configures Require.js with the fastlane namespace", async () => { await loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, }); expect(mockRequireJsConfig).toHaveBeenCalledWith({ paths: { [FL_NAMESPACE]: "https://www.paypalobjects.com/connect-boba", }, }); }); }); it("should load the minified AMD modules", async () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: true, }); await expect(promise).resolves.toStrictEqual( testContext.mockLoadResponse ); expect(mockRequire).toHaveBeenCalledWith( [btModulesLoadConfig.hostedFields.amdModule.minified], expect.any(Function), expect.any(Function) ); expect(mockRequire).toHaveBeenCalledWith( [`${FL_NAMESPACE}/axo.min`], expect.any(Function), expect.any(Function) ); expect(assetLoader.loadScript).not.toHaveBeenCalled(); }); it("should load the unminified AMD modules", async () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: false, }); await expect(promise).resolves.toStrictEqual( testContext.mockLoadResponse ); expect(mockRequire).toHaveBeenCalledWith( [`${FL_NAMESPACE}/axo`], expect.any(Function), expect.any(Function) ); expect(assetLoader.loadScript).not.toHaveBeenCalled(); }); it("should default to the minified script when no environment is specified", async () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, }); await expect(promise).resolves.toStrictEqual( testContext.mockLoadResponse ); expect(mockRequire).toHaveBeenCalledWith( [`${FL_NAMESPACE}/axo.min`], expect.any(Function), expect.any(Function) ); expect(assetLoader.loadScript).not.toHaveBeenCalled(); }); it("should load the bundle override URL when specified", async () => { const bundleId = "2bc4343c92655"; const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, metadata: { bundleIdOverride: bundleId }, }); await expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `https://cdn-${bundleId}.static.engineering.dev.paypalinc.com/${constants.LOCALE_PATH}`, }, }); expect(mockRequire).toHaveBeenCalledWith( [`${FL_NAMESPACE}/axo.min`], expect.any(Function), expect.any(Function) ); expect(assetLoader.loadScript).not.toHaveBeenCalled(); }); it("should call loadScript with the minified URL and bundle override URL when specified", async () => { const bundleId = "9df9369c92655"; const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: true, metadata: { bundleIdOverride: bundleId }, }); await expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `https://cdn-${bundleId}.static.engineering.dev.paypalinc.com/${constants.LOCALE_PATH}`, }, }); expect(assetLoader.loadScript).not.toHaveBeenCalled(); }); it("should load Bt hcf and axo when platform is bt", async () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.BT, btSdkVersion: version, minified: true, }); await expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `${constants.CDNX_PROD}/${constants.LOCALE_PATH}`, }, }); expect(mockRequire).toHaveBeenCalledTimes(2); expect(mockRequire).toHaveBeenCalledWith( [`${btModulesLoadConfig.hostedFields.amdModule.minified}`], expect.any(Function), expect.any(Function) ); expect(mockRequire).toHaveBeenCalledWith( [`${FL_NAMESPACE}/axo.min`], expect.any(Function), expect.any(Function) ); }); it("should load Bt client, hcf and Axo when platform is ppcp", async () => { const promise = loadAxo({ platform: AxoSupportedPlatforms.PPCP, btSdkVersion: version, minified: true, }); await expect(promise).resolves.toStrictEqual({ metadata: { localeUrl: `${constants.CDNX_PROD}/${constants.LOCALE_PATH}`, }, }); expect(mockRequire).toHaveBeenCalledTimes(3); expect(mockRequire).toHaveBeenCalledWith( [`${btModulesLoadConfig.hostedFields.amdModule.minified}`], expect.any(Function), expect.any(Function) ); expect(mockRequire).toHaveBeenCalledWith( [`${btModulesLoadConfig.client.amdModule.minified}`], expect.any(Function), expect.any(Function) ); expect(mockRequire).toHaveBeenCalledWith( [`${FL_NAMESPACE}/axo.min`], expect.any(Function), expect.any(Function) ); }); }); });