From 5bbe385b21597f240eafc989c8909768ca96a65a Mon Sep 17 00:00:00 2001 From: Robin Lindner Date: Fri, 7 Oct 2022 15:36:24 +0200 Subject: [PATCH] Improve test infrastructure for integrations/deno (#5005) * Improve test infrastructure for integrations/deno * Add changeset * Use declared type * Remove changeset * Upgrade deno version in -workflow --- .github/workflows/ci.yml | 2 +- .../integrations/deno/test/basics.test.js | 104 -------------- .../integrations/deno/test/basics.test.ts | 128 ++++++++++++++++++ packages/integrations/deno/test/deps.js | 4 - .../deno/test/dynamic-import.test.js | 21 --- .../deno/test/dynamic-import.test.ts | 22 +++ packages/integrations/deno/test/helpers.js | 60 -------- packages/integrations/deno/test/helpers.ts | 86 ++++++++++++ 8 files changed, 237 insertions(+), 190 deletions(-) delete mode 100644 packages/integrations/deno/test/basics.test.js create mode 100644 packages/integrations/deno/test/basics.test.ts delete mode 100644 packages/integrations/deno/test/deps.js delete mode 100644 packages/integrations/deno/test/dynamic-import.test.js create mode 100644 packages/integrations/deno/test/dynamic-import.test.ts delete mode 100644 packages/integrations/deno/test/helpers.js create mode 100644 packages/integrations/deno/test/helpers.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee5e55901..b98f3bc55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,7 +133,7 @@ jobs: - name: Use Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.19.3 + deno-version: v1.26.1 - name: Install dependencies run: pnpm install diff --git a/packages/integrations/deno/test/basics.test.js b/packages/integrations/deno/test/basics.test.js deleted file mode 100644 index c883fc8ae..000000000 --- a/packages/integrations/deno/test/basics.test.js +++ /dev/null @@ -1,104 +0,0 @@ -import { runBuildAndStartApp } from './helpers.js'; -import { assertEquals, assert, DOMParser } from './deps.js'; - -async function startApp(cb) { - await runBuildAndStartApp('./fixtures/basics/', cb); -} - -// this needs to be here and not in the specific test case, because -// the variables are loaded in the global scope of the built server -// module, which is only executed once upon the first load -const varContent = 'this is a value stored in env variable'; -Deno.env.set('SOME_VARIABLE', varContent); - -Deno.test({ - name: 'Basics', - async fn() { - await startApp(async () => { - const resp = await fetch('http://127.0.0.1:8085/'); - assertEquals(resp.status, 200); - const html = await resp.text(); - assert(html); - const doc = new DOMParser().parseFromString(html, `text/html`); - const div = doc.querySelector('#react'); - assert(div, 'div exists'); - }); - }, -}); - -Deno.test({ - name: 'Custom 404', - async fn() { - await startApp(async () => { - const resp = await fetch('http://127.0.0.1:8085/this-does-not-exist'); - assertEquals(resp.status, 404); - const html = await resp.text(); - assert(html); - const doc = new DOMParser().parseFromString(html, `text/html`); - const header = doc.querySelector('#custom-404'); - assert(header, 'displays custom 404'); - }); - }, -}); - -Deno.test({ - name: 'Loads style assets', - async fn() { - await startApp(async () => { - let resp = await fetch('http://127.0.0.1:8085/'); - const html = await resp.text(); - - const doc = new DOMParser().parseFromString(html, `text/html`); - const link = doc.querySelector('link'); - const href = link.getAttribute('href'); - - resp = await fetch(new URL(href, 'http://127.0.0.1:8085/')); - assertEquals(resp.status, 200); - const ct = resp.headers.get('content-type'); - assertEquals(ct, 'text/css'); - await resp.body.cancel(); - }); - }, -}); - -Deno.test({ - name: 'Correctly loads run-time env variables', - async fn() { - await startApp(async () => { - const resp = await fetch('http://127.0.0.1:8085/'); - const html = await resp.text(); - - const doc = new DOMParser().parseFromString(html, `text/html`); - const p = doc.querySelector('p#env-value'); - assertEquals(p.innerText, varContent); - }); - }, -}); - -Deno.test({ - name: 'Works with Markdown', - async fn() { - await startApp(async () => { - const resp = await fetch('http://127.0.0.1:8085/markdown'); - const html = await resp.text(); - - const doc = new DOMParser().parseFromString(html, `text/html`); - const h1 = doc.querySelector('h1'); - assertEquals(h1.innerText, 'Heading from Markdown'); - }); - }, -}); - -Deno.test({ - name: 'Works with MDX', - async fn() { - await startApp(async () => { - const resp = await fetch('http://127.0.0.1:8085/mdx'); - const html = await resp.text(); - - const doc = new DOMParser().parseFromString(html, `text/html`); - const h1 = doc.querySelector('h1'); - assertEquals(h1.innerText, 'Heading from MDX'); - }); - }, -}); diff --git a/packages/integrations/deno/test/basics.test.ts b/packages/integrations/deno/test/basics.test.ts new file mode 100644 index 000000000..5449788d7 --- /dev/null +++ b/packages/integrations/deno/test/basics.test.ts @@ -0,0 +1,128 @@ +import { StartServerCallback, runBuildAndStartApp, defaultTestPermissions } from './helpers.ts'; +import { DOMParser } from "https://deno.land/x/deno_dom@v0.1.35-alpha/deno-dom-wasm.ts"; +import { assert, assertEquals } from "https://deno.land/std@0.158.0/testing/asserts.ts"; + +async function startApp(cb: StartServerCallback) { + await runBuildAndStartApp('./fixtures/basics/', cb); +} + +// this needs to be here and not in the specific test case, because +// the variables are loaded in the global scope of the built server +// module, which is only executed once upon the first load +const varContent = 'this is a value stored in env variable'; +Deno.env.set('SOME_VARIABLE', varContent); + +Deno.test({ + name: 'Basics', + permissions: defaultTestPermissions, + async fn() { + await startApp(async (baseUrl: URL) => { + const resp = await fetch(baseUrl); + assertEquals(resp.status, 200); + + const html = await resp.text(); + assert(html); + + const doc = new DOMParser().parseFromString(html, `text/html`); + const div = doc!.querySelector('#react'); + + assert(div, 'div exists'); + }); + }, + sanitizeResources: false, + sanitizeOps: false +}); + +Deno.test({ + name: 'Custom 404', + permissions: defaultTestPermissions, + async fn() { + await startApp(async (baseUrl: URL) => { + const resp = await fetch(new URL("this-does-not-exist", baseUrl)); + assertEquals(resp.status, 404); + + const html = await resp.text(); + assert(html); + + const doc = new DOMParser().parseFromString(html, `text/html`); + const header = doc!.querySelector('#custom-404'); + assert(header, 'displays custom 404'); + }); + }, + sanitizeResources: false, + sanitizeOps: false +}); + +Deno.test({ + name: 'Loads style assets', + permissions: defaultTestPermissions, + async fn() { + await startApp(async (baseUrl: URL) => { + let resp = await fetch(baseUrl); + const html = await resp.text(); + + const doc = new DOMParser().parseFromString(html, `text/html`); + const link = doc!.querySelector('link'); + const href = link!.getAttribute('href'); + + resp = await fetch(new URL(href!, baseUrl)); + assertEquals(resp.status, 200); + const ct = resp.headers.get('content-type'); + assertEquals(ct, 'text/css'); + await resp.body!.cancel(); + }); + }, + sanitizeResources: false, + sanitizeOps: false +}); + +Deno.test({ + name: 'Correctly loads run-time env variables', + permissions: defaultTestPermissions, + async fn() { + await startApp(async (baseUrl: URL) => { + const resp = await fetch(baseUrl); + const html = await resp.text(); + + const doc = new DOMParser().parseFromString(html, `text/html`); + const p = doc!.querySelector('p#env-value'); + assertEquals(p!.innerText, varContent); + }); + }, + sanitizeResources: false, + sanitizeOps: false +}); + +Deno.test({ + name: 'Works with Markdown', + permissions: defaultTestPermissions, + async fn() { + await startApp(async (baseUrl: URL) => { + const resp = await fetch(new URL('markdown', baseUrl)); + const html = await resp.text(); + + const doc = new DOMParser().parseFromString(html, `text/html`); + const h1 = doc!.querySelector('h1'); + assertEquals(h1!.innerText, 'Heading from Markdown'); + }); + }, + sanitizeResources: false, + sanitizeOps: false +}); + +Deno.test({ + name: 'Works with MDX', + permissions: defaultTestPermissions, + async fn() { + await startApp(async (baseUrl: URL) => { + const resp = await fetch(new URL('mdx', baseUrl)); + const html = await resp.text(); + + const doc = new DOMParser().parseFromString(html, `text/html`); + const h1 = doc!.querySelector('h1'); + assertEquals(h1!.innerText, 'Heading from MDX'); + }); + }, + sanitizeResources: false, + sanitizeOps: false +}); diff --git a/packages/integrations/deno/test/deps.js b/packages/integrations/deno/test/deps.js deleted file mode 100644 index 0fd0a6673..000000000 --- a/packages/integrations/deno/test/deps.js +++ /dev/null @@ -1,4 +0,0 @@ -export * from 'https://deno.land/std@0.110.0/path/mod.ts'; -export * from 'https://deno.land/std@0.132.0/testing/asserts.ts'; -export * from 'https://deno.land/x/deno_dom/deno-dom-wasm.ts'; -export * from 'https://deno.land/std@0.142.0/streams/conversion.ts'; diff --git a/packages/integrations/deno/test/dynamic-import.test.js b/packages/integrations/deno/test/dynamic-import.test.js deleted file mode 100644 index 56d7fe5a2..000000000 --- a/packages/integrations/deno/test/dynamic-import.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import { runBuildAndStartAppFromSubprocess } from './helpers.js'; -import { assertEquals, assert, DOMParser } from './deps.js'; - -async function startApp(cb) { - await runBuildAndStartAppFromSubprocess('./fixtures/dynimport/', cb); -} - -Deno.test({ - name: 'Dynamic import', - async fn() { - await startApp(async () => { - const resp = await fetch('http://127.0.0.1:8085/'); - assertEquals(resp.status, 200); - const html = await resp.text(); - assert(html); - const doc = new DOMParser().parseFromString(html, `text/html`); - const div = doc.querySelector('#thing'); - assert(div, 'div exists'); - }); - }, -}); diff --git a/packages/integrations/deno/test/dynamic-import.test.ts b/packages/integrations/deno/test/dynamic-import.test.ts new file mode 100644 index 000000000..5b8cd043a --- /dev/null +++ b/packages/integrations/deno/test/dynamic-import.test.ts @@ -0,0 +1,22 @@ +import { DOMParser } from "https://deno.land/x/deno_dom@v0.1.35-alpha/deno-dom-wasm.ts"; +import { assert, assertEquals } from "https://deno.land/std@0.158.0/testing/asserts.ts"; +import { StartServerCallback, runBuildAndStartAppFromSubprocess } from "./helpers.ts"; + +async function startApp(cb: StartServerCallback) { + await runBuildAndStartAppFromSubprocess('./fixtures/dynimport/', cb); +} + +Deno.test({ + name: 'Dynamic import', + async fn() { + await startApp(async (baseUrl: URL) => { + const resp = await fetch(baseUrl); + assertEquals(resp.status, 200); + const html = await resp.text(); + assert(html); + const doc = new DOMParser().parseFromString(html, `text/html`); + const div = doc!.querySelector('#thing'); + assert(div, 'div exists'); + }); + }, +}); diff --git a/packages/integrations/deno/test/helpers.js b/packages/integrations/deno/test/helpers.js deleted file mode 100644 index d33267848..000000000 --- a/packages/integrations/deno/test/helpers.js +++ /dev/null @@ -1,60 +0,0 @@ -import { readableStreamFromReader, fromFileUrl } from './deps.js'; -const dir = new URL('./', import.meta.url); - -export async function runBuild(fixturePath) { - let proc = Deno.run({ - cmd: ['node', '../../../../../astro/astro.js', 'build', '--silent'], - cwd: fromFileUrl(new URL(fixturePath, dir)), - }); - await proc.status(); - return async () => await proc.close(); -} - -export async function startModFromImport(baseUrl) { - const entryUrl = new URL('./dist/server/entry.mjs', baseUrl); - const mod = await import(entryUrl); - - if (!mod.running()) { - mod.start(); - } - - return () => mod.stop(); -} - -export async function startModFromSubprocess(baseUrl) { - const entryUrl = new URL('./dist/server/entry.mjs', baseUrl); - let proc = Deno.run({ - cmd: ['deno', 'run', '--allow-env', '--allow-net', fromFileUrl(entryUrl)], - cwd: fromFileUrl(baseUrl), - stderr: 'piped', - }); - const stderr = readableStreamFromReader(proc.stderr); - const dec = new TextDecoder(); - for await (let bytes of stderr) { - let msg = dec.decode(bytes); - if (msg.includes(`Server running`)) { - break; - } - } - return () => proc.close(); -} - -export async function runBuildAndStartApp(fixturePath, cb) { - const url = new URL(fixturePath, dir); - const close = await runBuild(fixturePath); - const stop = await startModFromImport(url); - - await cb(); - await stop(); - await close(); -} - -export async function runBuildAndStartAppFromSubprocess(fixturePath, cb) { - const url = new URL(fixturePath, dir); - const close = await runBuild(fixturePath); - const stop = await startModFromSubprocess(url); - - await cb(); - await stop(); - await close(); -} diff --git a/packages/integrations/deno/test/helpers.ts b/packages/integrations/deno/test/helpers.ts new file mode 100644 index 000000000..4b65d210c --- /dev/null +++ b/packages/integrations/deno/test/helpers.ts @@ -0,0 +1,86 @@ +import { fromFileUrl } from 'https://deno.land/std@0.110.0/path/mod.ts'; +import { assert } from "https://deno.land/std@0.158.0/testing/asserts.ts"; +import { readableStreamFromReader } from 'https://deno.land/std@0.142.0/streams/conversion.ts'; + +const dir = new URL('./', import.meta.url); +const defaultURL = new URL("http://localhost:8085/"); + +export const defaultTestPermissions: Deno.PermissionOptions = { + read: true, + net: true, + run: true, + env: true +}; + +export declare type StartServerCallback = (url: URL) => Promise; +declare type ExitCallback = () => void; + +export async function runBuild(fixturePath: string) { + const proc = Deno.run({ + cmd: ['node', '../../../../../astro/astro.js', 'build', '--silent'], + cwd: fromFileUrl(new URL(fixturePath, dir)), + }); + try { + const status = await proc.status(); + assert(status.success) + } finally { + proc.close(); + } +} + +export async function startModFromImport(baseUrl: URL): Promise { + const entryUrl = new URL('./dist/server/entry.mjs', baseUrl); + const mod = await import(entryUrl.toString()); + + if (!mod.running()) { + mod.start(); + } + + return () => mod.stop(); +} + +export async function startModFromSubprocess(baseUrl: URL): Promise { + const entryUrl = new URL('./dist/server/entry.mjs', baseUrl); + const proc = Deno.run({ + cmd: ['deno', 'run', '--allow-env', '--allow-net', fromFileUrl(entryUrl)], + cwd: fromFileUrl(baseUrl), + stderr: 'piped', + }); + + const stderr = readableStreamFromReader(proc.stderr); + const dec = new TextDecoder(); + for await (const bytes of stderr) { + const msg = dec.decode(bytes); + if (msg.includes(`Server running`)) { + break; + } + } + + return () => proc.close(); +} + +export async function runBuildAndStartApp(fixturePath: string, cb: StartServerCallback) { + const url = new URL(fixturePath, dir); + + await runBuild(fixturePath); + const stop = await startModFromImport(url); + + try { + await cb(defaultURL); + } finally { + stop(); + } +} + +export async function runBuildAndStartAppFromSubprocess(fixturePath: string, cb: StartServerCallback) { + const url = new URL(fixturePath, dir); + + await runBuild(fixturePath); + const stop = await startModFromSubprocess(url); + + try { + await cb(defaultURL); + } finally { + stop(); + } +}