From 0775537d4c96869bfb9b58d81c8c25aa2723b70e Mon Sep 17 00:00:00 2001 From: Ben Stull Date: Fri, 12 Jun 2026 00:41:39 -0700 Subject: [PATCH] =?UTF-8?q?test(e2e):=20e2e=5Fimage=5Foutcomes=20=E2=80=94?= =?UTF-8?q?=20fixture=20image=20host,=20per-image=20outcomes=20+=20notice?= =?UTF-8?q?=20band=20(SD-0002=20=C2=A76.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- e2e/.gitignore | 1 + e2e/fixtures/images/good.png | Bin 0 -> 3689 bytes e2e/fixtures/images/tiny.png | Bin 0 -> 184 bytes e2e/fixtures/with-images.csv | 4 ++++ e2e/helpers.ts | 10 ++++++++++ e2e/image-host.mjs | 19 +++++++++++++++++++ e2e/serve.sh | 11 +++++++++++ e2e/tests/image-outcomes.spec.ts | 16 ++++++++++++++++ 8 files changed, 61 insertions(+) create mode 100644 e2e/fixtures/images/good.png create mode 100644 e2e/fixtures/images/tiny.png create mode 100644 e2e/fixtures/with-images.csv create mode 100644 e2e/image-host.mjs create mode 100644 e2e/tests/image-outcomes.spec.ts diff --git a/e2e/.gitignore b/e2e/.gitignore index cbc6ce8..1fcd284 100644 --- a/e2e/.gitignore +++ b/e2e/.gitignore @@ -1,4 +1,5 @@ node_modules/ .backend.log +.objectstore/ test-results/ playwright-report/ diff --git a/e2e/fixtures/images/good.png b/e2e/fixtures/images/good.png new file mode 100644 index 0000000000000000000000000000000000000000..0a50ae4834798afb3695c99b9d01af5eb4b3ee5c GIT binary patch literal 3689 zcmeAS@N?(olHy`uVBq!ia0y~yU{(NO4kn;Th|olP1_nL@PZ!6KiaBp@7z#ExFfbov zH}!B)+GJ4Z+7-*VGQY_7eK`Zei#rd+7#Nti85TT0$i&dWsl(7PA#(Q^H8j1r7;}3^|{H z#1R%T1_z%}#%Lgnri2lm3pjxNgj~6y*I5t%w#IJT46P;^om!6tQTFrRvab+itC0eB PAsIYf{an^LB{Ts5(2?~| literal 0 HcmV?d00001 diff --git a/e2e/fixtures/images/tiny.png b/e2e/fixtures/images/tiny.png new file mode 100644 index 0000000000000000000000000000000000000000..293a859482e7cb3be072b10dcbe4ede8be9cbd24 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMdQTU}kcv51&p8S*DDWKE5M=s! uJyT@IgN@lDPwq&bQt^94J9LNdPZdW!;|z}Sjmv;eVeoYIb6Mw<&;$TS2Sk7X literal 0 HcmV?d00001 diff --git a/e2e/fixtures/with-images.csv b/e2e/fixtures/with-images.csv new file mode 100644 index 0000000..4677cac --- /dev/null +++ b/e2e/fixtures/with-images.csv @@ -0,0 +1,4 @@ +Handle,Title,Image Src +good-lamp,Good Lamp,http://127.0.0.1:8799/good.png +tiny-lamp,Tiny Lamp,http://127.0.0.1:8799/tiny.png +gone-lamp,Gone Lamp,http://127.0.0.1:8799/missing.png diff --git a/e2e/helpers.ts b/e2e/helpers.ts index c911b97..fec4f28 100644 --- a/e2e/helpers.ts +++ b/e2e/helpers.ts @@ -83,6 +83,16 @@ export async function importGoodCsv(page: Page) { await expect(page.getByRole("heading", { level: 1, name: "good.csv" })).toBeVisible(); } +// Import with-images.csv (3 products, each with one Image Src) and confirm it. +// Mirrors importGoodCsv; callers continue from the run-detail screen, where the +// background image fetch progresses fetching_images → terminal. +export async function importWithImages(page: Page) { + await uploadFixture(page, "with-images.csv"); + await expect(page.getByRole("heading", { name: "Import preview — with-images.csv" })).toBeVisible(); + await page.getByRole("button", { name: "Import 3 products" }).click(); + await expect(page.getByRole("heading", { level: 1, name: "with-images.csv" })).toBeVisible(); +} + // Click an Export status option and capture the downloaded CSV's text + path. export async function exportCatalog(page: Page, label: string): Promise<{ text: string; path: string }> { // Open the
menu, then click the status option, capturing the download. diff --git a/e2e/image-host.mjs b/e2e/image-host.mjs new file mode 100644 index 0000000..c8ef1ac --- /dev/null +++ b/e2e/image-host.mjs @@ -0,0 +1,19 @@ +import { createServer } from "node:http"; +import { readFile } from "node:fs/promises"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const dir = join(dirname(fileURLToPath(import.meta.url)), "fixtures", "images"); +createServer(async (req, res) => { + const name = (req.url || "").replace(/^\//, "").split("?")[0]; + if (name === "good.png" || name === "tiny.png") { + try { + const buf = await readFile(join(dir, name)); + res.writeHead(200, { "Content-Type": "image/png" }); + res.end(buf); + return; + } catch { /* fallthrough */ } + } + res.writeHead(404); + res.end(); +}).listen(8799, "127.0.0.1"); diff --git a/e2e/serve.sh b/e2e/serve.sh index a6db7eb..c2c1cee 100755 --- a/e2e/serve.sh +++ b/e2e/serve.sh @@ -18,6 +18,17 @@ PY export ECOMM_DATABASE_URL="postgresql://ecomm:ecomm@localhost:5432/ecomm_e2e" export ECOMM_MAILER=log + +# Image pipeline (SLICE-7): local objectstore + a fixture image host on :8799. +# ECOMM_IMAGE_FETCH_ALLOW_PRIVATE lets the SSRF guard reach 127.0.0.1 — TEST-ONLY, +# never set in the deployed overlay. The &-backgrounded node host is a child of +# this serve process; Playwright tears the whole webServer group down between runs. +export ECOMM_OBJECTSTORE_KIND=local +export ECOMM_OBJECTSTORE_DIR="$repo_root/e2e/.objectstore" +export ECOMM_IMAGE_FETCH_ALLOW_PRIVATE=1 +rm -rf "$repo_root/e2e/.objectstore" +node "$repo_root/e2e/image-host.mjs" & + cd "$repo_root/backend" exec "$repo_root/.venv/bin/python" -m uvicorn app.main:app --port 8765 \ > "$repo_root/e2e/.backend.log" 2>&1 diff --git a/e2e/tests/image-outcomes.spec.ts b/e2e/tests/image-outcomes.spec.ts new file mode 100644 index 0000000..ce8736b --- /dev/null +++ b/e2e/tests/image-outcomes.spec.ts @@ -0,0 +1,16 @@ +import { expect, test } from "@playwright/test"; +import { gotoProducts, importWithImages, signUpWithStorefront } from "../helpers"; + +test("e2e_image_outcomes: per-image outcomes, problem rows, notice band", async ({ page }) => { + await signUpWithStorefront(page); + await gotoProducts(page); + await importWithImages(page); + // Run detail polls fetching_images → terminal; wait for the outcome summary. + await expect(page.getByText("1 fetched · 1 rejected · 1 failed")).toBeVisible({ timeout: 30_000 }); + // Problem images listed in the outcomes table. + await expect(page.getByText("tiny-lamp")).toBeVisible(); + await expect(page.getByText("gone-lamp")).toBeVisible(); + // Products page surfaces the aggregate notice. + await gotoProducts(page); + await expect(page.getByText(/products have image problems/)).toBeVisible(); +});