diff --git a/.github/scripts/test-plan.mjs b/.github/scripts/test-plan.mjs index 835e6712c2..1b4f1aca51 100644 --- a/.github/scripts/test-plan.mjs +++ b/.github/scripts/test-plan.mjs @@ -145,6 +145,12 @@ const RULES = [ lanes: ["unit", "integration", "browser", "browser_embeds"], unitMode: "full", }, + { + name: "marketplace image fixture", + matches: (path) => path.startsWith("packages/plugins/marketplace-test/"), + lanes: ["unit", "integration", "e2e_table"], + unitMode: "full", + }, { name: "plugin fixtures", matches: (path) => path.startsWith("packages/plugins/"), diff --git a/.github/scripts/test-plan.test.mjs b/.github/scripts/test-plan.test.mjs index 7394162529..dd0bf48221 100644 --- a/.github/scripts/test-plan.test.mjs +++ b/.github/scripts/test-plan.test.mjs @@ -78,6 +78,19 @@ describe("test plan", () => { assert.equal(plan.visual, false); }); + it("runs cross-browser image coverage for marketplace fixture changes", () => { + const plan = createTestPlan(["packages/plugins/marketplace-test/src/plugin.ts"]); + + assert.equal(plan.unit, true); + assert.equal(plan.integration, true); + assert.equal(plan.e2e_table, true); + assert.equal(plan.unit_mode, "full"); + assert.equal(plan.full, false); + + const otherFixture = createTestPlan(["packages/plugins/sandboxed-test/src/plugin.ts"]); + assert.equal(otherFixture.e2e_table, false); + }); + it("identifies changed package roots for focused unit tests", () => { const plan = createTestPlan([ "packages/marketplace/src/index.ts", diff --git a/e2e/tests/plugin-fixture-image.spec.ts b/e2e/tests/plugin-fixture-image.spec.ts new file mode 100644 index 0000000000..98fffe53de --- /dev/null +++ b/e2e/tests/plugin-fixture-image.spec.ts @@ -0,0 +1,28 @@ +import { expect, test } from "../fixtures/index.js"; + +test("serves a decodable marketplace fixture PNG @table-cross-engine", async ({ + page, + request, +}) => { + const response = await request.get("/_emdash/api/plugins/marketplace-test/fixture-image"); + expect(response.ok()).toBe(true); + expect(response.headers()["content-type"]).toContain("image/png"); + + const dimensions = await page.evaluate( + async (bytes) => { + const image = new Image(); + const url = URL.createObjectURL(new Blob([new Uint8Array(bytes)], { type: "image/png" })); + try { + image.src = url; + await image.decode(); + return { width: image.naturalWidth, height: image.naturalHeight }; + } finally { + URL.revokeObjectURL(url); + } + }, + [...(await response.body())], + ); + + expect(dimensions.width).toBeGreaterThan(0); + expect(dimensions.height).toBeGreaterThan(0); +}); diff --git a/packages/plugins/marketplace-test/src/plugin.ts b/packages/plugins/marketplace-test/src/plugin.ts index b4972ca7cc..d5d60cca74 100644 --- a/packages/plugins/marketplace-test/src/plugin.ts +++ b/packages/plugins/marketplace-test/src/plugin.ts @@ -26,7 +26,7 @@ const deleteRecordMcpOutput = z.object({ deleted: z.boolean() }); const fixturePng = new Uint8Array([ 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 4, 0, 0, 0, 181, 28, 12, 2, 0, 0, 0, 11, 73, 68, 65, 84, 120, 218, 99, 252, 255, 31, 0, 3, 3, 2, 0, 239, - 191, 105, 69, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130, + 162, 167, 91, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130, ]); type RedirectCreateProbeInput = RedirectCreateInput & { auto?: unknown }; diff --git a/playwright.table.config.ts b/playwright.table.config.ts index 2e95fc7b8c..d166137442 100644 --- a/playwright.table.config.ts +++ b/playwright.table.config.ts @@ -3,7 +3,7 @@ import { defineConfig, devices } from "@playwright/test"; import baseConfig from "./playwright.config"; export default defineConfig(baseConfig, { - testMatch: "portable-text-table.spec.ts", + testMatch: ["portable-text-table.spec.ts", "plugin-fixture-image.spec.ts"], testIgnore: [], timeout: 60_000, projects: [