UNPKG

jest-preview

Version:

Preview your Jest tests in a browser

197 lines (193 loc) 6.16 kB
#!/usr/bin/env node 'use strict'; var http = require('http'); var path = require('path'); var fs = require('fs'); var connect = require('connect'); var sirv = require('sirv'); var chokidar = require('chokidar'); var ws = require('ws'); var child_process = require('child_process'); var open = require('open'); const OSX_CHROME = "google chrome"; function openBrowser(url) { let browser = process.env.BROWSER; if (browser === "none") { return false; } const shouldTryOpenChromeWithAppleScript = process.platform === "darwin" && (typeof browser !== "string" || browser === OSX_CHROME); if (shouldTryOpenChromeWithAppleScript) { const supportedChromiumBrowsers = [ "Google Chrome Canary", "Google Chrome Dev", "Google Chrome Beta", "Google Chrome", "Microsoft Edge", "Brave Browser", "Vivaldi", "Chromium" ]; for (const chromiumBrowser of supportedChromiumBrowsers) { try { child_process.execSync(`ps cax | grep "${chromiumBrowser}"`); child_process.execSync( `osascript openChrome.applescript "${encodeURI( url )}" "${chromiumBrowser}"`, { cwd: __dirname, stdio: "ignore" } ); return true; } catch (err) { } } } if (process.platform === "darwin" && browser === "open") { browser = void 0; } try { const options = { app: { name: browser } }; open(url, options).catch(() => { }); return true; } catch (err) { return false; } } const app = connect(); const port = process.env.PORT || 3336; const wsPort = Number(port) + 1; const CACHE_DIRECTORY = "./node_modules/.cache/jest-preview"; const INDEX_BASENAME = "index.html"; const INDEX_PATH = path.join(CACHE_DIRECTORY, INDEX_BASENAME); const PUBLIC_CONFIG_BASENAME = "cache-public.config"; const PUBLIC_CONFIG_PATH = path.join(CACHE_DIRECTORY, PUBLIC_CONFIG_BASENAME); const FAV_ICON_PATH = "./node_modules/jest-preview/dist/cli/favicon.ico"; let publicFolder = "public"; if (fs.existsSync(PUBLIC_CONFIG_PATH)) { publicFolder = fs.readFileSync(PUBLIC_CONFIG_PATH, "utf8").trim(); } if (fs.existsSync(INDEX_PATH)) { const files = fs.readdirSync(CACHE_DIRECTORY); files.forEach((file) => { if (!file.startsWith("cache-")) { fs.unlinkSync(path.join(CACHE_DIRECTORY, file)); } }); } else { fs.mkdirSync(CACHE_DIRECTORY, { recursive: true }); } const defaultIndexHtml = `<!DOCTYPE html> <html> <head> <link rel="shortcut icon" href="${FAV_ICON_PATH}"> <title>Jest Preview Dashboard</title> </head> <body> No preview found.<br/> Please add following lines to your test: <br /> <br /> <div style="background-color: grey;width: fit-content;padding: 8px;"> <code> import { debug } from 'jest-preview'; <br /> <br /> // Inside your tests <br /> debug(); </code> </div> <br /> Then rerun your tests. <br /> See an example in the <a href="https://www.jest-preview.com/docs/getting-started/usage#3-preview-your-html-from-jest-following-code-demo-how-to-use-it-with-react-testing-library" target="_blank" rel="noopener noreferrer">documentation</a> </body> </html>`; fs.writeFileSync(INDEX_PATH, defaultIndexHtml); const wss = new ws.WebSocketServer({ port: wsPort }); wss.on("connection", function connection(ws) { ws.on("message", function message(data) { console.log("received: %s", data); try { const dataJSON = JSON.parse(data); if (dataJSON.type === "publicFolder") { publicFolder = dataJSON.payload; } } catch (error) { console.error(error); } }); }); const watcher = chokidar.watch([INDEX_PATH, PUBLIC_CONFIG_PATH], { // ignored: ['**/node_modules/**', '**/.git/**'], ignoreInitial: true, ignorePermissionErrors: true, disableGlobbing: true }); function handleFileChange(filePath) { const basename = path.basename(filePath); if (basename === INDEX_BASENAME) { wss.clients.forEach((client) => { if (client.readyState === 1) { client.send(JSON.stringify({ type: "reload" })); } }); } if (basename === PUBLIC_CONFIG_BASENAME) { publicFolder = fs.readFileSync(PUBLIC_CONFIG_PATH, "utf8").trim(); } } watcher.on("change", handleFileChange).on("add", handleFileChange).on("unlink", handleFileChange); function injectToString(string, word, injectWord) { const breakPosition = string.indexOf(word) + word.length; return string.slice(0, breakPosition) + injectWord + string.slice(breakPosition); } function injectToHead(html, content) { return injectToString(html, "<head>", content); } app.use((req, res, next) => { const serve = sirv(".", { dev: true, etag: true }); if (req.url === "/") { return next(); } const filePath = path.join(".", req.url); if (!fs.existsSync(filePath)) { const newPath = path.join(publicFolder, req.url); if (fs.existsSync(newPath)) { req.url = newPath; } else { console.log("[WARN] File not found: ", req.url); console.log(`[WARN] Please check if ${req.url} is existed.`); console.log( `[WARN] If it is existed, likely you forget to setup the code transformation, or you haven't flushed the old cache yet. Try to run "./node_modules/.bin/jest --clearCache" to clear the cache. ` ); } } serve(req, res, next); }); app.use("/", (req, res) => { const reloadScriptContent = fs.readFileSync(path.join(__dirname, "./ws-client.js"), "utf-8").replace(/\$PORT/g, `${wsPort}`); let indexHtml = fs.readFileSync(INDEX_PATH, "utf-8"); indexHtml += `<script>${reloadScriptContent}<\/script>`; indexHtml = injectToHead( indexHtml, `<link rel="shortcut icon" href="${FAV_ICON_PATH}"> <title>Jest Preview Dashboard</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover">` ); res.setHeader("Content-Type", "text/html"); res.end(indexHtml); }); const server = http.createServer(app); server.listen(port, () => { console.log(`Jest Preview Server listening on port ${port}`); openBrowser(`http://localhost:${port}`); });