Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

integration tests framework #2437

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/zoid/buttons/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
fundingEligibility: {
type: "object",
default: getRefinedFundingEligibility,
value: __ENV__ === ENV.LOCAL ? undefined : getRefinedFundingEligibility,
value:
getEnv() === ENV.LOCAL ? undefined : getRefinedFundingEligibility,
queryParam: true,
serialization: "base64",
},
Expand Down
35 changes: 35 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Testing

Testing is important to the health of an application but it can be confusing to understand when and what type of test to write. Hopefully the following explanations are helpful in your test-writing journey.

## Functional

These are end to end tests. There should be no mocking of internal dependencies or external endpoints. These should be run with test accounts and test data that simulate real user flows. These should be able to run in our staging, sandbox, and production environments.

There should be few of these tests. Simple success and error cases should be covered. This is usually not the place to test detailed internal app logic.

Example tests:

-

## Integration

These are like end to end tests with one difference. There should be no mocking of internal dependencies unless its not relevant to the test at hand. There should be full or close to full mocking of external dependencies.

There should be more integration tests than functional tests. These are great to test different return types of external services and edge cases from business logic

Example tests:

-

## Unit

These are small, fast tests that should mock most if not all dependencies. These tests should live alongside the code they test. For example, a file called `/src/service.ts` should have a unit test file `/src/service.test.ts` in the same parent folder.

Unit tests are not just for verifying business logic. They work well as guides to designing the code being written. Code that is easy to unit test is usually easier to maintain and understand.

Not everything needs unit tests. Some functionality makes more sense to be tested with integration tests. An example in this repository would be controller files. These are the brains of the routes for this app. While there would be design benefits from unit testing these files, the confidence we'd gain from using integration tests would be much greater. Hopefully, the controller files are made up of smaller functions that have unit tests and all the logic is tested together at the integration and functional level.

## Legacy test folder

This folder contains tests written before we solidified our best practices. Our hope is to migrate these tests to functional, integration, and/or unit tests and remove the `test` singular folder in favor of the `tests` plural folder.
28 changes: 28 additions & 0 deletions tests/integration/buttons.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* @flow */
/* eslint max-lines: 0 */
import { vi, describe, beforeEach, afterEach, test, expect } from "vitest";

import { Buttons } from "../../src/interface/button";

import {
addComponentsToWindow,
createTestContainer,
destroyTestContainer,
} from "./testutils";

describe("PayPal Buttons Component", () => {
beforeEach(() => {
createTestContainer();
addComponentsToWindow({ Buttons });
});

afterEach(() => {
destroyTestContainer();
});

test("should render Buttons with no issues", async () => {
await expect(
window.paypal.Buttons({}).render("#testContainer")
).resolves.toEqual();
});
});
28 changes: 28 additions & 0 deletions tests/integration/testutils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const addComponentsToWindow = (components: { [name: string]: any }) => {
if (!window.paypal) {
window.paypal = {};
}

for (const [name, component] of Object.entries(components)) {
if (component.__get__) {
window.paypal[name] = component.__get__();
} else {
window.paypal[name] = component;
}
}
};

export const createTestContainer = () => {
const container = document.createElement("div");
container.id = "testContainer";

document.body?.appendChild(container);
};

export const destroyTestContainer = () => {
const container = document.querySelector("#testContainer");

if (container) {
container.remove();
}
};
50 changes: 32 additions & 18 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,39 @@
/* @flow */

import { defineConfig } from "vite";
import { flowPlugin, esbuildFlowPlugin } from "@bunchtogether/vite-plugin-flow";

Check failure on line 11 in vite.config.js

View workflow job for this annotation

GitHub Actions / main

There should be at least one empty line between import groups
import { __ZOID__, __POST_ROBOT__, __PAYPAL_CHECKOUT__ } from "./globals";

const FILE_NAME = "sdk";
const PROTOCOL = "https";
const HOSTNAME = "localhost.paypal.com";
const PORT = 9001;

const define = {
__DEBUG__: false,
__TEST__: true,
__WEB__: true,
__POST_ROBOT__: JSON.stringify({
__GLOBAL_KEY__: `__post_robot__`,
__AUTO_SETUP__: false,
__IE_POPUP_SUPPORT__: false,
__GLOBAL_MESSAGE_SUPPORT__: true,
__SCRIPT_NAMESPACE__: false,
}),
__PAYPAL_CHECKOUT__: JSON.stringify({
_MAJOR_VERSION__: "",
__MINOR_VERSION__: "",
}),
__DISABLE_SET_COOKIE__: false,
__EXPERIMENTATION__: {
__COMPONENTS__: JSON.stringify(["buttons"]),
__CORRELATION_ID__: JSON.stringify("abc123"),
__DEBUG__: "false",
__DISABLE_SET_COOKIE__: "false",
__ENV__: JSON.stringify("test"),
__EXPERIMENTATION__: JSON.stringify({
__EXPERIENCE__: "",
__TREATMENT__: "",
},
}),
__FUNDING_ELIGIBILITY__: JSON.stringify({ paypal: { eligible: true } }),
__HOST__: JSON.stringify(`${HOSTNAME}:${PORT}`),
__NAMESPACE__: JSON.stringify("paypal"),
__PATH__: JSON.stringify(`/${FILE_NAME}.js`),
__PAYPAL_CHECKOUT__: JSON.stringify(__PAYPAL_CHECKOUT__),
__PAYPAL_DOMAIN__: JSON.stringify("mock://www.paypal.com"),
__PAYPAL_API_DOMAIN__: JSON.stringify("mock://sandbox.paypal.com"),
__PORT__: JSON.stringify(PORT),
__PROTOCOL__: JSON.stringify(PROTOCOL),
__POST_ROBOT__: JSON.stringify(__POST_ROBOT__),
__SDK_HOST__: JSON.stringify(`${HOSTNAME}:${PORT}`),
__TEST__: "true",
__VERSION__: JSON.stringify("5.0.0"),
__WEB__: "true",
__ZOID__: JSON.stringify(__ZOID__),
};

// $FlowIssue
Expand All @@ -41,7 +52,10 @@
test: {
environment: "jsdom",
setupFiles: ["vitestSetup.js"],
include: ["**/src/**/*.test.{js,jsx}"],
include: [
"**/src/**/*.test.{js,jsx}",
"**/tests/**/*.test.{js,jsx,ts,tsx}",
],
globals: true,
coverage: {
// exclude untested files
Expand Down
12 changes: 11 additions & 1 deletion vitestSetup.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* @flow */
// eslint-disable-next-line import/no-nodejs-modules
import crypto from "crypto";

Check failure on line 3 in vitestSetup.js

View workflow job for this annotation

GitHub Actions / main

There should be at least one empty line between import groups

import { vi } from "vitest";

Object.defineProperty(window, "matchMedia", {
Expand All @@ -20,3 +19,14 @@

// $FlowIssue missing browser crypto typedefs
window.crypto = crypto.webcrypto;

// sdk-client relies on a current script to create and find a lot of
// globals that it needs. We are mocking that functionality for tests with this
const sourceURL = `https://${__SDK_HOST__}${__PATH__}?client-id=test`;

Check failure on line 25 in vitestSetup.js

View workflow job for this annotation

GitHub Actions / main

'__SDK_HOST__' is not defined
const script = document.createElement("script");
script.src = sourceURL;

Object.defineProperty(document, "currentScript", {
writable: true,
value: script,
});
Loading