-
Notifications
You must be signed in to change notification settings - Fork 14
feat: integration tests #572
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
Merged
kylemcd
merged 52 commits into
main
from
kyle-kno-8705-sdk-implement-tests-that-run-with-different-versions-of
Jun 5, 2025
Merged
Changes from 50 commits
Commits
Show all changes
52 commits
Select commit
Hold shift + click to select a range
59b3eb2
feat: run ci for multiple react versions
kylemcd 3b0665f
fix: file path reference for run job
kylemcd dc2ed21
fix: indent
kylemcd 549f29a
fix: include react version in concurrency check
kylemcd e6826b4
fix: set resolution strategy
kylemcd 55d6338
fix: change install strategy
kylemcd 6ad118a
fix: change install strategy
kylemcd a4b3396
fix: change install strategy
kylemcd 1b4e751
fix: add install step to child jobs
kylemcd 8e4d53a
fix: type check command
kylemcd 2713684
fix: add registry to setup node comamnd
kylemcd 869c9c6
fix: restore build cache to type check job
kylemcd 3357ab0
fix: combine jobs into steps to simplify output
kylemcd 0562fbf
fix: remove resolutions from pkg json and add it in CI
kylemcd cb86ce6
fix: pin react testing version in some cases
kylemcd 9c75ff3
feat: move to integration model
kylemcd a8bc7fd
fix: reconfigure deps and setup test
kylemcd 295ff74
fix: reconfigure setup + remove concurrency at top level
kylemcd 0364c82
feat: add env vars for integration tests
kylemcd 0d2b55e
fix: change how react versions get set
kylemcd 6b8a78b
fix: syntax error
kylemcd 90448bf
fix: syntax error + test structure
kylemcd 35b2274
fix: syntax error
kylemcd cc03ab8
fix: add env values + testing library pinning
kylemcd 872c021
fix: linting in integration
kylemcd 477bc3d
fix: remove concurrency flag
kylemcd b9dca6a
fix: downgrade dialog to fix jsx runtime issue
kylemcd 45327a5
chore: change how dialog dep is managed
kylemcd 4ee5960
fix: change how react version is set
kylemcd 8a10d60
fix: change how react version is set
kylemcd d6ff8a1
fix: change how integration is setup
kylemcd 6107997
Revert "fix: change how integration is setup"
kylemcd cf6c5ca
chore: revert back to cleaner way
kylemcd 91539dd
chore: upgrade dialog again
kylemcd d9bc2d3
fix: change jsx flag in react package
kylemcd 2de20b3
fix: change how versions are setup
kylemcd aa7e966
fix: pkg json read from
kylemcd 48ce16b
chore: verify working
kylemcd 3855620
fix: overiting of resolutions
kylemcd a667914
chore: verify working
kylemcd 917b5ac
chore: verify working
kylemcd 74d7fd2
fix: move resolutions to main pkg json to get correct resolution
kylemcd 135aa2b
feat: shell based runner
kylemcd c194c0d
fix: add install and build step to ci
kylemcd c93ba6a
fix: yarn install commands for CI
kylemcd 96bd498
chore: cleanup
kylemcd bd2ee45
fix: change ci name + restore test commands
kylemcd 7dcff62
chore: cleanup + add readme
kylemcd 33c2188
fix: add syntax highlighting to code blocks
kylemcd 6e05496
fix: heading
kylemcd 0e515d1
feat: add matrix for running tests
kylemcd 50c727c
fix: spelling mistakes
kylemcd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| name: Integration Tests | ||
|
|
||
| on: | ||
| pull_request: | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| run-integration-tests: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| INTEGRATION_KNOCK_PUBLIC_KEY: ${{ secrets.INTEGRATION_KNOCK_PUBLIC_KEY }} | ||
| INTEGRATION_KNOCK_USER_ID: ${{ secrets.INTEGRATION_KNOCK_USER_ID }} | ||
| INTEGRATION_KNOCK_FEED_ID: ${{ secrets.INTEGRATION_KNOCK_FEED_ID }} | ||
| steps: | ||
| - name: Checkout Latest | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version-file: "package.json" | ||
| cache: "yarn" | ||
| registry-url: "https://registry.npmjs.org" | ||
| scope: "@knocklabs" | ||
| - name: Install Dependencies | ||
| run: yarn install | ||
| - name: Build Packages | ||
| run: yarn build:packages | ||
| - name: Run Integration Tests | ||
| run: yarn test:integration | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| INTEGRATION_KNOCK_PUBLIC_KEY= | ||
| INTEGRATION_KNOCK_FEED_ID= | ||
| INTEGRATION_KNOCK_USER_ID= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| /** @type {import("eslint").Linter.Config} */ | ||
| module.exports = { | ||
| root: true, | ||
| extends: [ | ||
| "@knocklabs/eslint-config/library.js", | ||
| "plugin:react-hooks/recommended", | ||
| "plugin:jsx-a11y/strict", | ||
| ], | ||
| parserOptions: { | ||
| projects: ["tsconfig.json", "tsconfig.node.json"], | ||
| }, | ||
| settings: { | ||
| "jsx-a11y": { | ||
| polymorphicPropName: "as", | ||
| }, | ||
| }, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # Knock Javascript SDK Integration Testing | ||
|
|
||
| Because the Knock SDK can be utilized in different versions of React, it's vital that we have visibility into this surface area. This package aims to give maintainers an easy way to verify that their package | ||
| works across the versions of react that we support. | ||
|
|
||
| ## Getting started | ||
|
|
||
| First you will need to setup your environment variables so that the test runner can properly run any of the functions and components that we are testing. You can find an example of what this file should like in `.env.sample`. For Knock employees, you can find the file contents in the 1Password vault titled "JS SDK Integration Testing Env File". | ||
|
|
||
| ## Running the integration tests locally | ||
|
|
||
| There are 3 different ways that you can run the test suite depending on the result you're looking for. | ||
|
|
||
| Run for all versions of react that we support: | ||
|
|
||
| ```bash | ||
| yarn test:integration | ||
| ``` | ||
|
|
||
| Run for individual version of react that we support: | ||
|
|
||
| ```bash | ||
| yarn test:integration:react-18 | ||
| yarn test:integration:react-19 | ||
| ``` | ||
|
|
||
| Run for a specific version of react not already defined in `package.json` | ||
|
|
||
| ```bash | ||
| ./integration/run-integration.sh x.x.x | ||
| ``` | ||
|
|
||
| It's recommended that you build your packages using `yarn build:packages` before running this command, to ensure that the test suite references the correctly built packages when running the test suite. | ||
|
|
||
| > [!CAUTION] | ||
| > When running the integration tests, the runner will perform a `yarn install` so that the correct version of react is present. In doing so, it will add the `resolutions` key to the root `package.json` temporarily while the test suite runs. Please make sure that the `resolutions` key is **NEVER** committed to version control. See more details about how this process works below. | ||
|
|
||
| ## Adding new tests | ||
|
|
||
| To add new tests to our integration test suite, navigate to `./integration/tests`. We try to categorize tests into top level grouping so that they're easier to find later. For this test suite, at this point in time, we're only _really_ looking to see if the current package can work in multiple versions of react. So, all that you need to do is make sure the component or function is called in the test and ran. | ||
|
|
||
| Here's an example of adding a component to our test suite. | ||
|
|
||
| ```tsx | ||
| import { NotificationFeed } from "@knocklabs/react"; | ||
| import { render } from "@testing-library/react"; | ||
| import { describe, it } from "vitest"; | ||
|
|
||
| describe("NotificationFeed", () => { | ||
| it("should render", () => { | ||
| render(<NotificationFeed />); | ||
| }); | ||
| }); | ||
| ``` | ||
|
|
||
| ## Explanation of the architecture | ||
|
|
||
| In order to reproduce the most realistic integration test, we need to: | ||
|
|
||
| 1. Build our packages with the react version currently present in the repo. | ||
| 2. Override that react version when testing to see how our code responds in those scenarios, similar to how `peerDependencies` work. | ||
|
|
||
| ### The issue | ||
|
|
||
| Unfortunately, this isn't super straightfoward. In our `yarn` monorepo there is a single version of `react` present. We do this so that there are not multiple versions running at the same time to avoid this error: | ||
|
|
||
| ``` | ||
| A React Element from an older version of React was rendered. This is not supported. It can happen if: | ||
| - Multiple copies of "react" are used | ||
| - A library pre-bundled an old copy of "react" or "react/jsx-runtime" | ||
| - A compiler tries to "inline" JSX instead of using the runtime. | ||
| ``` | ||
|
|
||
| This means that if we want to test specific versions of react in our integration tests, the entire repo will need to resolve to that version. The initial solve would be to add this specific version as the one that is referenced in `@knocklabs/integration`, this won't work. The resolved version of `react` will end up being the version hoisted in the root `node_modules`. If you try to override that by pointing directly to specific version of `react` in the `node_modules` folder via `vitest` alias (or other solution), you will get the above error because the built version and the resolved version will be running at the same time. We could build the packages utilizing the verison we want to test against, but that means in some cases the build would not succeed even though the package would work with a lower version of react. | ||
|
kylemcd marked this conversation as resolved.
Outdated
|
||
|
|
||
| There is no "easy" way around this. | ||
|
|
||
| ### The solve | ||
|
|
||
| Luckily, `yarn` v4 gives us one singular escape hatch, the `resolutions` key in `package.json`. This config will override **EVERY** version of the specificed package throughout the repo, yipee. But the caveat is this value is only configurable in the monorepo's root `package.json` file. So, if we want to test specific `react` versions we'll need to add the `resolutions` key defining those verisons. Here's how our script works. | ||
|
kylemcd marked this conversation as resolved.
Outdated
|
||
|
|
||
| 1. Take in the `react` version that the maintainer wants to test. Any version should be easily testable without any extra configuration. So we take this in as a parameter when running the script, example: `./integration.run-integration.sh 18.2.0`. | ||
| 2. Create a copy of the monorepo's root `package.json` file so that we can restore it back to it's original state after the run. This helps to prevent the maintainer from comitting configuration changes to version control everytime they need to test a different version of `react`. | ||
| 3. Set the `resolutions` key to the specified `react` version passed as a parameter and write it to the `package.json` file. This sets the version for `react` and `react-dom`. | ||
| 4. Run `yarn` so that each instance of `react` points to the specified version. | ||
| 5. Run the test suite from `@knocklabs/integration` via `yarn test:integration:runner` | ||
| 6. Restore the `package.json` file back to it's original state, removing the `resolutions` key entirely. | ||
| 7. Run `yarn` again to restore the dependencies back to their original state. | ||
|
|
||
| Full script exists here: | ||
|
|
||
| ``` | ||
| ./integration/run-integration.sh | ||
| ``` | ||
|
|
||
| Running the test suite in this way gives the maintainer the ability to locally run the test suite while also allowing us to run the same script in CI. This setup is the best we've found without introducing a TON of overhead to our repo. The crux of this issue is how dependency hoisting is managed in a monorepo. Yarn has a [great article](https://classic.yarnpkg.com/blog/2018/02/15/nohoist/) describing this pattern in more detail, note that this is referencing `yarn` v1 under the "How to use it?" section, which is not applicable to us since we use `yarn` v4. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "name": "@knocklabs/integration", | ||
| "private": true, | ||
| "prettier": "@knocklabs/prettier-config", | ||
| "engines": { | ||
| "node": "20.9.0" | ||
| }, | ||
| "scripts": { | ||
| "test:integration": "vitest run" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| set -e # exit on any error | ||
|
|
||
| # Get React version from first argument | ||
| REACT_VERSION=$1 | ||
|
|
||
| if [ -z "$REACT_VERSION" ]; then | ||
| echo "Error: You must provide a React version." | ||
| echo "Usage: $0 <react-version>" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Set the root package.json resolutions to the correct | ||
| # react version so that we can run tests against it. | ||
| JQ_CMD=".resolutions += {} | ||
| | .resolutions[\"react\"] = \"$REACT_VERSION\" | ||
| | .resolutions[\"react-dom\"] = \"$REACT_VERSION\"" | ||
|
|
||
| # Back up original package.json | ||
| cp ./package.json ./original.json | ||
|
|
||
| # Ensure that we always restore the original package.json AND run yarn install on exit | ||
| trap 'echo "Restoring original package.json..."; mv ./original.json ./package.json; echo "Running yarn install to restore lockfile and node_modules..."; yarn install --no-immutable' EXIT | ||
|
|
||
| # Apply modifications | ||
| jq "$JQ_CMD" ./package.json > tmp.json | ||
| mv tmp.json ./package.json | ||
|
|
||
| echo "Starting run for react version $REACT_VERSION" | ||
|
|
||
| # Run commands | ||
| yarn install --no-immutable | ||
| yarn test:integration:runner | ||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { | ||
| KnockFeedProvider, | ||
| KnockProvider, | ||
| NotificationFeed, | ||
| } from "@knocklabs/react"; | ||
| import { render } from "@testing-library/react"; | ||
| import { describe, it } from "vitest"; | ||
|
|
||
| const Feed = () => { | ||
| return ( | ||
| <KnockProvider | ||
| apiKey={process.env.INTEGRATION_KNOCK_PUBLIC_KEY} | ||
| userId={process.env.INTEGRATION_KNOCK_USER_ID} | ||
| > | ||
| <KnockFeedProvider feedId={process.env.INTEGRATION_KNOCK_FEED_ID}> | ||
| <NotificationFeed /> | ||
| </KnockFeedProvider> | ||
| </KnockProvider> | ||
| ); | ||
| }; | ||
|
|
||
| describe("NotificationFeed", () => { | ||
| it("should render", () => { | ||
| render(<Feed />); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "extends": "@knocklabs/typescript-config/node.json", | ||
| "include": ["vitest.config.ts", "tests"], | ||
| "exclude": ["node_modules", "dist"], | ||
| "compilerOptions": { | ||
| "types": ["node", "vite"], | ||
| "incremental": true, | ||
| "jsx": "react-jsx" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import path from "path"; | ||
| import { loadEnv } from "vite"; | ||
| import { defineConfig } from "vitest/config"; | ||
|
|
||
| export default defineConfig(({ mode }) => { | ||
| return { | ||
| test: { | ||
| globals: true, | ||
| environment: "jsdom", | ||
| setupFiles: ["./vitest.setup.ts"], | ||
| env: loadEnv(mode, "./", ""), | ||
| include: ["./tests/**/*.test.tsx"], | ||
| }, | ||
| }; | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import "@testing-library/jest-dom/vitest"; | ||
| import { cleanup } from "@testing-library/react"; | ||
| import { afterEach } from "vitest"; | ||
|
|
||
| afterEach(() => { | ||
| cleanup(); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.