metatron-open-seo/src/server/lib/dataforseoLighthousePayload.test.ts
Ben Senescu 638f5a6602 refactor: move lighthouse audits to dataforseo (#43)
* refactor: move lighthouse audits to dataforseo

* chore: remove obsolete audit settings modal

* refactor: rename psi flows to lighthouse

* save

* refactor: simplify audit lighthouse storage flow

* fix: separate lighthouse metrics from actionable audits

* refactor: remove redundant audit project inputs

* feat: redesign lighthouse issues screen with score gauges and table layout

Replace flat score cards with circular SVG gauges, condense metrics into
a compact grid, and switch issue list from cards to an expandable table
with fixed column widths.

* test: harden lighthouse regression coverage

* fix: restore project-scoped audit inputs

* refactor: simplify lighthouse payload handling

* refactor: inline lighthouse server handlers

* refactor: share audit workflow types

* refactor: simplify lighthouse payload flows

* save

* refactor: drop project pagespeed api key

* fix: restore lighthouse issues loading with resilient project context

* fix: restore audit issues back navigation

* refactor: simplify project context and lighthouse error handling

* fix: tolerate DataForSEO lighthouse payload drift

* refactor: route audit lighthouse through dataforseo client
2026-03-26 20:25:27 -04:00

251 lines
7.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { parseDataforseoLighthousePayload } from "@/server/lib/dataforseoLighthousePayload";
import { readStoredLighthousePayload } from "@/server/lib/lighthousePayload";
describe("parseDataforseoLighthousePayload", () => {
it("stores only issue-level lighthouse data and key metadata", () => {
const parsed = parseDataforseoLighthousePayload(
{
status_code: 20000,
status_message: "Ok.",
tasks: [
{
id: "task-1",
status_code: 20000,
status_message: "Ok.",
cost: 0.00425,
result: [
{
requestedUrl: "https://everyapp.dev/",
finalUrl: "https://everyapp.dev/",
lighthouseVersion: "12.2.0",
categories: {
performance: {
score: 0.54,
auditRefs: [{ id: "unused-javascript" }],
},
accessibility: {
score: 0.93,
auditRefs: [{ id: "accesskeys" }],
},
"best-practices": { score: 0.79, auditRefs: [] },
seo: { score: 0.92, auditRefs: [] },
},
audits: {
"unused-javascript": {
title: "Reduce unused JavaScript",
description: "Trim dead code.",
score: 0,
scoreDisplayMode: "metricSavings",
displayValue: "Potential savings of 188 KiB",
numericValue: 193002,
details: {
overallSavingsMs: 1270,
overallSavingsBytes: 193002,
items: [
{
url: "https://cdn.example.com/app.js",
wastedBytes: 193002,
},
],
},
},
accesskeys: {
title: "`[accesskey]` values are unique",
description: "Access keys should not conflict.",
score: null,
scoreDisplayMode: "error",
},
interactive: {
title: "Time to Interactive",
description: "Time until the page becomes interactive.",
score: 0.13,
scoreDisplayMode: "numeric",
displayValue: "12.8 s",
numericValue: 12800,
},
},
},
],
},
],
},
{
url: "https://everyapp.dev/",
strategy: "mobile",
},
);
const { report } = readStoredLighthousePayload(JSON.stringify(parsed));
expect(parsed.metrics.timeToInteractive.displayValue).toBe("12.8 s");
expect(parsed).toMatchObject({
version: 2,
source: "dataforseo-lighthouse",
hasIssueDetails: true,
metadata: {
requestedUrl: "https://everyapp.dev/",
finalUrl: "https://everyapp.dev/",
strategy: "mobile",
lighthouseVersion: "12.2.0",
taskId: "task-1",
cost: 0.00425,
},
scores: {
performance: 54,
accessibility: 93,
"best-practices": 79,
seo: 92,
},
metrics: {
timeToInteractive: {
score: 13,
displayValue: "12.8 s",
numericValue: 12800,
},
},
});
expect(parsed.issues).toHaveLength(1);
expect(parsed.issues).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ auditKey: "interactive" }),
]),
);
expect(parsed).not.toHaveProperty("lighthouseResult");
expect(report.hasIssueDetails).toBe(true);
expect(report.issues).toEqual([
expect.objectContaining({
auditKey: "unused-javascript",
category: "performance",
impactMs: 1270,
impactBytes: 193002,
title: "Reduce unused JavaScript",
}),
]);
});
it("throws when the lighthouse response has no category scores", () => {
expect(() =>
parseDataforseoLighthousePayload(
{
status_code: 20000,
status_message: "Ok.",
tasks: [
{
id: "task-1",
status_code: 20000,
status_message: "Ok.",
cost: 0.00425,
result: [
{
requestedUrl:
"https://everyapp.dev/blog/category/cyber-security",
finalUrl:
"https://everyapp.dev/blog/category/cyber-security/",
lighthouseVersion: "12.2.0",
categories: {
performance: { score: null, auditRefs: [] },
accessibility: { score: null, auditRefs: [] },
"best-practices": { score: null, auditRefs: [] },
seo: { score: null, auditRefs: [] },
},
audits: {},
},
],
},
],
},
{
url: "https://everyapp.dev/blog/category/cyber-security",
strategy: "desktop",
},
),
).toThrow("DataForSEO Lighthouse returned no category scores");
});
it("throws when DataForSEO returns a non-success task status", () => {
expect(() =>
parseDataforseoLighthousePayload(
{
status_code: 20000,
status_message: "Ok.",
tasks: [
{
id: "task-1",
status_code: 40501,
status_message: "Insufficient credits",
result: [],
},
],
},
{
url: "https://everyapp.dev/",
strategy: "mobile",
},
),
).toThrow("Insufficient credits");
});
it("includes schema details when the payload shape is invalid", () => {
expect(() =>
parseDataforseoLighthousePayload(null, {
url: "https://everyapp.dev/",
strategy: "mobile",
}),
).toThrow("<root>");
});
it("accepts audits whose details.items is an object", () => {
expect(() =>
parseDataforseoLighthousePayload(
{
status_code: 20000,
status_message: "Ok.",
tasks: [
{
id: "task-1",
status_code: 20000,
status_message: "Ok.",
cost: 0.00425,
result: [
{
requestedUrl: "https://everyapp.dev/",
finalUrl: "https://everyapp.dev/",
lighthouseVersion: "12.2.0",
categories: {
performance: {
score: 0.54,
auditRefs: [{ id: "document-latency-insight" }],
},
accessibility: { score: 0.93, auditRefs: [] },
"best-practices": { score: 0.79, auditRefs: [] },
seo: { score: 0.92, auditRefs: [] },
},
audits: {
"document-latency-insight": {
title: "Document request latency",
description: "Latency insight.",
score: 0,
scoreDisplayMode: "informative",
details: {
items: {
latencyMs: 120,
},
},
},
},
},
],
},
],
},
{
url: "https://everyapp.dev/",
strategy: "mobile",
},
),
).not.toThrow();
});
});