Last active
May 15, 2022 22:34
-
-
Save sergiodxa/891a3cd1736c1b09c9a0b5fd106d604e to your computer and use it in GitHub Desktop.
An E2E helper for Vitest and Puppeteer to test Remix app
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
import { test, expect, describe, beforeAll, afterAll } from "vitest"; | |
import "pptr-testing-library/extend"; | |
import { type App, start } from "test/helpers/app"; | |
import { loader } from "./articles"; | |
import { logger } from "~/services/logger.server"; | |
import type { PrismaClient } from "@prisma/client"; | |
import { createDatabaseClient } from "test/helpers/db"; | |
describe("E2E", () => { | |
let app: App; | |
beforeAll(async () => { | |
app = await start(); | |
}); | |
afterAll(async () => { | |
await app.stop(); | |
}); | |
test("Articles page should render list of articles", async () => { | |
let document = await app.navigate("/articles"); | |
let $h1 = await document.findByRole("heading", { | |
name: "Articles", | |
level: 1, | |
}); | |
expect(await $h1.getNodeText()).toBe("Articles"); | |
}); | |
}); | |
describe("Loader", () => { | |
let db: PrismaClient; | |
beforeAll(async () => { | |
db = await createDatabaseClient(); | |
await db.$connect(); | |
}); | |
afterAll(async () => { | |
await db.$disconnect(); | |
}); | |
test("The loader should have an articles key", async () => { | |
let response = await loader({ | |
request: new Request("/articles"), | |
params: {}, | |
context: { logger, db }, | |
}); | |
let data = await response.json(); | |
expect(data).toHaveProperty("articles"); | |
expect(data.articles).toBeInstanceOf(Array); | |
}); | |
}); |
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
import "pptr-testing-library/extend"; | |
import getPort from "get-port"; | |
import { execa } from "execa"; | |
import puppeteer from "puppeteer"; | |
import { type DATABASE_URL, generateDatabaseUrl, migrateDatabase } from "./db"; | |
import { clear } from "winston"; | |
export type Process = { | |
stop(): Promise<void>; | |
port: number; | |
}; | |
export type App = { | |
navigate(path: string): Promise<puppeteer.ElementHandle<Element>>; | |
stop(): Promise<void>; | |
browser: puppeteer.Browser; | |
page: puppeteer.Page; | |
}; | |
function clearBuild() { | |
return Promise.all([ | |
execa("rm", ["-rf", "server/build"]), | |
execa("rm", ["-rf", "public/build"]), | |
]); | |
} | |
function buildApp() { | |
return execa("npm", ["run", "build"]); | |
} | |
async function prepareBuild() { | |
await clearBuild(); | |
await buildApp(); | |
} | |
async function prepareDatabase() { | |
let databaseUrl = generateDatabaseUrl(); | |
await migrateDatabase(databaseUrl); | |
return databaseUrl; | |
} | |
async function startProcess({ databaseUrl }: { databaseUrl: DATABASE_URL }) { | |
let port = await getPort(); | |
let server = execa("npm", ["start"], { | |
env: { | |
CI: "true", | |
NODE_ENV: "test", | |
PORT: port.toString(), | |
BASE_URL: `http://localhost:${port}`, | |
DATABASE_URL: databaseUrl, | |
}, | |
}); | |
return await new Promise<Process>(async (resolve, reject) => { | |
server.catch((error) => reject(error)); | |
if (server.stdout === null) return reject("Failed to start server."); | |
server.stdout.on("data", (stream: Buffer) => { | |
if (stream.toString().includes(port.toString())) { | |
return resolve({ | |
async stop() { | |
if (server.killed) return; | |
server.cancel(); | |
}, | |
port, | |
}); | |
} | |
}); | |
}); | |
} | |
function openBrowser() { | |
return puppeteer.launch(); | |
} | |
function openPage(browser: puppeteer.Browser) { | |
return browser.newPage(); | |
} | |
export async function start(): Promise<App> { | |
let [databaseUrl] = await Promise.all([prepareDatabase(), prepareBuild]); | |
let { port, stop } = await startProcess({ databaseUrl }); | |
let browser = await openBrowser(); | |
let page = await openPage(browser); | |
return { | |
browser, | |
page, | |
async navigate(path: string) { | |
let url = new URL(path, `http://localhost:${port}/`); | |
await page.goto(url.toString()); | |
return await page.getDocument(); | |
}, | |
async stop() { | |
await stop(); | |
await browser.close(); | |
await clearBuild(); | |
}, | |
}; | |
} |
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
import { PrismaClient } from "@prisma/client"; | |
import { execa } from "execa"; | |
import { randomUUID } from "node:crypto"; | |
declare const helperDb: unique symbol; | |
export type DATABASE_URL = string & { [helperDb]: true }; | |
const DATABASE_URL_FORMAT = "file:./test/{{uuid}}.db"; | |
export function generateDatabaseUrl() { | |
let uuid = randomUUID(); | |
return DATABASE_URL_FORMAT.replace("{{uuid}}", uuid) as DATABASE_URL; | |
} | |
export function migrateDatabase(url: DATABASE_URL) { | |
return execa("npx", ["prisma", "migrate", "dev"], { | |
env: { | |
NODE_ENV: "test", | |
DATABASE_URL: url, | |
}, | |
}); | |
} | |
export async function createDatabaseClient() { | |
let url = generateDatabaseUrl(); | |
await migrateDatabase(url); | |
return new PrismaClient({ | |
datasources: { db: { url } }, | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment