Skip to main content
Module

x/fresh/tests/cli_test.ts

The next-gen web framework.
Extremely Popular
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
import * as path from "$std/path/mod.ts";import { DenoConfig } from "$fresh/server.ts";import { JSONC, Status } from "../src/server/deps.ts";import { assert, assertEquals, assertMatch, assertNotMatch, assertStringIncludes, delay, puppeteer, retry,} from "./deps.ts";import { clickWhenListenerReady, startFreshServer, waitForText,} from "./test_utils.ts";
const assertFileExistence = async (files: string[], dirname: string) => { for (const filePath of files) { const parts = filePath.split("/").slice(1);
const osFilePath = path.join(dirname, ...parts); const stat = await Deno.stat(osFilePath); assert(stat.isFile, `Could not find file ${osFilePath}`); }};
Deno.test({ name: "fresh-init asdf", async fn(t) { // Preparation const tmpDirName = await Deno.makeTempDir();
await t.step("execute init command", async () => { const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", "init.ts", tmpDirName, ], stdin: "null", stdout: "null", }); const { code } = await cliProcess.output(); assertEquals(code, 0); });
const files = [ `/README.md`, `/.gitignore`, `/deno.json`, `/fresh.gen.ts`, `/components/Button.tsx`, `/islands/Counter.tsx`, `/main.ts`, `/routes/greet/[name].tsx`, `/routes/api/joke.ts`, `/routes/_app.tsx`, `/routes/index.tsx`, `/static/logo.svg`, ];
await t.step("check generated files", async () => { await assertFileExistence(files, tmpDirName); });
await t.step("check project", async () => { const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "task", "check", ], cwd: tmpDirName, stdin: "null", stdout: "piped", stderr: "piped", }); const { code } = await cliProcess.output(); assertEquals(code, 0); });
await t.step("check deno.json", async () => { const configPath = path.join(tmpDirName, "deno.json"); const json = JSON.parse(await Deno.readTextFile(configPath));
assert(json.tasks.start, "Missing 'start' task"); assert(json.tasks.build, "Missing 'build' task"); assert(json.tasks.preview, "Missing 'preview' task"); });
await t.step("start up the server and access the root page", async () => { const { serverProcess, lines, address } = await startFreshServer({ args: ["run", "-A", "--check", "main.ts"], cwd: tmpDirName, });
await delay(100);
// Access the root page const res = await fetch(address); await res.body?.cancel(); assertEquals(res.status, Status.OK);
// verify the island is revived. const browser = await puppeteer.launch({ args: ["--no-sandbox"], }); const page = await browser.newPage(); await page.goto(address, { waitUntil: "networkidle2" }); const counter = await page.$("body > div > div > div > p"); let counterValue = await counter?.evaluate((el) => el.textContent); assert(counterValue === "3");
await clickWhenListenerReady( page, "body > div > div > div > button:nth-child(3)", );
await waitForText(page, "body > div > div > div > p", "4");
counterValue = await counter?.evaluate((el) => el.textContent); assert(counterValue === "4"); await page.close(); await browser.close();
await lines.cancel(); serverProcess.kill("SIGTERM"); await serverProcess.status; });
await retry(() => Deno.remove(tmpDirName, { recursive: true })); }, sanitizeResources: false,});
Deno.test({ name: "fresh-init --twind --vscode", async fn(t) { // Preparation const tmpDirName = await Deno.makeTempDir();
await t.step("execute init command", async () => { const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", "init.ts", tmpDirName, "--twind", "--vscode", ], stdin: "null", stdout: "null", }); const { code } = await cliProcess.output(); assertEquals(code, 0); });
const files = [ "/README.md", "/fresh.gen.ts", "/twind.config.ts", "/components/Button.tsx", "/islands/Counter.tsx", "/main.ts", "/routes/greet/[name].tsx", "/routes/api/joke.ts", "/routes/_app.tsx", "/routes/index.tsx", "/static/logo.svg", "/.vscode/settings.json", "/.vscode/extensions.json", "/.gitignore", ];
await t.step("check generated files", async () => { await assertFileExistence(files, tmpDirName); });
await t.step("start up the server and access the root page", async () => { const { serverProcess, lines, address } = await startFreshServer({ args: ["run", "-A", "--check", "main.ts"], cwd: tmpDirName, });
await delay(100);
// Access the root page const res = await fetch(address); await res.body?.cancel(); assertEquals(res.status, Status.OK);
// verify the island is revived. const browser = await puppeteer.launch({ args: ["--no-sandbox"] }); const page = await browser.newPage(); await page.goto(address, { waitUntil: "networkidle2" });
const counter = await page.$("body > div > div > div > p"); let counterValue = await counter?.evaluate((el) => el.textContent); assertEquals(counterValue, "3");
const fontWeight = await counter?.evaluate((el) => getComputedStyle(el).fontWeight ); assertEquals(fontWeight, "400");
const buttonPlus = await page.$( "body > div > div > div > button:nth-child(3)", ); await buttonPlus?.click();
await waitForText(page, "body > div > div > div > p", "4");
counterValue = await counter?.evaluate((el) => el.textContent); assert(counterValue === "4"); await page.close(); await browser.close();
await lines.cancel(); serverProcess.kill("SIGTERM"); await serverProcess.status; });
await retry(() => Deno.remove(tmpDirName, { recursive: true })); }, sanitizeResources: false,});
Deno.test("fresh-init error(help)", async function (t) { const includeText = "fresh-init";
await t.step( "execute invalid init command (deno run -A init.ts)", async () => { const cliProcess = new Deno.Command(Deno.execPath(), { args: ["run", "-A", "init.ts"], stdin: "null", stderr: "piped", }); const { code, stderr } = await cliProcess.output(); assertEquals(code, 1);
const errorString = new TextDecoder().decode(stderr); assertStringIncludes(errorString, includeText); }, );
await t.step( "execute invalid init command (deno run -A init.ts -f)", async () => { const cliProcess = new Deno.Command(Deno.execPath(), { args: ["run", "-A", "init.ts", "-f"], }); const { code, stderr } = await cliProcess.output(); assertEquals(code, 1);
const errorString = new TextDecoder().decode(stderr); assertStringIncludes(errorString, includeText); }, );
await t.step( "execute invalid init command (deno run -A init.ts --foo)", async () => { const cliProcess = new Deno.Command(Deno.execPath(), { args: ["run", "-A", "init.ts", "--foo"], }); const { code, stderr } = await cliProcess.output(); assertEquals(code, 1);
const errorString = new TextDecoder().decode(stderr); assertStringIncludes(errorString, includeText); }, );});
Deno.test("fresh-init .", async function (t) { // Preparation const tmpDirName = await Deno.makeTempDir();
await t.step("execute init command", async () => { const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", path.join(Deno.cwd(), "init.ts"), ".", ], cwd: tmpDirName, stdin: "null", stdout: "piped", stderr: "piped", }); const { code, stdout } = await cliProcess.output(); const output = new TextDecoder().decode(stdout); assertNotMatch(output, /Enter your project directory/); assertEquals(code, 0); });});
Deno.test({ name: "fresh-init subdirectory", async fn(t) { // Preparation const tmpDirName = await Deno.makeTempDir();
await Deno.mkdir(path.join(tmpDirName, "subdirectory"));
const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", path.join(Deno.cwd(), "init.ts"), "subdirectory/subsubdirectory", ], cwd: tmpDirName, stdin: "null", stdout: "piped", stderr: "inherit", });
await cliProcess.output();
// move deno.json one level up await Deno.rename( path.join(tmpDirName, "subdirectory", "subsubdirectory", "deno.json"), path.join(tmpDirName, "deno.json"), );
const files = [ "/deno.json", "/subdirectory/subsubdirectory/main.ts", "/subdirectory/subsubdirectory/dev.ts", "/subdirectory/subsubdirectory/fresh.gen.ts", ];
await t.step("check generated files", async () => { await assertFileExistence(files, tmpDirName); });
await t.step("start up the server", async () => { const { serverProcess, lines } = await startFreshServer({ args: ["run", "-A", "--check", "subdirectory/subsubdirectory/dev.ts"], cwd: tmpDirName, });
await delay(100);
await lines.cancel(); serverProcess.kill("SIGTERM"); await serverProcess.status; });
await retry(() => Deno.remove(tmpDirName, { recursive: true })); }, sanitizeResources: false,});
Deno.test("fresh-update", async function fn(t) { // Preparation const tmpDirName = await Deno.makeTempDir();
const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", path.join(Deno.cwd(), "init.ts"), ".", ], cwd: tmpDirName, stdin: "null", stdout: "null", });
await cliProcess.output();
await t.step("execute update command", async () => { await updateAndVerify( /The manifest has been generated for \d+ routes and \d+ islands./, ); });
await t.step("check deno.json", async () => { const configPath = path.join(tmpDirName, "deno.json"); const json = JSONC.parse(await Deno.readTextFile(configPath)) as DenoConfig;
assert(json.tasks?.start, "Missing 'start' task"); assert(json.tasks?.build, "Missing 'build' task"); assert(json.tasks?.preview, "Missing 'preview' task"); });
const comment = "// This is a test comment"; const regex = /("preact": "https:\/\/esm.sh\/preact@[\d.]+",\n)/; const originalName = `${tmpDirName}/deno.json`; const updatedName = `${originalName}c`;
await t.step("execute update command deno.jsonc support", async () => { try { Deno.renameSync(originalName, updatedName); let denoJsonText = await Deno.readTextFile(updatedName); denoJsonText = denoJsonText.replace(regex, `$1${comment}\n`); await Deno.writeTextFile(updatedName, denoJsonText); await updateAndVerify( /The manifest has been generated for \d+ routes and \d+ islands./, ); } finally { let denoJsonText = await Deno.readTextFile(updatedName); denoJsonText = denoJsonText.replace(new RegExp(`\n${comment}\n`), "\n"); await Deno.writeTextFile(updatedName, denoJsonText); Deno.renameSync(updatedName, originalName); } });
await t.step("execute update command src dir", async () => { const names = [ "components", "islands", "routes", "static", "dev.ts", "main.ts", "fresh.gen.ts", ]; try { Deno.mkdirSync(tmpDirName + "/src"); names.forEach((x) => { Deno.renameSync( path.join(tmpDirName, x), path.join(tmpDirName, "src", x), ); }); await updateAndVerify( /The manifest has been generated for (?!0 routes and 0 islands)\d+ routes and \d+ islands./, ); } finally { names.forEach((x) => { Deno.renameSync( path.join(tmpDirName, "src", x), path.join(tmpDirName, x), ); }); Deno.removeSync(tmpDirName + "/src", { recursive: true }); } });
await t.step("execute update command (no islands directory)", async () => { await retry(() => Deno.remove(path.join(tmpDirName, "islands"), { recursive: true }) ); await updateAndVerify( /The manifest has been generated for \d+ routes and 0 islands./, ); });
await retry(() => Deno.remove(tmpDirName, { recursive: true }));
async function updateAndVerify(expected: RegExp) { const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", path.join(Deno.cwd(), "update.ts"), ".", ], cwd: tmpDirName, stdin: "null", stdout: "piped", });
const { code, stdout } = await cliProcess.output(); const output = new TextDecoder().decode(stdout);
assertMatch( output, expected, ); assertEquals(code, 0); }});
Deno.test("fresh-update add _app.tsx if not present", async function fn(t) { // Preparation const tmpDirName = await Deno.makeTempDir();
const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", path.join(Deno.cwd(), "init.ts"), ".", ], cwd: tmpDirName, stdin: "null", stdout: "null", });
await cliProcess.output();
const appTsx = path.join(tmpDirName, "routes", "_app.tsx"); await Deno.remove(appTsx);
await t.step("execute update command", async () => { await updateAndVerify( /The manifest has been generated for \d+ routes and \d+ islands./, ); });
await t.step("add _app.tsx", async () => { const raw = await Deno.readTextFile(appTsx); assert(raw.includes("<html>"), `<html>-tag not found in _app.tsx`); });
async function updateAndVerify(expected: RegExp) { const cliProcess = new Deno.Command(Deno.execPath(), { args: [ "run", "-A", path.join(Deno.cwd(), "update.ts"), ".", ], cwd: tmpDirName, stdin: "null", stdout: "piped", });
const { code, stdout } = await cliProcess.output(); const output = new TextDecoder().decode(stdout);
assertMatch( output, expected, ); assertEquals(code, 0); }});