import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; import { runAbb120Suite } from "../../src/suites/abb120Suite.js"; import { parseGrl } from "../../src/grl/parser/index.js"; import { compileSemanticProgram } from "../../src/grl/semantic/index.js"; import { ABB_IRB120_ZERO_JOINTS } from "../fixtures/abbIrb120.js"; const WEB_ROOT = join(dirname(fileURLToPath(import.meta.url)), "../.."); const PROGRAM_DIR = join(WEB_ROOT, "tests", "fixtures", "abb120", "programs"); const APP_DIR = join(WEB_ROOT, "app"); const PROGRAM_NAMES = [ "A120_00_Smoke.grl", "A120_10_JointPickPlace.grl", "A120_20_CartesianBlend.grl", "A120_30_IOWaitPulse.grl", "A120_40_ErrorDiagnostics.grl", "A120_50_OperationProcess.grl" ]; function loadPrograms() { return PROGRAM_NAMES.map((name) => ({ name, text: readFileSync(join(PROGRAM_DIR, name), "utf8") })); } describe("ABB120 suite artifacts", () => { it("ships six parseable GRL programs with source maps and KDL bridge requests", () => { for (const program of loadPrograms()) { const ast = parseGrl(program.text); const ir = compileSemanticProgram(ast, { startJoints: [...ABB_IRB120_ZERO_JOINTS], sampleTime: 0.02 }); expect(ast.kind).toBe("Program"); expect(ir.moduleName).toMatch(/^A120/); expect(ir.sourceMap.length + ir.kdlBridge.motionRequests.length + ir.kdlBridge.pathRequests.length).toBeGreaterThan(0); } }); it("creates job, post, roundtrip, report, delivery, and screenshot evidence paths", () => { const job = runAbb120Suite(loadPrograms(), { now: "2026-06-27T12:00:00.000Z" }); expect(job.job_id).toBe("A120-JOB-20260627120000-doc"); expect(job.robot.robotId).toBe("abb_irb120_3_58"); expect(job.programs.map((program) => program.name)).toEqual(PROGRAM_NAMES); expect(job.post.filenames).toEqual(expect.arrayContaining([ "A120Smoke.mod", "A120Smoke.ls", "A120Smoke.src" ])); expect(job.roundtrip.status).toMatch(/pass|warn/); expect(job.report_id).toBe("A120-REPORT-20260627120000"); expect(job.artifacts).toMatchObject({ jobJson: "A120-JOB-20260627120000-doc/job.json", reportHtml: "A120-JOB-20260627120000-doc/reports/A120-REPORT-20260627120000.html", deliveryPackageJson: "A120-JOB-20260627120000-doc/delivery/package.json", desktopScreenshot: "A120-JOB-20260627120000-doc/screenshots/virtual-controller-desktop.png", mobileScreenshot: "A120-JOB-20260627120000-doc/screenshots/virtual-controller-mobile.png" }); }); it("has an HTML virtual controller entry with required panels", () => { const html = readFileSync(join(APP_DIR, "virtual-controller.html"), "utf8"); const css = readFileSync(join(APP_DIR, "virtual-controller.css"), "utf8"); const js = readFileSync(join(APP_DIR, "virtual-controller.js"), "utf8"); expect(html).toContain("ABB120 Station"); expect(html).toContain("Path: pick_place"); expect(html).toContain("W2-JOB-latest"); expect(html).toContain("working2-abb120-spec"); expect(html).toContain("Controller"); expect(html).toContain("Motion Queue"); expect(html).toContain("Reports"); expect(html).toContain('src="./virtual-controller.js"'); expect(html).not.toContain('src="./virtual-controller.ts"'); expect(css).toContain(".viewport"); expect(css).toContain("@media"); expect(js).toContain("data-command"); expect(js).toContain("data-tab"); }); });