test(e2e): e2e_image_outcomes — fixture image host, per-image outcomes + notice band (SD-0002 §6.8)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
node_modules/
|
||||
.backend.log
|
||||
.objectstore/
|
||||
test-results/
|
||||
playwright-report/
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 184 B |
@@ -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
|
||||
|
@@ -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 <details> menu, then click the status option, capturing the download.
|
||||
|
||||
@@ -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");
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
Reference in New Issue
Block a user