Skip to content

Commit

Permalink
wip: test
Browse files Browse the repository at this point in the history
  • Loading branch information
pcattori committed Feb 11, 2023
1 parent 05b40d6 commit 26b7ecc
Showing 1 changed file with 242 additions and 0 deletions.
242 changes: 242 additions & 0 deletions integration/hmr-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import { test, expect } from "@playwright/test";
import execa from "execa";
import fs from "node:fs";
import path from "node:path";
import type { Readable } from "node:stream";

import { createFixtureProject } from "./helpers/create-fixture";

let port = 3099;

let fixture = {
future: {
unstable_dev: {
appServerPort: port,
},
unstable_tailwind: true,
},
files: {
"package.json": `
{
"private": true,
"sideEffects": false,
"scripts": {
"dev:remix": "NODE_ENV=development node ../../../build/node_modules/@remix-run/dev/dist/cli.js dev",
"dev:app": "NODE_ENV=development nodemon --watch build/ ./server.js"
},
"dependencies": {
"@remix-run/node": "0.0.0-local-version",
"@remix-run/react": "0.0.0-local-version",
"express": "0.0.0-local-version",
"nodemon": "0.0.0-local-version",
"react": "0.0.0-local-version",
"react-dom": "0.0.0-local-version",
"tailwindcss": "0.0.0-local-version"
},
"devDependencies": {
"@remix-run/dev": "0.0.0-local-version",
"@types/react": "0.0.0-local-version",
"@types/react-dom": "0.0.0-local-version",
"typescript": "0.0.0-local-version"
},
"engines": {
"node": ">=14"
}
}
`,
"server.js": `
let path = require("path");
let express = require("express");
let { createRequestHandler } = require("@remix-run/express");
const app = express();
app.use(express.static("public", { immutable: true, maxAge: "1y" }));
const MODE = process.env.NODE_ENV;
const BUILD_DIR = path.join(process.cwd(), "build");
app.all(
"*",
createRequestHandler({
build: require(BUILD_DIR),
mode: MODE,
})
);
let port = ${port};
app.listen(port, () => {
require(BUILD_DIR);
console.log('✅ app ready: http://localhost:' + port);
});
`,
"tailwind.config.js": `
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./app/**/*.{ts,tsx,jsx,js}"],
theme: {
extend: {},
},
plugins: [],
};
`,
"app/tailwind.css": `
@tailwind base;
@tailwind components;
@tailwind utilities;
`,
"app/root.tsx": `
import type { LinksFunction } from "@remix-run/node";
import { Link, Links, Meta, Outlet, Scripts } from "@remix-run/react";
import styles from "./tailwind.css";
export const links: LinksFunction = () => [
{ rel: "stylesheet", href: styles },
];
export default function Root() {
return (
<html lang="en" className="h-full">
<head>
<Meta />
<Links />
</head>
<body className="h-full">
<header>
<input id="root-input" />
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/a">A</Link></li>
<li><Link to="/b">B</Link></li>
</ul>
</nav>
</header>
<Outlet />
<Scripts />
</body>
</html>
);
}
`,
"app/routes/index.tsx": `
export default function Index() {
return (
<main>
<h1>Index</h1>
<label htmlFor="index-input">Index Input</label>
<input id="index-input" />
</main>
)
}
`,
"app/routes/a.tsx": `
export default function A() {
return (
<main>
<h1>A</h1>
<label htmlFor="a-input">A Input</label>
<input id="a-input" />
</main>
)
}
`,
"app/routes/b.tsx": `
export default function B() {
return (
<main>
<h1>B</h1>
<label htmlFor="b-input">B Input</label>
<input id="b-input" />
</main>
)
}
`,
},
};

let sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

let wait = async (
callback: () => boolean,
{ timeoutMs = 1000, intervalMs = 250 } = {}
) => {
let start = Date.now();
while (Date.now() - start <= timeoutMs) {
if (callback()) {
return;
}
await sleep(intervalMs);
}
throw Error(`wait: timeout ${timeoutMs}ms`);
};

let bufferize = (stream: Readable): (() => string) => {
let buffer = "";
stream.on("data", (data) => (buffer += data.toString()));
return () => buffer;
};

test("blah", async ({ page }) => {
let projectDir = await createFixtureProject(fixture);

let dev = execa("npm", ["run", "dev:remix"], { cwd: projectDir });
let devStdout = bufferize(dev.stdout!);
await wait(() => /💿 Built in /.test(devStdout()), { timeoutMs: 3000 });

let app = execa("npm", ["run", "dev:app"], { cwd: projectDir });
let appStdout = bufferize(app.stdout!);
await wait(() => /✅ app ready: /.test(appStdout()));

try {
await page.goto(`http://localhost:${port}`);

let input = page.getByLabel("Index Input");
expect(input).toBeVisible();
await input.type("asdfasdf");

let newIndex = `
export default function Index() {
return (
<main>
<h1 className="bg-red-500">Changed</h1>
<label htmlFor="index-input">Persisted Input</label>
<input id="index-input" />
</main>
)
}
`;
fs.writeFileSync(
path.join(projectDir, "app", "routes", "index.tsx"),
newIndex
);

await page.getByText("Changed").waitFor({ timeout: 1000 });
let input2 = page.getByLabel("Persisted Input");
expect(await input2.inputValue()).toBe("asdfasdf");
} finally {
dev.kill();
app.kill();
}

// expect home page
// setup stateful thing (e.g. text input)
// edit css
// expect style changes
// expect state remains
// edit markup
// expect markup changes
// expect state remains
// edit loader
// expect loader changes in markup
// expect state remains
});

// TEST undo flow
// 1. Go to route A
// 2. Go to route B
// 3. Modify code for route A
// 4. Navigate to route A
// 5. Nav to route B
// 6. Undo changes from (3)
// 7. Nav to route A

0 comments on commit 26b7ecc

Please sign in to comment.