diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..50ca2ff3 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + testEnvironment: "jsdom", +}; diff --git a/package.json b/package.json index 5c75ab51..32f7a0ae 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "license": "Apache-2.0", "readmeFilename": "README.md", "dependencies": { - "@krakenjs/beaver-logger": "^5.0.0", + "@krakenjs/beaver-logger": "^5.5.0", "@krakenjs/belter": "^2.0.0", "@krakenjs/cross-domain-utils": "^3.0.2", "@krakenjs/jsx-pragmatic": "^3.0.0", diff --git a/src/data-collector.js b/src/data-collector.js new file mode 100644 index 00000000..433f00d2 --- /dev/null +++ b/src/data-collector.js @@ -0,0 +1,57 @@ +/* @flow */ + +// import { ENV } from '@paypal/sdk-constants/src'; + +export const FRAUDNET_FNCLS = "fnparams-dede7cc5-15fd-4c75-a9f4-36c430ee3a99"; +export const FRAUDNET_APP_NAME = "SMART_PAYMENT_BUTTONS"; + +// const FRAUDNET_URL = { +// [ENV.LOCAL]: "https://www.msmaster.qa.paypal.com/en_US/m/fb-raw.js", +// [ENV.STAGE]: "https://stage2mb044.qa.paypal.com/fraudnetjsnodeweb/automate/develop/stage_raw.js", +// [ENV.SANDBOX]: "https://c.paypal.com/da/r/fb.js", +// [ENV.PRODUCTION]: "https://c.paypal.com/da/r/fb.js", +// [ENV.TEST]: "https://c.paypal.com/da/r/fb.js", +// }; + +export const loadDataCollector = async ({ + cspNonce = "", + clientMetadataID, + env, +}) => { + // TODO: Ensure these functions return zalgo promises accordingly. reference fraudnet.js in SPB for pattern + createConfigScript({ cspNonce, clientMetadataID }); + createFraudnetScript({ cspNonce, env }); + + // TODO: test and implement the window.fallback/timeout logic (see fraudnet.js in SPB) +}; + +export const createConfigScript = ({ cspNonce = "", clientMetadataID }) => { + const fraudnetConfig = { + f: clientMetadataID, + s: FRAUDNET_APP_NAME, + cb1: "fnCallback", + }; + + const configScript = document.createElement("script"); + + configScript.setAttribute("nonce", cspNonce); + configScript.setAttribute("type", "application/json"); + configScript.setAttribute("id", "fconfig"); + configScript.setAttribute("fncls", FRAUDNET_FNCLS); + configScript.textContent = JSON.stringify(fraudnetConfig); + + document.body.appendChild(configScript); +}; + +export const createFraudnetScript = ({ cspNonce, env }) => { + const fraudnetScript = document.createElement("script"); + + // const fraudnetUrl = FRAUDNET_URL[env] + fraudnetScript.setAttribute("nonce", cspNonce); + // fraudnetScript.setAttribute('src', fraudnetUrl) + fraudnetScript.addEventListener("error", () => { + /* We'll want to resolve here Zalgo style */ + }); + + document.body.appendChild(fraudnetScript); +}; diff --git a/test/server/data-collector.test.js b/test/server/data-collector.test.js new file mode 100644 index 00000000..be7f1413 --- /dev/null +++ b/test/server/data-collector.test.js @@ -0,0 +1,89 @@ +/** + * @jest-environment jsdom + */ +import { + loadDataCollector, + createConfigScript, + createFraudnetScript, + FRAUDNET_FNCLS, + FRAUDNET_APP_NAME, +} from "../../src/data-collector"; + +describe.only("data-collector.js", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const testInputs = { + clientMetadataID: "some-cmid", + fraudnetAppName: "spb-test-name", + env: "unit-tests", + cspNonce: "not-sure-what-this-is-yet-csp-nonce", + queryStringParams: { + /* TBD */ + }, + }; + + test("creates both scripts", async () => { + const mockAppendChild = jest.fn(); + document.body.appendChild = mockAppendChild; + + await loadDataCollector(testInputs); + + expect(mockAppendChild).toHaveBeenCalledTimes(2); + }); + + test("it create and append the config element", async () => { + const mockSetAttribute = jest.fn(); + const mockAppendChild = jest.fn(); + const mockReturnedElement = { + setAttribute: mockSetAttribute, + }; + document.createElement = jest.fn(() => { + return mockReturnedElement; + }); + document.body.appendChild = mockAppendChild; + + const expectedScriptConfig = { + f: testInputs.clientMetadataID, + s: FRAUDNET_APP_NAME, + //u: + cb1: "fnCallback", + }; + await createConfigScript(testInputs); + + expect(document.createElement).toHaveBeenNthCalledWith(1, "script"); + expect(mockSetAttribute).toBeCalledWith("nonce", testInputs.cspNonce); + expect(mockSetAttribute).toBeCalledWith("type", "application/json"); + expect(mockSetAttribute).toBeCalledWith("id", "fconfig"); + expect(mockSetAttribute).toBeCalledWith("fncls", FRAUDNET_FNCLS); + expect(mockReturnedElement.textContent).toEqual( + JSON.stringify(expectedScriptConfig) + ); + expect(mockAppendChild).toBeCalledWith(mockReturnedElement); + }); + + test("creates fraudnet script with config", async () => { + const mockAppendChild = jest.fn(); + const mockListener = jest.fn(); + const mockSetAttribute = jest.fn(); + const mockReturnedElement = { + setAttribute: mockSetAttribute, + addEventListener: mockListener, + }; + document.body.appendChild = mockAppendChild; + + document.createElement = jest.fn(() => { + return mockReturnedElement; + }); + + await createFraudnetScript(testInputs); + + expect(document.createElement).toHaveBeenCalledWith("script"); + expect(mockSetAttribute).toHaveBeenCalledWith("nonce", testInputs.cspNonce); + // TODO: Update the test and implementation to address the need for the SRC attribute and various env urls + // expect(mockSetAttribute).toHaveBeenCalledWith("src", expect.stringContaining("fb.js")) + expect(mockListener).toHaveBeenCalledWith("error", expect.any(Function)); + expect(mockAppendChild).toBeCalledWith(mockReturnedElement); + }); +});