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

0.9.0 (4) - Add integration test for vitejs + streaming #198

Merged
merged 55 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
9eaac4f
rename wrapped classes to original name, also export in RSC
phryneas Feb 21, 2024
f1535a6
fix up test
phryneas Feb 21, 2024
10817a5
fixup build types
phryneas Feb 21, 2024
ad9022a
comment shape
phryneas Feb 21, 2024
abe6c96
adjust type resolution
phryneas Feb 21, 2024
43c1ef2
fixup
phryneas Feb 21, 2024
3d61cc1
adjust package shapes
phryneas Feb 27, 2024
835f780
bundling adjustment: no `/index.js` in CJS
phryneas Feb 28, 2024
41a9ea7
check "package shapes"
phryneas Feb 28, 2024
e0919aa
revert changes to examples/integration tests
phryneas Feb 28, 2024
b330f7c
reset README changes
phryneas Feb 28, 2024
19a4a4f
add description to helper scripts
phryneas Feb 28, 2024
1b3d70e
Merge branch 'main' into pr/unify-exports
phryneas Feb 28, 2024
2ae052b
wrap hook functionality from ApolloClient instance
phryneas Feb 23, 2024
ac239d9
move wrappers to `QueryManager`
phryneas Feb 23, 2024
12b6ff3
update version
phryneas Feb 27, 2024
7b6da7e
remove hook exports from `@apollo/client-react-streaming`
phryneas Feb 28, 2024
9fe9582
run test-bundle in CI
phryneas Feb 28, 2024
b81176c
Merge branch 'pr/unify-exports' into pr/wrapHooks-2
phryneas Feb 28, 2024
2d7c8d9
drop hook exports
phryneas Feb 28, 2024
9410422
move "integration-test" into "integration-test/nextjs"
phryneas Feb 14, 2024
b523520
initialize basic vite react-ssr project
phryneas Feb 14, 2024
fbbd152
apply prettier
phryneas Feb 14, 2024
0c0b47b
[WIP] try to set up a streaming server
phryneas Feb 16, 2024
70b970b
fix hydration, force faster flush
phryneas Feb 16, 2024
91beaa2
experimental-react demo working
phryneas Feb 16, 2024
a66a502
add delay
phryneas Feb 16, 2024
1db1623
also works with prod build
phryneas Feb 16, 2024
c62ec33
update lockfile
phryneas Feb 16, 2024
7f44046
global react version
phryneas Feb 20, 2024
ef3d2ef
simplify a bit
phryneas Feb 20, 2024
b0c0f78
move experimental detail out to `WrappedApolloProvider`
phryneas Feb 20, 2024
66af26f
adjustments
phryneas Feb 28, 2024
101958c
remove `@apollo/client-react-streaming/experimental-react-transport`
phryneas Feb 28, 2024
85c32a9
alternative installation
phryneas Feb 28, 2024
8f6b8d5
alternative approach
phryneas Feb 29, 2024
6fe73df
fix non-failing tests
phryneas Feb 29, 2024
8f36c3f
fix bug that prevented rerunning hydrated queries if initialized too …
phryneas Feb 29, 2024
131199b
adjust script
phryneas Feb 29, 2024
48ceaad
more packaging foo
phryneas Feb 29, 2024
f79953a
fixup app rendering
phryneas Feb 29, 2024
c6196aa
playwright test
phryneas Feb 29, 2024
a5d525f
split up
phryneas Feb 29, 2024
5d93d03
copy experimental to vite-streaming
phryneas Feb 20, 2024
0824db5
adjust types
phryneas Feb 20, 2024
09047aa
remove `Writable` from experimental example
phryneas Feb 20, 2024
0209bf9
vite-streaming
phryneas Feb 20, 2024
e97339c
remove insertions after last flush
phryneas Feb 20, 2024
39c90fb
add pipeTrough
phryneas Feb 20, 2024
26440fc
learnings from experimental-react
phryneas Feb 29, 2024
586d156
add steps to workflow
phryneas Feb 29, 2024
97a2452
build only libraries in test preparation
phryneas Feb 29, 2024
ae76923
review feedback
phryneas Mar 6, 2024
8ef94cc
Merge remote-tracking branch 'origin/main' into pr/integration-tests
phryneas Mar 6, 2024
7909434
small fixup
phryneas Mar 6, 2024
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
7 changes: 6 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
- run: yarn workspaces foreach --all --include "@integration-test/*" add @apollo/client@${{ matrix.version }}

- name: Build libraries
run: yarn workspaces foreach --all -t --include "@apollo/*" --include @integration-test/nextjs run build
run: yarn workspaces foreach --all -t --include "@apollo/*" run build

- name: "Next.js: Build"
run: yarn workspace @integration-test/nextjs run build
Expand All @@ -94,3 +94,8 @@ jobs:
run: yarn workspace @integration-test/experimental-react run build
- name: "Experimentally patched React: Test"
run: yarn workspace @integration-test/experimental-react run test | tee $GITHUB_STEP_SUMMARY; exit ${PIPESTATUS[0]}

