Skip to content

Commit

Permalink
0.9.0 (6) - Add integration test for vitest (#221)
Browse files Browse the repository at this point in the history
* vitest setup & tests

* adjust `package.json` files

* fix up broken merge
  • Loading branch information
phryneas authored Mar 6, 2024
1 parent 8b02705 commit 72d81a7
Show file tree
Hide file tree
Showing 12 changed files with 779 additions and 20 deletions.
7 changes: 3 additions & 4 deletions integration-test/experimental-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"prepare": "rm -rf node_modules/@apollo/client-react-streaming; mkdir node_modules/@apollo/client-react-streaming && cp -r ../../packages/client-react-streaming/{package.json,dist} node_modules/@apollo/client-react-streaming",
"dev": "yarn prepare; node server",
"build": "yarn prepare; npm run build:client && npm run build:server",
"dev": "node server",
"build": "npm run build:client && npm run build:server",
"build:client": "vite build --ssrManifest --outDir dist/client",
"build:server": "vite build --ssr src/entry-server.jsx --outDir dist/server",
"preview": "cross-env NODE_ENV=production node server",
Expand All @@ -15,7 +14,7 @@
},
"dependencies": {
"@apollo/client": "^3.9.1",
"@apollo/client-react-streaming": "workspace:*",
"@apollo/client-react-streaming": "*",
"compression": "^1.7.4",
"express": "^4.18.2",
"graphql": "^16.8.1",
Expand Down
7 changes: 4 additions & 3 deletions integration-test/jest/package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
{
"name": "@integration-test/jest",
"scripts": {
"prepare": "rm -rf node_modules/@apollo/client-react-streaming node_modules/@apollo/experimental-nextjs-app-support; mkdir -p node_modules/@apollo/client-react-streaming node_modules/@apollo/experimental-nextjs-app-support && cp -r ../../packages/client-react-streaming/{package.json,dist} node_modules/@apollo/client-react-streaming && cp -r ../../packages/experimental-nextjs-app-support/{package.json,dist} node_modules/@apollo/experimental-nextjs-app-support",
"test": "yarn prepare; jest"
"test": "jest"
},
"dependencies": {
"@apollo/client": "^3.9.5",
"@apollo/client-react-streaming": "workspace:*",
"@apollo/experimental-nextjs-app-support": "workspace:*",
"@graphql-tools/schema": "^10.0.3",
"graphql-tag": "^2.12.6"
"graphql-tag": "^2.12.6",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@babel/core": "^7.24.0",
Expand Down
7 changes: 3 additions & 4 deletions integration-test/vite-streaming/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"prepare": "rm -rf node_modules/@apollo/client-react-streaming; mkdir node_modules/@apollo/client-react-streaming && cp -r ../../packages/client-react-streaming/{package.json,dist} node_modules/@apollo/client-react-streaming",
"dev": "yarn prepare; node server",
"build": "yarn prepare; npm run build:client && npm run build:server",
"dev": "node server",
"build": "npm run build:client && npm run build:server",
"build:client": "vite build --ssrManifest --outDir dist/client",
"build:server": "vite build --ssr src/entry-server.jsx --outDir dist/server",
"preview": "cross-env NODE_ENV=production node server",
Expand All @@ -15,7 +14,7 @@
},
"dependencies": {
"@apollo/client": "^3.9.1",
"@apollo/client-react-streaming": "workspace:*",
"@apollo/client-react-streaming": "*",
"compression": "^1.7.4",
"express": "^4.18.2",
"graphql": "^16.8.1",
Expand Down
20 changes: 20 additions & 0 deletions integration-test/vitest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@integration-test/vitest",
"scripts": {
"test": "vitest"
},
"dependencies": {
"@apollo/client": "^3.9.1",
"@apollo/experimental-nextjs-app-support": "*",
"@graphql-tools/schema": "^10.0.3",
"graphql-tag": "^2.12.6",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"@vitejs/plugin-react": "^4.2.1",
"vitest": "^1.3.1"
}
}
9 changes: 9 additions & 0 deletions integration-test/vitest/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { expect, afterEach } from "vitest";
import { cleanup } from "@testing-library/react";
import * as matchers from "@testing-library/jest-dom/matchers";

expect.extend(matchers);

afterEach(() => {
cleanup();
});
81 changes: 81 additions & 0 deletions integration-test/vitest/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Suspense, useState } from "react";
import {
ApolloNextAppProvider,
NextSSRApolloClient,
NextSSRInMemoryCache,
useSuspenseQuery,
} from "@apollo/experimental-nextjs-app-support/ssr";
import { SchemaLink } from "@apollo/client/link/schema/index.js";
import { gql, ApolloLink, Observable } from "@apollo/client/index.js";
import { schema } from "./schema";

const delayLink = new ApolloLink((operation, forward) => {
return new Observable((observer) => {
const handle = setTimeout(() => {
forward(operation).subscribe(observer);
}, 20);

return () => {
clearTimeout(handle);
};
});
});

export const makeClient = () => {
return new NextSSRApolloClient({
cache: new NextSSRInMemoryCache(),
link: delayLink.concat(new SchemaLink({ schema })),
});
};

function App() {
return (
<>
<h1>Vite + React (patched) Streaming SSR + Apollo Client + Suspense</h1>
<div className="card">
<ApolloNextAppProvider makeClient={makeClient}>
<Suspense fallback={<div>Loading...</div>}>
<Countries />
<Counter />
</Suspense>
</ApolloNextAppProvider>
</div>
</>
);
}

export const QUERY = gql`
query {
products {
id
title
}
}
`;

function Countries() {
const { data } = useSuspenseQuery(QUERY);

return (
<ul>
{data.products.map((product) => (
<li key={product.id}>{product.title}</li>
))}
</ul>
);
}

/**
* Counter components to test that the client has hydrated and is interactive.
*/
function Counter() {
const [counter, setCounter] = useState(0);
return (
<>
<div data-testid="counter">{counter}</div>
<button onClick={() => setCounter((x) => x + 1)}>increment</button>
</>
);
}

export default App;
28 changes: 28 additions & 0 deletions integration-test/vitest/src/App.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import App from "./App";
import { resetNextSSRApolloSingletons } from "@apollo/experimental-nextjs-app-support/ssr";

afterEach(resetNextSSRApolloSingletons);

test("loads data", async () => {
render(<App />);

expect(screen.getByText("Loading...")).toBeInTheDocument();
expect(
await screen.findByText("Soft Warm Apollo Beanie")
).toBeInTheDocument();
expect(screen.queryByText("Loading...")).not.toBeInTheDocument();
});

test("is interactive", async () => {
render(<App />);

const counter = await screen.findByTestId("counter");
expect(counter.textContent).toBe("0");

await userEvent.click(screen.getByText("increment"));

expect(counter.textContent).toBe("1");
});
66 changes: 66 additions & 0 deletions integration-test/vitest/src/hooks.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";

import { makeClient, QUERY } from "./App";
import {
ApolloNextAppProvider,
NextSSRApolloClient,
useQuery,
resetNextSSRApolloSingletons,
} from "@apollo/experimental-nextjs-app-support/ssr";
import { Suspense } from "react";

const wrapper = ({ children }) => (
<ApolloNextAppProvider makeClient={makeClient}>
<Suspense fallback={<div>Loading...</div>}>{children}</Suspense>
</ApolloNextAppProvider>
);

afterEach(resetNextSSRApolloSingletons);

/**
* We test that jest is using the "browser" build.
* This is important because the "browser" build will not try to transport
* data from the server to the browser.
*/
test("uses the browser build", () => {
expect(NextSSRApolloClient.name).toBe("ApolloClientBrowserImpl");
});

/**
* The SSR build would just skip all `useQuery` calls because their result
* would never be able to be transported to the browser anyways.
* In a SSR build, it would always render loading.
*/
test("`useQuery` renders", async () => {
const Component = () => {
const { data, loading } = useQuery(QUERY);
return loading ? (
<div>Loading...</div>
) : (
<div>{data.products[0].title}</div>
);
};
render(<Component />, { wrapper });

expect(screen.getByText("Loading...")).toBeInTheDocument();
expect(
await screen.findByText("Soft Warm Apollo Beanie")
).toBeInTheDocument();
expect(screen.queryByText("Loading...")).not.toBeInTheDocument();
});

test("will set up the data transport", () => {
render(<></>, { wrapper });
expect(globalThis[Symbol.for("ApolloSSRDataTransport")]).toBeDefined();
expect(globalThis[Symbol.for("ApolloClientSingleton")]).toBeDefined();
});

test("resetNextSSRApolloSingletons tears down global singletons", () => {
render(<></>, { wrapper });
// wrappers are now set up, see last test
// usually, we do this in `afterEach`
resetNextSSRApolloSingletons();
expect(globalThis[Symbol.for("ApolloSSRDataTransport")]).not.toBeDefined();
expect(globalThis[Symbol.for("ApolloClientSingleton")]).not.toBeDefined();
});
48 changes: 48 additions & 0 deletions integration-test/vitest/src/schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { makeExecutableSchema } from "@graphql-tools/schema";
import gql from "graphql-tag";

const typeDefs = gql`
type Product {
id: String!
title: String!
}
type Query {
products: [Product!]!
}
`;

const resolvers = {
Query: {
products: async () => [
{
id: "product:5",
title: "Soft Warm Apollo Beanie",
},
{
id: "product:2",
title: "Stainless Steel Water Bottle",
},
{
id: "product:3",
title: "Athletic Baseball Cap",
},
{
id: "product:4",
title: "Baby Onesies",
},
{
id: "product:1",
title: "The Apollo T-Shirt",
},
{
id: "product:6",
title: "The Apollo Socks",
},
],
},
};

export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
25 changes: 25 additions & 0 deletions integration-test/vitest/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
plugins: [react()],
resolve: {
// this line is important so the "browser build" of dependencies is used
// and not the "SSR build", which would contain "streaming-to-the-browser"
// specific code
conditions: ["browser"],
},
test: {
globals: true,
environment: "jsdom",
setupFiles: "./setup.js",
server: {
// this is important so that the `graphql` dependency is inlined by vitest,
// which seems to get around the "dual package hazard" with ESM/CJS
// at least in this specific setup
deps: {
inline: true,
},
},
},
});
Loading

0 comments on commit 72d81a7

Please sign in to comment.