大量修改二期功能清单内容
@@ -0,0 +1,72 @@
|
||||
// 二期第1段验收抓图:脚本入口(两选) / 设定卡 / 上传脚本 / 三视图双通道
|
||||
import { chromium } from "playwright";
|
||||
import { mkdirSync } from "node:fs";
|
||||
|
||||
const BASE = process.env.BASE || "http://127.0.0.1:5173";
|
||||
const API = process.env.API || "http://127.0.0.1:8010";
|
||||
const OUT = process.argv[2] || "shots-seg1";
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
const login = await (await fetch(`${API}/api/auth/login/`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
|
||||
})).json();
|
||||
const tok = login.token;
|
||||
const auth = { Authorization: `Token ${tok}`, "Content-Type": "application/json" };
|
||||
|
||||
// 要看的是「还没脚本」的空态,所以每次现建一个干净项目,抓完删掉
|
||||
const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || [];
|
||||
const target = await (await fetch(`${API}/api/projects/`, {
|
||||
method: "POST", headers: auth,
|
||||
body: JSON.stringify({ name: `二期验收抓图-${Date.now() % 100000}`, product: prods[0].id })
|
||||
})).json();
|
||||
console.log("project:", target.id, target.name);
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
|
||||
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), tok);
|
||||
const page = await ctx.newPage();
|
||||
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
|
||||
|
||||
async function shot(name, clip) {
|
||||
await page.screenshot({ path: `${OUT}/${name}.png`, ...(clip ? { clip } : { fullPage: true }) });
|
||||
console.log("shot", name);
|
||||
}
|
||||
|
||||
// ── Stage 1 · 脚本入口 ──
|
||||
await page.goto(`${BASE}/pipeline/${target.id}?st=1#stage-1`, { waitUntil: "networkidle", timeout: 40000 });
|
||||
await page.waitForTimeout(2000);
|
||||
const pane = await page.locator(".chat-pane").first().boundingBox();
|
||||
await shot("1-script-entries", pane);
|
||||
|
||||
// 点「脚本辅助生成」→ 设定卡
|
||||
await page.locator('.chat-mode[data-mode="ai"]').click();
|
||||
await page.waitForTimeout(700);
|
||||
await shot("2-assist-setup", await page.locator(".chat-pane").first().boundingBox());
|
||||
|
||||
// 返回 → 点「上传脚本」(会拉起选文件,用 filechooser 拦掉并喂一个 txt)
|
||||
await page.locator(".setup-foot .btn-ghost").first().click();
|
||||
await page.waitForTimeout(400);
|
||||
page.once("filechooser", async (chooser) => {
|
||||
await chooser.setFiles({
|
||||
name: "样稿.txt",
|
||||
mimeType: "text/plain",
|
||||
buffer: Buffer.from("钩子:还在为卡裆发愁?\n痛点:久坐一天就变形\n卖点:高弹面料不变形\nCTA:点小黄车", "utf-8")
|
||||
});
|
||||
});
|
||||
await page.locator('.chat-mode[data-mode="manual"]').click();
|
||||
await page.waitForTimeout(1500);
|
||||
await shot("3-upload-script", await page.locator(".chat-pane").first().boundingBox());
|
||||
|
||||
// ── Stage 2 · 三视图双通道 ──
|
||||
// 新建项目还没脚本,Stage 2 会停在「提取角色/场景」门口,看不到商品卡 → 换已跑过的演示项目
|
||||
const projects = (await (await fetch(`${API}/api/projects/?page_size=100`, { headers: auth })).json()).results || [];
|
||||
const demo = projects.find((p) => p.name.startsWith("演示")) || projects.find((p) => p.id !== target.id);
|
||||
await page.goto(`${BASE}/pipeline/${demo.id}?st=2#stage-2`, { waitUntil: "networkidle", timeout: 40000 });
|
||||
await page.waitForTimeout(3000);
|
||||
await shot("4-triview-channels", await page.locator(".prod-row").first().boundingBox());
|
||||
|
||||
await browser.close();
|
||||
await fetch(`${API}/api/projects/${target.id}/`, { method: "DELETE", headers: auth });
|
||||
console.log("DONE ->", OUT);
|
||||
@@ -0,0 +1,57 @@
|
||||
// 二期第2段端到端:真实调脚本 agent,验「总时长 = 各镜相加」「单镜 4–15 秒」「组合回写」
|
||||
const API = process.env.API || "http://127.0.0.1:8010";
|
||||
const login = await (await fetch(`${API}/api/auth/login/`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
|
||||
})).json();
|
||||
const auth = { Authorization: `Token ${login.token}`, "Content-Type": "application/json" };
|
||||
|
||||
const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || [];
|
||||
const project = await (await fetch(`${API}/api/projects/`, {
|
||||
method: "POST", headers: auth,
|
||||
body: JSON.stringify({ name: `二期第2段e2e-${Date.now() % 100000}`, product: prods[0].id })
|
||||
})).json();
|
||||
|
||||
const CASES = [
|
||||
{ total_duration: 40, presentation_format: "drama", video_structure: "pain" },
|
||||
{ total_duration: 15, presentation_format: "oral", video_structure: "contrast" },
|
||||
];
|
||||
|
||||
for (const params of CASES) {
|
||||
const res = await fetch(`${API}/api/projects/${project.id}/script-agent-stream/`, {
|
||||
method: "POST", headers: auth,
|
||||
body: JSON.stringify({ mode: "auto", aspect_ratio: "9:16", ...params })
|
||||
});
|
||||
let saved = null;
|
||||
const reader = res.body.getReader();
|
||||
const dec = new TextDecoder();
|
||||
let buf = "";
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buf += dec.decode(value, { stream: true });
|
||||
// SSE 帧只有 data: 行,事件类型在 JSON 的 type 字段里
|
||||
for (const chunk of buf.split("\n\n").slice(0, -1)) {
|
||||
const data = /^data:\s*([\s\S]+)$/m.exec(chunk)?.[1];
|
||||
if (!data) continue;
|
||||
let ev;
|
||||
try { ev = JSON.parse(data); } catch { continue; }
|
||||
if (ev.type === "draft") saved = ev.draft;
|
||||
if (ev.type === "error") console.error(" error:", data);
|
||||
}
|
||||
buf = buf.slice(buf.lastIndexOf("\n\n") + 2);
|
||||
}
|
||||
const draft = saved || {};
|
||||
const segs = draft.segments || [];
|
||||
const durs = segs.map((s) => s.duration);
|
||||
const sum = durs.reduce((a, b) => a + b, 0);
|
||||
console.log(
|
||||
`请求 ${params.total_duration}s ${params.presentation_format}×${params.video_structure}`,
|
||||
"→ 出稿", draft?.total_duration, "s ·", segs.length, "镜 ·", JSON.stringify(durs), "加总", sum,
|
||||
"|", draft?.presentation_format, draft?.video_structure,
|
||||
sum === params.total_duration && durs.every((d) => d >= 4 && d <= 15) ? "✅" : "❌"
|
||||
);
|
||||
}
|
||||
|
||||
await fetch(`${API}/api/projects/${project.id}/`, { method: "DELETE", headers: auth });
|
||||
@@ -0,0 +1,65 @@
|
||||
// 二期第2段验收抓图:设定卡「表现形式 × 视频结构 × 人物 × 时长」+ 组合联动 + 时长提醒
|
||||
import { chromium } from "playwright";
|
||||
import { mkdirSync } from "node:fs";
|
||||
|
||||
const BASE = process.env.BASE || "http://127.0.0.1:5173";
|
||||
const API = process.env.API || "http://127.0.0.1:8010";
|
||||
const OUT = process.argv[2] || "shots-seg2";
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
const login = await (await fetch(`${API}/api/auth/login/`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
|
||||
})).json();
|
||||
const tok = login.token;
|
||||
const auth = { Authorization: `Token ${tok}`, "Content-Type": "application/json" };
|
||||
|
||||
// 设定卡只在「还没脚本」的空态出现,所以现建一个干净项目,抓完删掉
|
||||
const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || [];
|
||||
const target = await (await fetch(`${API}/api/projects/`, {
|
||||
method: "POST", headers: auth,
|
||||
body: JSON.stringify({ name: `二期第2段抓图-${Date.now() % 100000}`, product: prods[0].id })
|
||||
})).json();
|
||||
console.log("project:", target.id, "product:", prods[0].title, "/", prods[0].category);
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
|
||||
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), tok);
|
||||
const page = await ctx.newPage();
|
||||
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
|
||||
|
||||
const pane = () => page.locator(".chat-pane").first();
|
||||
async function shot(name) {
|
||||
await page.screenshot({ path: `${OUT}/${name}.png`, clip: await pane().boundingBox() });
|
||||
console.log("shot", name);
|
||||
}
|
||||
|
||||
await page.goto(`${BASE}/pipeline/${target.id}?st=1#stage-1`, { waitUntil: "networkidle", timeout: 40000 });
|
||||
await page.waitForTimeout(2000);
|
||||
await page.locator('.chat-mode[data-mode="ai"]').click();
|
||||
await page.waitForTimeout(700);
|
||||
await shot("1-setup-default");
|
||||
|
||||
const selects = page.locator(".setup-card .setup-select");
|
||||
const structureOptions = async () =>
|
||||
selects.nth(1).locator("option").evaluateAll((els) => els.map((e) => e.textContent));
|
||||
|
||||
console.log("默认结构可选:", await structureOptions());
|
||||
|
||||
// 1.8 组合联动:选「短剧」后,视频结构里应当没有「测评验证」
|
||||
await selects.nth(0).selectOption("drama");
|
||||
await page.waitForTimeout(400);
|
||||
console.log("短剧下结构可选:", await structureOptions());
|
||||
await shot("2-setup-drama-filtered");
|
||||
|
||||
// 时长调到最短 → 结构最短时长提醒(honey 色 .setup-rec.warn)
|
||||
await selects.nth(0).selectOption("oral");
|
||||
await selects.nth(1).selectOption("review");
|
||||
await selects.nth(3).selectOption("5");
|
||||
await page.waitForTimeout(400);
|
||||
await shot("3-setup-duration-warn");
|
||||
|
||||
await browser.close();
|
||||
await fetch(`${API}/api/projects/${target.id}/`, { method: "DELETE", headers: auth });
|
||||
console.log("DONE ->", OUT);
|
||||
@@ -0,0 +1,65 @@
|
||||
// 二期第5段端到端:上传视频 → 拆解稿 → 点确定 → 真出结构化脚本;并验「来源」记成「上传视频提炼」
|
||||
import { chromium } from "playwright";
|
||||
import { mkdirSync, existsSync } from "node:fs";
|
||||
|
||||
const BASE = process.env.BASE || "http://127.0.0.1:5173";
|
||||
const API = process.env.API || "http://127.0.0.1:8010";
|
||||
const OUT = process.argv[2] || "shots-seg5";
|
||||
const VIDEO = process.env.VIDEO || "/tmp/_probe_tshirt.mp4";
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
if (!existsSync(VIDEO)) throw new Error(`测试视频不存在: ${VIDEO}`);
|
||||
|
||||
const login = await (await fetch(`${API}/api/auth/login/`, {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
|
||||
})).json();
|
||||
const tok = login.token;
|
||||
const auth = { Authorization: `Token ${tok}`, "Content-Type": "application/json" };
|
||||
|
||||
const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || [];
|
||||
const target = await (await fetch(`${API}/api/projects/`, {
|
||||
method: "POST", headers: auth,
|
||||
body: JSON.stringify({ name: `二期第5段E2E-${Date.now() % 100000}`, product: prods[0].id })
|
||||
})).json();
|
||||
console.log("project:", target.id, "| product:", prods[0].title);
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
|
||||
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), tok);
|
||||
const page = await ctx.newPage();
|
||||
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
|
||||
|
||||
await page.goto(`${BASE}/pipeline/${target.id}?st=1#stage-1`, { waitUntil: "networkidle", timeout: 40000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const chooser = page.waitForEvent("filechooser");
|
||||
await page.locator('.chat-mode[data-mode="video"]').click();
|
||||
await (await chooser).setFiles(VIDEO);
|
||||
await page.locator(".chat-attach-row .chip").first().waitFor({ timeout: 180000 });
|
||||
console.log("拆解稿字数:", (await page.locator("#chat-textarea").inputValue()).length);
|
||||
|
||||
// 时长跟参考视频对齐(22 秒 → 20 秒档),再点确定生成
|
||||
await page.locator(".setup-card .setup-select").nth(3).selectOption("20");
|
||||
await page.locator(".setup-card .btn-primary").click();
|
||||
console.log("已点确定,等脚本生成…");
|
||||
|
||||
await page.locator(".shot-card, .shot-row").first().waitFor({ timeout: 240000 });
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
const project = await (await fetch(`${API}/api/projects/${target.id}/`, { headers: auth })).json();
|
||||
const script = (project.script_versions || []).find((v) => v.is_adopted) || (project.script_versions || [])[0];
|
||||
console.log("脚本来源(后端记录):", script?.source);
|
||||
console.log("镜数:", script?.segments?.length, "| 各镜时长:", (script?.segments || []).map((s) => s.duration_seconds).join("/"));
|
||||
console.log("总时长:", (script?.segments || []).reduce((a, s) => a + s.duration_seconds, 0));
|
||||
console.log("第1镜旁白:", (script?.segments || [])[0]?.narration);
|
||||
console.log("第1镜画面:", ((script?.segments || [])[0]?.visual_prompt || "").slice(0, 90));
|
||||
|
||||
const brief = await page.locator("#brief-source").textContent();
|
||||
console.log("来源徽标(前端):", brief);
|
||||
|
||||
await page.screenshot({ path: `${OUT}/5-generated-script.png`, fullPage: false });
|
||||
console.log("shot 5-generated-script");
|
||||
|
||||
await browser.close();
|
||||
await fetch(`${API}/api/projects/${target.id}/`, { method: "DELETE", headers: auth });
|
||||
console.log("DONE");
|
||||
@@ -0,0 +1,77 @@
|
||||
// 二期第5段验收:1.11 上传视频提炼 —— 三入口、真视频拆解、拆解稿可编辑、生成后来源徽标
|
||||
import { chromium } from "playwright";
|
||||
import { mkdirSync, existsSync } from "node:fs";
|
||||
|
||||
const BASE = process.env.BASE || "http://127.0.0.1:5173";
|
||||
const API = process.env.API || "http://127.0.0.1:8010";
|
||||
const OUT = process.argv[2] || "shots-seg5";
|
||||
const VIDEO = process.env.VIDEO || "/tmp/_probe_tshirt.mp4";
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
if (!existsSync(VIDEO)) throw new Error(`测试视频不存在: ${VIDEO}`);
|
||||
|
||||
const login = await (await fetch(`${API}/api/auth/login/`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
|
||||
})).json();
|
||||
const tok = login.token;
|
||||
const auth = { Authorization: `Token ${tok}`, "Content-Type": "application/json" };
|
||||
|
||||
const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || [];
|
||||
const target = await (await fetch(`${API}/api/projects/`, {
|
||||
method: "POST", headers: auth,
|
||||
body: JSON.stringify({ name: `二期第5段抓图-${Date.now() % 100000}`, product: prods[0].id })
|
||||
})).json();
|
||||
console.log("project:", target.id, "product:", prods[0].title);
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
|
||||
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), tok);
|
||||
const page = await ctx.newPage();
|
||||
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
|
||||
|
||||
const pane = () => page.locator(".chat-pane").first();
|
||||
async function shot(name) {
|
||||
await page.screenshot({ path: `${OUT}/${name}.png`, clip: await pane().boundingBox() });
|
||||
console.log("shot", name);
|
||||
}
|
||||
|
||||
await page.goto(`${BASE}/pipeline/${target.id}?st=1#stage-1`, { waitUntil: "networkidle", timeout: 40000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// ① 空态三入口
|
||||
const modes = await page.locator(".chat-mode").allTextContents();
|
||||
console.log("入口:", modes);
|
||||
if (modes.length !== 3) throw new Error(`应有三个入口,实际 ${modes.length}`);
|
||||
await shot("1-three-entries");
|
||||
|
||||
// ② 选「上传视频提炼」→ 自动拉起选文件,喂真视频
|
||||
const chooser = page.waitForEvent("filechooser");
|
||||
await page.locator('.chat-mode[data-mode="video"]').click();
|
||||
await (await chooser).setFiles(VIDEO);
|
||||
await page.waitForTimeout(800);
|
||||
await shot("2-digesting");
|
||||
|
||||
// ③ 等拆解回来(抽帧 + 读帧,真调模型,给足 3 分钟)
|
||||
await page.locator(".chat-attach-row .chip").first().waitFor({ timeout: 180000 });
|
||||
await page.waitForTimeout(600);
|
||||
const digest = await page.locator("#chat-textarea").inputValue();
|
||||
console.log("拆解稿字数:", digest.length);
|
||||
console.log("镜数:", (digest.match(/【第 \d+ 镜】/g) || []).length, "| 有存疑节:", digest.includes("【拆解存疑】"));
|
||||
console.log("----- 前 260 字 -----\n" + digest.slice(0, 260));
|
||||
await shot("3-digest-in-editor");
|
||||
|
||||
// ④ 拆解稿必须可编辑 —— 这是 1.11 的硬要求(AI 拆解有已知毛病)
|
||||
await page.locator("#chat-textarea").fill(digest + "\n【人工补充】第 1 镜改成室内暖光。");
|
||||
await page.waitForTimeout(300);
|
||||
const edited = await page.locator("#chat-textarea").inputValue();
|
||||
console.log("可编辑:", edited.includes("【人工补充】"));
|
||||
await shot("4-manually-edited");
|
||||
|
||||
// ⑤ 设定卡(表现形式/结构/人物/时长)照常出现,确定后进生成
|
||||
console.log("设定卡可见:", await page.locator(".setup-card").isVisible());
|
||||
console.log("设定卡引导语:", await page.locator(".setup-lead").textContent());
|
||||
|
||||
await browser.close();
|
||||
await fetch(`${API}/api/projects/${target.id}/`, { method: "DELETE", headers: auth });
|
||||
console.log("DONE ->", OUT);
|
||||
@@ -0,0 +1,71 @@
|
||||
// 二期第6段验收抓图:自由创作「引用平台素材」弹窗(资产库 / 模特库 / 商品库三 tab)
|
||||
import { chromium } from "playwright";
|
||||
import { mkdirSync } from "node:fs";
|
||||
|
||||
const BASE = process.env.BASE || "http://127.0.0.1:5173";
|
||||
const API = process.env.API || "http://127.0.0.1:8010";
|
||||
const OUT = process.argv[2] || "shots-seg6";
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
const login = await (await fetch(`${API}/api/auth/login/`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
|
||||
})).json();
|
||||
const tok = login.token;
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 940 }, deviceScaleFactor: 2 });
|
||||
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), tok);
|
||||
const page = await ctx.newPage();
|
||||
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
|
||||
|
||||
async function shot(name, locator) {
|
||||
const box = locator ? await locator.boundingBox() : null;
|
||||
await page.screenshot({ path: `${OUT}/${name}.png`, ...(box ? { clip: box } : {}) });
|
||||
console.log("shot", name);
|
||||
}
|
||||
|
||||
await page.goto(`${BASE}/free-create`, { waitUntil: "networkidle", timeout: 40000 });
|
||||
await page.waitForTimeout(1500);
|
||||
await shot("0-page");
|
||||
|
||||
await page.getByRole("button", { name: "引用平台素材", exact: true }).click();
|
||||
await page.waitForTimeout(1200);
|
||||
const modal = page.locator(".fc-lib-modal");
|
||||
await shot("1-assets-tab", modal);
|
||||
|
||||
const cards = await page.locator(".fc-lib-asset").count();
|
||||
const pickable = await page.locator(".fc-lib-asset.pickable").count();
|
||||
const pills = await page.locator(".fc-lib-status").allTextContents();
|
||||
console.log(`资产库:${cards} 张,可引用 ${pickable} 张,拦截徽标:`, [...new Set(pills)]);
|
||||
|
||||
// 4.2 拦截态:上传来源且没审过的卡应当有「待送审」徽标 + 送审按钮,且不可点选
|
||||
const pending = page.locator(".fc-lib-asset", { has: page.locator(".fc-lib-status") }).first();
|
||||
if (await pending.count()) {
|
||||
await pending.scrollIntoViewIfNeeded();
|
||||
await page.waitForTimeout(300);
|
||||
await shot("1b-review-gate", pending);
|
||||
console.log("拦截卡有送审按钮:", await pending.locator(".fc-lib-review-btn").count() > 0);
|
||||
console.log("拦截卡可点选:", await pending.evaluate((el) => el.classList.contains("pickable")));
|
||||
}
|
||||
|
||||
await page.locator(".fc-lib-tabs .tab", { hasText: "模特库" }).click();
|
||||
await page.waitForTimeout(1200);
|
||||
await shot("2-models-tab", modal);
|
||||
console.log("模特卡:", await page.locator(".fc-lib-asset").count());
|
||||
|
||||
await page.locator(".fc-lib-tabs .tab", { hasText: "商品库" }).click();
|
||||
await page.waitForTimeout(1200);
|
||||
await shot("3-products-tab", modal);
|
||||
const products = await page.locator(".fc-lib-asset").count();
|
||||
console.log("商品卡:", products);
|
||||
|
||||
if (products > 0) {
|
||||
await page.locator(".fc-lib-asset").first().click();
|
||||
await page.waitForTimeout(1500);
|
||||
await shot("4-product-drilldown", modal);
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
console.log("DONE ->", OUT);
|
||||
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 483 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 474 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 50 KiB |