diff options
Diffstat (limited to 'packages/integrations/web-vitals/test')
6 files changed, 194 insertions, 0 deletions
diff --git a/packages/integrations/web-vitals/test/basics.test.js b/packages/integrations/web-vitals/test/basics.test.js new file mode 100644 index 000000000..937619b48 --- /dev/null +++ b/packages/integrations/web-vitals/test/basics.test.js @@ -0,0 +1,118 @@ +// @ts-check + +import * as assert from 'node:assert/strict'; +import { after, before, beforeEach, describe, it } from 'node:test'; +import { parseHTML } from 'linkedom'; +import { loadFixture } from './test-utils.js'; + +/** + * @template {Record<K, (...args: any[]) => void>} T + * @template {keyof T} K + */ +class MockFunction { + /** @type {Parameters<T[K]>[]} */ + calls = []; + + /** + * @param {T} object + * @param {K} property + */ + constructor(object, property) { + this.object = object; + this.property = property; + this.original = object[property]; + object[property] = /** @param {Parameters<T[K]>} args */ (...args) => { + this.calls.push(args); + }; + } + restore() { + this.object[this.property] = this.original; + } + reset() { + this.calls = []; + } +} + +describe('Web Vitals integration basics', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + /** @type {import('./test-utils').DevServer} */ + let devServer; + /** @type {MockFunction<Console, 'error'>} */ + let consoleErrorMock; + + before(async () => { + consoleErrorMock = new MockFunction(console, 'error'); + fixture = await loadFixture({ root: './fixtures/basics/' }); + devServer = await fixture.startDevServer({}); + }); + + after(async () => { + consoleErrorMock.restore(); + await devServer.stop(); + }); + + beforeEach(() => { + consoleErrorMock.reset(); + }); + + it('adds a meta tag to the page', async () => { + const html = await fixture.fetch('/', {}).then((res) => res.text()); + const { document } = parseHTML(html); + const meta = document.querySelector('head > meta[name="x-astro-vitals-route"]'); + assert.ok(meta); + assert.equal(meta.getAttribute('content'), '/'); + }); + + it('adds a meta tag using the route pattern to the page', async () => { + const html = await fixture.fetch('/test', {}).then((res) => res.text()); + const { document } = parseHTML(html); + const meta = document.querySelector('head > meta[name="x-astro-vitals-route"]'); + assert.ok(meta); + assert.equal(meta.getAttribute('content'), '/[dynamic]'); + }); + + it('returns a 200 response even when bad data is sent to the injected endpoint', async () => { + { + // bad data + const res = await fixture.fetch('/_web-vitals', { method: 'POST', body: 'garbage' }); + assert.equal(res.status, 200); + } + { + // no data + const res = await fixture.fetch('/_web-vitals', { method: 'POST', body: '[]' }); + assert.equal(res.status, 200); + } + assert.equal(consoleErrorMock.calls.length, 2); + }); + + it('validates data sent to the injected endpoint with Zod', async () => { + const res = await fixture.fetch('/_web-vitals', { method: 'POST', body: '[{}]' }); + assert.equal(res.status, 200); + const call = consoleErrorMock.calls[0][0]; + assert.ok(call instanceof Error); + assert.equal(call.name, 'ZodError'); + }); + + it('inserts data via the injected endpoint', async () => { + const res = await fixture.fetch('/_web-vitals', { + method: 'POST', + body: JSON.stringify([ + { + pathname: '/', + route: '/', + name: 'CLS', + id: 'v3-1711484350895-3748043125387', + value: 0, + rating: 'good', + }, + ]), + }); + assert.equal(res.status, 200); + assert.equal( + consoleErrorMock.calls.length, + 0, + 'Endpoint logged errors:\n' + consoleErrorMock.calls[0]?.join(' ') + ); + }); +}); diff --git a/packages/integrations/web-vitals/test/fixtures/basics/astro.config.mjs b/packages/integrations/web-vitals/test/fixtures/basics/astro.config.mjs new file mode 100644 index 000000000..42bfa6f66 --- /dev/null +++ b/packages/integrations/web-vitals/test/fixtures/basics/astro.config.mjs @@ -0,0 +1,14 @@ +import db from '@astrojs/db'; +import node from '@astrojs/node'; +import webVitals from '@astrojs/web-vitals'; +import { defineConfig } from 'astro/config'; + +// https://astro.build/config +export default defineConfig({ + integrations: [db(), webVitals()], + output: 'hybrid', + adapter: node({ mode: 'standalone' }), + devToolbar: { + enabled: false, + }, +}); diff --git a/packages/integrations/web-vitals/test/fixtures/basics/package.json b/packages/integrations/web-vitals/test/fixtures/basics/package.json new file mode 100644 index 000000000..25ab0abc1 --- /dev/null +++ b/packages/integrations/web-vitals/test/fixtures/basics/package.json @@ -0,0 +1,16 @@ +{ + "name": "@test/web-vitals", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview" + }, + "dependencies": { + "@astrojs/db": "workspace:*", + "@astrojs/node": "workspace:*", + "@astrojs/web-vitals": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/web-vitals/test/fixtures/basics/src/pages/[dynamic].astro b/packages/integrations/web-vitals/test/fixtures/basics/src/pages/[dynamic].astro new file mode 100644 index 000000000..36c7c50e6 --- /dev/null +++ b/packages/integrations/web-vitals/test/fixtures/basics/src/pages/[dynamic].astro @@ -0,0 +1,19 @@ +--- +import type { GetStaticPaths } from "astro"; +export const getStaticPaths = (() => { + return [{ params: { dynamic: 'test' } }]; +}) satisfies GetStaticPaths; +--- + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Web Vitals basics — dynamic route test</title> +</head> +<body> + <h1>Web Vitals basics</h1> + <p>Dynamic route test</p> +</body> +</html> diff --git a/packages/integrations/web-vitals/test/fixtures/basics/src/pages/index.astro b/packages/integrations/web-vitals/test/fixtures/basics/src/pages/index.astro new file mode 100644 index 000000000..06ddd6565 --- /dev/null +++ b/packages/integrations/web-vitals/test/fixtures/basics/src/pages/index.astro @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Web Vitals basics test</title> +</head> +<body> + <h1>Web Vitals basics test</h1> +</body> +</html> diff --git a/packages/integrations/web-vitals/test/test-utils.js b/packages/integrations/web-vitals/test/test-utils.js new file mode 100644 index 000000000..8dd4d970b --- /dev/null +++ b/packages/integrations/web-vitals/test/test-utils.js @@ -0,0 +1,16 @@ +import { loadFixture as baseLoadFixture } from '../../../astro/test/test-utils.js'; + +/** @typedef {import('../../../astro/test/test-utils').Fixture} Fixture */ +/** @typedef {import('../../../astro/test/test-utils').DevServer} DevServer */ + +/** @type {typeof import('../../../astro/test/test-utils.js')['loadFixture']} */ +export function loadFixture(inlineConfig) { + if (!inlineConfig?.root) throw new Error("Must provide { root: './fixtures/...' }"); + + // resolve the relative root (i.e. "./fixtures/tailwindcss") to a full filepath + // without this, the main `loadFixture` helper will resolve relative to `packages/astro/test` + return baseLoadFixture({ + ...inlineConfig, + root: new URL(inlineConfig.root, import.meta.url).toString(), + }); +} |