Skip to main content
Module

x/fresh/tests/main_test.ts

The next-gen web framework.
Extremely Popular
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
import { ServerContext, Status } from "../server.ts";import { assert, assertEquals, assertStringIncludes, delay, puppeteer,} from "./deps.ts";import manifest from "./fixture/fresh.gen.ts";import options from "./fixture/options.ts";import { BUILD_ID } from "../src/server/build_id.ts";import { startFreshServer } from "./test_utils.ts";
const ctx = await ServerContext.fromManifest(manifest, options);const handler = ctx.handler();const router = (req: Request) => { return handler(req, { localAddr: { transport: "tcp", hostname: "127.0.0.1", port: 80, }, remoteAddr: { transport: "tcp", hostname: "127.0.0.1", port: 80, }, });};
Deno.test("/ page prerender", async () => { const resp = await router(new Request("https://fresh.deno.dev/")); assert(resp); assertEquals(resp.status, Status.OK); assertEquals(resp.headers.get("content-type"), "text/html; charset=utf-8"); assertEquals(resp.headers.get("server"), "fresh test server"); const body = await resp.text(); assertStringIncludes(body, `<html lang="en">`); assertStringIncludes(body, "test.js"); assertStringIncludes(body, "<p>Hello!</p>"); assertStringIncludes(body, "<p>Viewing JIT render.</p>"); assertStringIncludes(body, `>{"v":[[{"message":"Hello!"}],[]]}</script>`); assertStringIncludes( body, '<meta name="description" content="Hello world!"/>', ); assertStringIncludes(body, `<link rel="modulepreload"`);});
Deno.test("/props/123 page prerender", async () => { const resp = await router(new Request("https://fresh.deno.dev/props/123")); assert(resp); assertEquals(resp.status, Status.OK); assertEquals(resp.headers.get("content-type"), "text/html; charset=utf-8"); const body = await resp.text(); assertStringIncludes( body, `{&quot;params&quot;:{&quot;id&quot;:&quot;123&quot;},&quot;url&quot;:&quot;https://fresh.deno.dev/props/123&quot;,&quot;route&quot;:&quot;/props/:id&quot;}`, );});
Deno.test("/[name] page prerender", async () => { const resp = await router(new Request("https://fresh.deno.dev/bar")); assert(resp); assertEquals(resp.status, Status.OK); assertEquals(resp.headers.get("content-type"), "text/html; charset=utf-8"); const body = await resp.text(); assertStringIncludes(body, "<div>Hello bar</div>");});
Deno.test("/api/head_override - HEAD", async () => { const req = new Request("https://fresh.deno.dev/api/head_override", { method: "HEAD", }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.NoContent); assertEquals(resp.body, null); assertEquals( resp.headers.get("content-type"), "text/html; charset=utf-8", );});
Deno.test("/api/get_only - HEAD fallback", async () => { const req = new Request("https://fresh.deno.dev/api/get_only", { method: "HEAD", }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.OK); assertEquals(resp.body, null); assertEquals( resp.headers.get("content-type"), "application/json; charset=utf-8", );});
Deno.test("/intercept - GET html", async () => { const req = new Request("https://fresh.deno.dev/intercept", { headers: { "accept": "text/html" }, }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertStringIncludes(body, "<div>This is HTML</div>");});
Deno.test("/intercept - GET text", async () => { const req = new Request("https://fresh.deno.dev/intercept", { headers: { "accept": "text/plain" }, }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertEquals(body, "This is plain text");});
Deno.test("/intercept - POST", async () => { const req = new Request("https://fresh.deno.dev/intercept", { method: "POST", }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertEquals(body, "POST response");});
Deno.test("/intercept - DELETE", async () => { const req = new Request("https://fresh.deno.dev/intercept", { method: "DELETE", }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.MethodNotAllowed);});
Deno.test("/intercept_args - GET html", async () => { const req = new Request("https://fresh.deno.dev/intercept_args", { headers: { "accept": "text/html" }, }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertStringIncludes(body, "<div>intercepted</div>");});
Deno.test("/status_overwrite", async () => { const req = new Request("https://fresh.deno.dev/status_overwrite", { headers: { "accept": "text/html" }, }); const resp = await router(req); assert(resp); assertEquals(resp.status, Status.Unauthorized); assertEquals(resp.headers.get("x-some-header"), "foo"); const body = await resp.text(); assertStringIncludes(body, "<div>This is HTML</div>");});
Deno.test("/api/get_only - NOTAMETHOD", async () => { const resp = await router( new Request("https://fresh.deno.dev/api/get_only", { method: "NOTAMETHOD", }), ); assert(resp); assertEquals(resp.status, Status.MethodNotAllowed);});
Deno.test("/api/xyz not found", async () => { const resp = await router(new Request("https://fresh.deno.dev/api/xyz")); assert(resp); assertEquals(resp.status, Status.NotFound); const body = await resp.text(); assertStringIncludes(body, "404 not found: /api/xyz");});
Deno.test("/static page prerender", async () => { const resp = await router(new Request("https://fresh.deno.dev/static")); assert(resp); assertEquals(resp.status, Status.OK); assertEquals(resp.headers.get("content-type"), "text/html; charset=utf-8"); const body = await resp.text(); assert(!body.includes(`main.js`)); assert(!body.includes(`island-test.js`)); assertStringIncludes(body, "<p>This is a static page.</p>"); assertStringIncludes(body, `src="/image.png?__frsh_c=`); assert(!body.includes("__FRSH_ISLAND_PROPS"));});
Deno.test("/books/:id page - /books/123", async () => { const resp = await router(new Request("https://fresh.deno.dev/books/123")); assert(resp); assertEquals(resp.status, Status.OK); assertEquals(resp.headers.get("content-type"), "text/html; charset=utf-8"); const body = await resp.text(); assertStringIncludes(body, "<div>Book 123</div>");});
Deno.test("/books/:id page - /books/abc", async () => { const resp = await router(new Request("https://fresh.deno.dev/books/abc")); assert(resp); assertEquals(resp.status, Status.NotFound);});
Deno.test("redirect /pages/fresh/ to /pages/fresh", async () => { const resp = await router(new Request("https://fresh.deno.dev/pages/fresh/")); assert(resp); assertEquals(resp.status, Status.TemporaryRedirect); assertEquals( resp.headers.get("location"), "/pages/fresh", );});
Deno.test("redirect /pages/////fresh///// to /pages/////fresh", async () => { const resp = await router( new Request("https://fresh.deno.dev/pages/////fresh/////"), ); assert(resp); assertEquals(resp.status, Status.TemporaryRedirect); assertEquals( resp.headers.get("location"), "/pages/////fresh", );});
Deno.test("redirect /pages/////fresh/ to /pages/////fresh", async () => { const resp = await router( new Request("https://fresh.deno.dev/pages/////fresh/"), ); assert(resp); assertEquals(resp.status, Status.TemporaryRedirect); assertEquals( resp.headers.get("location"), "/pages/////fresh", );});
Deno.test("no redirect for /pages/////fresh", async () => { const resp = await router( new Request("https://fresh.deno.dev/pages/////fresh"), ); assert(resp); assertEquals(resp.status, Status.NotFound);});
Deno.test("/failure", async () => { const resp = await router(new Request("https://fresh.deno.dev/failure")); assert(resp); assertEquals(resp.status, Status.InternalServerError); const body = await resp.text(); assert(body.includes("500 internal error: it errored!"));});
Deno.test("/foo/:path*", async () => { const resp = await router(new Request("https://fresh.deno.dev/foo/bar/baz")); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assert(body.includes("bar/baz"));});
Deno.test("static files in custom directory", async () => { const newCtx = await ServerContext.fromManifest(manifest, { ...options, staticDir: "./custom_static", }); const newRouter = (req: Request) => { return newCtx.handler()(req, { localAddr: { transport: "tcp", hostname: "127.0.0.1", port: 80, }, remoteAddr: { transport: "tcp", hostname: "127.0.0.1", port: 80, }, }); };
const resp = await newRouter( new Request("https://fresh.deno.dev/custom.txt"), ); assertEquals(resp.status, Status.OK); const body = await resp.text(); assert(body.startsWith("dir"));});
Deno.test("static file - by file path", async () => { const resp = await router(new Request("https://fresh.deno.dev/foo.txt")); assertEquals(resp.status, Status.OK); const body = await resp.text(); assert(body.startsWith("bar")); const etag = resp.headers.get("etag"); assert(etag); // The etag is not weak, because this did not go through content encoding, so // this is not a real server. assert(!etag.startsWith("W/"), "etag should be weak"); assertEquals(resp.headers.get("content-type"), "text/plain");
const resp2 = await router( new Request("https://fresh.deno.dev/foo.txt", { headers: { "if-none-match": etag, }, }), ); assertEquals(resp2.status, Status.NotModified); assertEquals(resp2.headers.get("etag"), etag); assertEquals(resp2.headers.get("content-type"), "text/plain");
const resp3 = await router( new Request("https://fresh.deno.dev/foo.txt", { headers: { "if-none-match": `W/${etag}`, }, }), ); assertEquals(resp3.status, Status.NotModified); assertEquals(resp3.headers.get("etag"), etag); assertEquals(resp3.headers.get("content-type"), "text/plain");});
Deno.test("HEAD request", async () => { // Static file const resp = await router( new Request("https://fresh.deno.dev/foo.txt", { method: "HEAD", }), ); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertEquals(body, "");
// route const resp2 = await router( new Request("https://fresh.deno.dev/books/123", { method: "HEAD", }), ); assert(resp2); assertEquals(resp2.status, Status.OK); const body2 = await resp2.text(); assertEquals(body2, "");});
Deno.test("static file - by 'hashed' path", async () => { // Check that the file path have the BUILD_ID const resp = await router( new Request("https://fresh.deno.dev/assetsCaching"), ); const body = await resp.text(); const imgFilePath = body.match(/img id="img-with-hashing" src="(.*?)"/)?.[1]; assert(imgFilePath); assert(imgFilePath.includes(`?__frsh_c=${BUILD_ID}`));
// check the static file is served corectly under its cacheable route const resp2 = await router( new Request(`https://fresh.deno.dev${imgFilePath}`), ); const _ = await resp2.text(); assertEquals(resp2.status, Status.OK); assertEquals( resp2.headers.get("cache-control"), "public, max-age=31536000, immutable", );
const resp3 = await router( new Request(`https://fresh.deno.dev${imgFilePath}`, { headers: { "if-none-match": resp2.headers.get("etag")!, }, }), ); assertEquals(resp3.status, Status.NotModified);
// ensure asset hook is not applied on file explicitly excluded with attribute const imgFilePathWithNoCache = body.match( /img id="img-without-hashing" src="(.*?)"/, )?.[1]; assert(imgFilePathWithNoCache); assert( !imgFilePathWithNoCache.includes(BUILD_ID), "img-without-hashing", );
// ensure asset hook is applied on img within an island const imgInIsland = body.match(/img id="img-in-island" src="(.*?)"/)?.[1]; assert(imgInIsland); assert(imgInIsland.includes(BUILD_ID), "img-in-island");
// verify that the asset hook is applied to the srcset const imgInIslandSrcSet = body.match(/srcset="(.*?)"/)?.[1]; assert(imgInIslandSrcSet); assert( imgInIslandSrcSet.includes(BUILD_ID), "img-in-island-srcset", );
// verify that the asset hook is not applied to img outside reference out of the static folder const imgMissing = body.match(/img id="img-missing" src="(.*?)"/)?.[1]; assert(imgMissing); assert( !imgMissing.includes(BUILD_ID), "Applying hash on unknown asset", );});
Deno.test("/params/:path*", async () => { const resp = await router( new Request("https://fresh.deno.dev/params/bar/baz"), ); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertEquals(body, "bar/baz");});
Deno.test("/connInfo", async () => { const resp = await router(new Request("https://fresh.deno.dev/connInfo")); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertEquals(body, "127.0.0.1");});
Deno.test({ name: "/middleware - root", fn: async () => { const resp = await router( new Request("https://fresh.deno.dev/middleware_root"), ); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertStringIncludes(body, "root_mw"); assert(!body.includes("layer1_mw")); },});
Deno.test({ name: "/middleware - mixedHandler(cors)", fn: async () => { const resp = await router( new Request("https://fresh.deno.dev/middleware_root", { method: "OPTIONS", }), ); assert(resp);
// test cors handler assertEquals(resp.status, Status.NoContent); },});
Deno.test({ name: "/middleware - mixedHandler(log)", fn: async () => { const resp = await router( new Request("https://fresh.deno.dev/middleware_root"), ); assert(resp); assertEquals(resp.status, Status.OK);
// test log handler const latency = resp.headers.get("latency"); assert(latency); assert(+latency >= 0, `latency=${latency}ms `); },});
Deno.test({ name: "/middleware - layer 2 middleware", fn: async () => { const resp = await router( new Request("https://fresh.deno.dev/layeredMdw/layer2/abc"), ); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); console.log(body); assertStringIncludes(body, "root_mw"); assertStringIncludes(body, "layer1_mw"); assertStringIncludes(body, "layer2_mw"); // layered 2 should not run layer 3 middleware assert(!body.includes("layer3_mw"));
const resp1 = await router( new Request("https://fresh.deno.dev/layeredMdw/layer2-no-mw/without_mw"), ); assert(resp1); assertEquals(resp1.status, Status.OK); const body1 = await resp1.text(); assertStringIncludes(body1, "root_mw"); assertStringIncludes(body1, "layer1_mw"); // layered 2 should not run layer 2 or 3 middleware assert(!body1.includes("layer2_mw")); assert(!body1.includes("layer3_mw")); },});
Deno.test({ name: "/middleware - layer 2 middleware at index", fn: async () => { const resp = await router( new Request("https://fresh.deno.dev/layeredMdw/layer2"), ); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); console.log(body); assertStringIncludes(body, "root_mw"); assertStringIncludes(body, "layer1_mw"); assertStringIncludes(body, "layer2_mw"); // layered 2 should not run layer 3 middleware assert(!body.includes("layer3_mw"));
const resp1 = await router( new Request("https://fresh.deno.dev/layeredMdw/layer2-no-mw/without_mw"), ); assert(resp1); assertEquals(resp1.status, Status.OK); const body1 = await resp1.text(); assertStringIncludes(body1, "root_mw"); assertStringIncludes(body1, "layer1_mw"); // layered 2 should not run layer 2 or 3 middleware assert(!body1.includes("layer2_mw")); assert(!body1.includes("layer3_mw")); },});
Deno.test({ name: "/middleware - layer 3 middleware", fn: async () => { // layered 3 should contain layer 3 middleware data const resp = await router( new Request("https://fresh.deno.dev/layeredMdw/layer2/layer3/abc"), ); assert(resp); assertEquals(resp.status, Status.OK); const body = await resp.text(); assertStringIncludes(body, "root_mw"); assertStringIncludes(body, "layer1_mw"); assertStringIncludes(body, "layer3_mw"); assertEquals(resp.headers.get("layer3"), "fresh test server layer3"); // the below ensure that the middlewware are applied in the correct order. // i.e response header set from layer3 middleware is overwritten // by the reponse header in layer 0 assertEquals(resp.headers.get("server"), "fresh test server"); },});
Deno.test({ name: "/not_found", fn: async () => { const resp = await router(new Request("https://fresh.deno.dev/not_found")); assert(resp); assertEquals(resp.status, 404); const body = await resp.text(); assertStringIncludes(body, "404 not found: /not_found"); },});
Deno.test("middleware destination", async (t) => { await t.step("internal", async () => { const resp = await router( new Request("https://fresh.deno.dev/_frsh/refresh.js"), ); assert(resp); assertEquals(resp.headers.get("destination"), "internal"); await resp.body?.cancel(); });
await t.step("static", async () => { const resp = await router(new Request("https://fresh.deno.dev/foo.txt")); assert(resp); assertEquals(resp.headers.get("destination"), "static"); await resp.body?.cancel(); });
await t.step("route", async () => { const resp = await router(new Request("https://fresh.deno.dev/")); assert(resp); assertEquals(resp.headers.get("destination"), "route"); await resp.body?.cancel(); });
await t.step("notFound", async () => { const resp = await router(new Request("https://fresh.deno.dev/bar/bar")); assert(resp); assertEquals(resp.headers.get("destination"), "notFound"); await resp.body?.cancel(); });});
Deno.test("experimental Deno.serve", { sanitizeOps: false, sanitizeResources: false, ignore: Deno.build.os === "windows", // TODO: Deno.serve hang on Windows?}, async (t) => { // Preparation const { serverProcess, lines, address } = await startFreshServer({ args: [ "run", "-A", "--unstable", "./tests/fixture/main.ts", "--experimental-deno-serve", ], });
await delay(100);
await t.step("ssr", async () => { const resp = await fetch(address); assert(resp); assertEquals(resp.status, Status.OK); assertEquals(resp.headers.get("content-type"), "text/html; charset=utf-8"); assertEquals(resp.headers.get("server"), "fresh test server"); const body = await resp.text(); assertStringIncludes(body, `<html lang="en">`); assertStringIncludes(body, "test.js"); assertStringIncludes(body, "<p>Hello!</p>"); assertStringIncludes(body, "<p>Viewing JIT render.</p>"); assertStringIncludes(body, `>{"v":[[{"message":"Hello!"}],[]]}</script>`); assertStringIncludes( body, '<meta name="description" content="Hello world!"/>', ); });
await t.step("static file", async () => { const resp = await fetch(`${address}/foo.txt`); assertEquals(resp.status, Status.OK); const body = await resp.text(); assert(body.startsWith("bar")); const etag = resp.headers.get("etag"); assert(etag); // TODO(kt3k): Enable this assertion when new Deno.serve is released. // https://github.com/denoland/deno/pull/18568 // assert(etag.startsWith("W/"), "etag should be weak"); assertEquals(resp.headers.get("content-type"), "text/plain"); });
await lines.cancel(); serverProcess.kill("SIGTERM");});
Deno.test("jsx pragma works", { sanitizeOps: false, sanitizeResources: false,}, async (t) => { // Preparation const { serverProcess, lines, address } = await startFreshServer({ args: ["run", "-A", "./tests/fixture_jsx_pragma/main.ts"], });
await delay(100);
await t.step("ssr", async () => { const resp = await fetch(address); assertEquals(resp.status, Status.OK); const text = await resp.text(); assertStringIncludes(text, "Hello World"); assertStringIncludes(text, "ssr"); });
const browser = await puppeteer.launch({ args: ["--no-sandbox"] }); const page = await browser.newPage();
await page.goto(address, { waitUntil: "networkidle2", });
await t.step("island is revived", async () => { await page.waitForSelector("#csr"); });
await browser.close();
await lines.cancel(); serverProcess.kill("SIGTERM");});
Deno.test("preact/debug is active in dev mode", { sanitizeOps: false, sanitizeResources: false,}, async (t) => { // Preparation const { serverProcess, lines, address } = await startFreshServer({ args: ["run", "-A", "./tests/fixture_render_error/main.ts"], });
await delay(100);
await t.step("SSR error is shown", async () => { const resp = await fetch(address); assertEquals(resp.status, Status.InternalServerError); const text = await resp.text(); assertStringIncludes(text, "Objects are not valid as a child"); });
const browser = await puppeteer.launch({ args: ["--no-sandbox"] }); const page = await browser.newPage();
await page.goto(address, { waitUntil: "networkidle2", });
await t.step("error page is shown with error message", async () => { const el = await page.waitForSelector(".frsh-error-page"); const text = await page.evaluate((el) => el.textContent, el); assertStringIncludes(text, "Objects are not valid as a child"); });
await browser.close();
await lines.cancel(); serverProcess.kill("SIGTERM");});
Deno.test("preloading javascript files", { sanitizeOps: false, sanitizeResources: false,}, async () => { // Preparation const { serverProcess, lines, address } = await startFreshServer({ args: ["run", "-A", "./tests/fixture/main.ts"], });
const browser = await puppeteer.launch({ args: ["--no-sandbox"] }); const page = await browser.newPage();
try { // request js file to start esbuild execution await page.goto(address, { waitUntil: "networkidle2", });
await delay(5000); // wait running esbuild
await page.goto(address, { waitUntil: "networkidle2", });
const preloads: string[] = await page.$$eval( 'link[rel="modulepreload"]', (elements) => elements.map((element) => element.getAttribute("href")), );
assert( preloads.some((url) => url.match(/\/_frsh\/js\/.*\/main\.js/)), "preloads does not include main.js", ); assert( preloads.some((url) => url.match(/\/_frsh\/js\/.*\/island-.*\.js/)), "preloads does not include island-*.js", ); assert( preloads.some((url) => url.match(/\/_frsh\/js\/.*\/chunk-.*\.js/)), "preloads does not include chunk-*.js", ); } finally { await browser.close();
await lines.cancel(); serverProcess.kill("SIGTERM"); }});
Deno.test("PORT environment variable", { sanitizeOps: false, sanitizeResources: false,}, async () => { const PORT = "8765"; // Preparation const { serverProcess, lines } = await startFreshServer({ args: ["run", "-A", "./tests/fixture/main.ts"], env: { PORT }, });
await delay(100);
const resp = await fetch("http://localhost:" + PORT); assert(resp); assertEquals(resp.status, Status.OK);
await lines.cancel(); serverProcess.kill("SIGTERM");});