summaryrefslogtreecommitdiff
path: root/packages/integrations/web-vitals/test/basics.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/web-vitals/test/basics.test.js')
-rw-r--r--packages/integrations/web-vitals/test/basics.test.js118
1 files changed, 118 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(' ')
+ );
+ });
+});