From 3524d95f4d08eaa352a6721ed55eb568ea5eda66 Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Tue, 4 Mar 2025 16:11:52 -0800 Subject: [PATCH] chore: add frontend integration test --- .github/workflows/jumble-integration.yml | 60 +++++++++++++++++++ typescript/packages/deno.json | 1 + typescript/packages/deno.lock | 1 + typescript/packages/jumble/deno.json | 3 +- .../packages/jumble/integration/smoke-test.ts | 28 +++++++++ .../packages/jumble/integration/utils.ts | 41 +++++++++++++ 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/jumble-integration.yml create mode 100644 typescript/packages/jumble/integration/smoke-test.ts create mode 100644 typescript/packages/jumble/integration/utils.ts diff --git a/.github/workflows/jumble-integration.yml b/.github/workflows/jumble-integration.yml new file mode 100644 index 000000000..e91374158 --- /dev/null +++ b/.github/workflows/jumble-integration.yml @@ -0,0 +1,60 @@ +name: Jumble Integration + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 10 + defaults: + run: + working-directory: ./typescript/packages/jumble + + services: + redis: + image: redis + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v4 + + - name: Setup Deno + uses: denoland/setup-deno@v2 + with: + deno-version: "2.2.2" + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + ~/.deno + ~/.cache/deno + key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.json') }} + + - name: Run Toolshed + working-directory: typescript/packages/toolshed + run: deno task dev & + + - name: Run Vite + working-directory: typescript/packages/jumble + run: TOOLSHED_API_URL=http://localhost:8000 deno task dev & + # For Astral + # https://github.com/lino-levan/astral/blob/f5ef833b2c5bde3783564a6b925073d5d46bb4b8/README.md#no-usable-sandbox-with-user-namespace-cloning-enabled + - name: Disable AppArmor + run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns + + - name: Run Integration + working-directory: ./typescript/packages/jumble + run: deno task integration diff --git a/typescript/packages/deno.json b/typescript/packages/deno.json index 97b02faa6..b5b729a45 100644 --- a/typescript/packages/deno.json +++ b/typescript/packages/deno.json @@ -46,6 +46,7 @@ }, "imports": { "ai": "npm:ai@^4.1.5", + "@astral/astral": "jsr:@astral/astral", "@cfworker/json-schema": "npm:@cfworker/json-schema@^4.1.0", "@codemirror/lang-javascript": "npm:@codemirror/lang-javascript@^6.2.2", "@codemirror/lang-markdown": "npm:@codemirror/lang-markdown@^6.3.2", diff --git a/typescript/packages/deno.lock b/typescript/packages/deno.lock index 9219415fd..fb2f13b24 100644 --- a/typescript/packages/deno.lock +++ b/typescript/packages/deno.lock @@ -7207,6 +7207,7 @@ }, "workspace": { "dependencies": [ + "jsr:@astral/astral@*", "jsr:@std/assert@1", "jsr:@std/crypto@^1.0.3", "jsr:@std/dotenv@~0.225.3", diff --git a/typescript/packages/jumble/deno.json b/typescript/packages/jumble/deno.json index 549733633..a8c52872c 100644 --- a/typescript/packages/jumble/deno.json +++ b/typescript/packages/jumble/deno.json @@ -3,7 +3,8 @@ "dev": "deno run -A --node-modules-dir=auto npm:vite", "build": "deno run -A --node-modules-dir=auto npm:vite build", "preview": "deno run -A --node-modules-dir=auto npm:vite preview", - "test": "echo 'No tests to run.'" + "test": "echo 'No tests to run.'", + "integration": "deno run -A ./integration/smoke-test.ts" }, "imports": { "@/": "./src/", diff --git a/typescript/packages/jumble/integration/smoke-test.ts b/typescript/packages/jumble/integration/smoke-test.ts new file mode 100644 index 000000000..ff9729448 --- /dev/null +++ b/typescript/packages/jumble/integration/smoke-test.ts @@ -0,0 +1,28 @@ +import { launch } from "@astral/astral"; +import { assert } from "@std/assert"; +import { login } from "./utils.ts"; + +const FRONTEND_URL = "http://localhost:5173/"; + +async function main() { + const browser = await launch(); + console.log(`Waiting to open website at ${FRONTEND_URL}`); + const page = await browser.newPage(FRONTEND_URL); + console.log(`Opened website at ${FRONTEND_URL}`); + + await login(page); + + const anchor = await page.waitForSelector("nav a"); + assert( + (await anchor.innerText()) === "common-knowledge", + "Logged in and Common Knowledge title renders", + ); + await browser.close(); +} + +try { + await main(); +} catch (e) { + console.error(e); + Deno.exit(1); +} diff --git a/typescript/packages/jumble/integration/utils.ts b/typescript/packages/jumble/integration/utils.ts new file mode 100644 index 000000000..3cdf18eb8 --- /dev/null +++ b/typescript/packages/jumble/integration/utils.ts @@ -0,0 +1,41 @@ +import { Page } from "@astral/astral"; + +export const login = async (page: Page) => { + // First, see if any credential data is + // persisting. If so, destroy local data. + let buttons = await page.$$("button"); + for (const button of buttons) { + if ((await button.innerText()) === "Clear Saved Credentials") { + await button.click(); + } + } + + // Click the first button, "register" + let button = await page.$("button"); + await button!.click(); + + // Click the first button, "register with passphrase" + button = await page.$("button"); + await button!.click(); + + // Get the mnemonic from textarea. + let input = await page.$("textarea"); + const mnemonic = await input!.evaluate((textarea: HTMLInputElement) => + textarea.value + ); + + // Click the SECOND button, "continue to login" + buttons = await page.$$("button"); + await buttons[1]!.click(); + + // Paste the mnemonic in the input. + input = await page.$("input"); + await input!.evaluate( + (input: HTMLInputElement, mnemonic: string) => input.value = mnemonic, + { args: [mnemonic] }, + ); + + // Click the only button, "login" + button = await page.$("button"); + await button!.click(); +};