- name: "Vite Streaming: Build"
run: yarn workspace @integration-test/vite-streaming run build
- name: "Vite Streaming: Test"
run: yarn workspace @integration-test/vite-streaming run test | tee $GITHUB_STEP_SUMMARY; exit ${PIPESTATUS[0]}
14 changes: 14 additions & 0 deletions integration-test/vite-streaming/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<!--app-head-->
</head>

<body>
<div id="root"><!--app-html--></div>
<script type="module" src="/src/entry-client.jsx"></script>
</body>

</html>
37 changes: 37 additions & 0 deletions integration-test/vite-streaming/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@integration-test/vite-streaming",
"private": true,
"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",
"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",
"build-and-test": "yarn build && yarn test",
"test": "yarn playwright test"
},
"dependencies": {
"@apollo/client": "^3.9.1",
"@apollo/client-react-streaming": "workspace:*",
"compression": "^1.7.4",
"express": "^4.18.2",
"graphql": "^16.8.1",
"graphql-tag": "^2.12.6",
"react": "18.3.0-canary-60a927d04-20240113",
"react-dom": "18.3.0-canary-60a927d04-20240113",
"sirv": "^2.0.4"
},
"devDependencies": {
"@playwright/test": "^1.39.0",
"@tsconfig/vite-react": "^3.0.0",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"cross-env": "^7.0.3",
"prettier": "^3.2.5",
"vite": "^5.0.10"
}
}
21 changes: 21 additions & 0 deletions integration-test/vite-streaming/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineConfig } from "@playwright/test";

export default defineConfig({
webServer: {
command: "node server",
port: 3000,
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
env: {
PORT: 3000,
NODE_ENV: "production",
},
},
timeout: 120 * 1000,
use: {
headless: true,
viewport: { width: 1280, height: 720 },
ignoreHTTPSErrors: true,
},
testDir: "src/",
});
101 changes: 101 additions & 0 deletions integration-test/vite-streaming/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import express from "express";
import { renderToReadableStream } from "react-dom/server.edge";
import { readFile } from "node:fs/promises";

// Constants
const isProduction = process.env.NODE_ENV === "production";
const port = process.env.PORT || 5173;
const base = process.env.BASE || "/";

// Create http server
const app = express();

// Add Vite or respective production middlewares
let vite;
let bootstrapModules = [];
let assets = [];
if (!isProduction) {
const { createServer } = await import("vite");
vite = await createServer({
server: { middlewareMode: true, hmr: true },
appType: "custom",
base,
});
app.use(vite.middlewares);
} else {
const compression = (await import("compression")).default;
const sirv = (await import("sirv")).default;
app.use(compression());
app.use(base, sirv("./dist/client", { extensions: [] }));
const index = await readFile("./dist/client/index.html", "utf-8");
for (const script of index.matchAll(
/<script type="module" \w+ src="(.*)">/g
)) {
bootstrapModules.push(script[1]);
}
for (const link of index.matchAll(
/<link rel="stylesheet" \w+ href="(.*)">/g
)) {
assets.push(link[1]);
}
}

console.log({
bootstrapModules,
assets,
});

app.use("*", async (req, res) => {
// The new wiring is a bit more involved.
res.socket.on("error", (error) => {
console.error("Fatal", error);
});

const { createTransport, render } =
/** @type {import('./src/entry-server.jsx.js')}*/ (
await (isProduction
? import("./dist/server/entry-server.js")
: vite.ssrLoadModule("/src/entry-server.jsx"))
);

const { injectIntoStream, transformStream } = createTransport();

const App = render({
isProduction,
assets,
injectIntoStream,
});

const reactStream =
/** @type {ReadableStream & {allReady: Promise<void>}} */ (
await renderToReadableStream(App, {
bootstrapModules,
})
);

await pipeReaderToResponse(
reactStream.pipeThrough(transformStream).getReader(),
res
);
});

async function pipeReaderToResponse(reader, res) {
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
res.end();
return;
} else {
res.write(value);
}
}
} catch (e) {
res.destroy(e);
}
}

// Start http server
app.listen(port, () => {
console.log(`Server started at http://localhost:${port}`);
});
81 changes: 81 additions & 0 deletions integration-test/vite-streaming/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;

font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}

a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}

body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}

h1 {
font-size: 3.2em;
line-height: 1.1;
}

button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}


#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.card {
padding: 2em;
}
90 changes: 90 additions & 0 deletions integration-test/vite-streaming/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Suspense, useState } from "react";
import "./App.css";
import { ApolloClient, InMemoryCache } from "@apollo/client-react-streaming";
import { SchemaLink } from "@apollo/client/link/schema/index.js";
import {
gql,
ApolloLink,
Observable,
TypedDocumentNode,
useSuspenseQuery,
} from "@apollo/client/index.js";
import { schema } from "./schema";
import { WrappedApolloProvider } from "./Transport";

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

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

const makeClient = () => {
return new ApolloClient({
cache: new InMemoryCache(),
link:
// we do not even have a graphql endpoint in the browser, so if this works, streaming works
typeof window === "undefined"
? delayLink.concat(new SchemaLink({ schema }))
: undefined,
});
};

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

const QUERY: TypedDocumentNode<{
products: Array<{ id: string; title: string }>;
}> = 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;
Loading
Loading