150 lines
8.0 KiB
JavaScript
150 lines
8.0 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const readSource = (relativePath) => readFileSync(join(process.cwd(), relativePath), "utf8");
|
|
|
|
describe("self-serve studio task editing", () => {
|
|
const studioSource = () =>
|
|
readSource("src/views/dashboards/departmentDashboard/modules/self-serve/DepartmentSelfServeStudio.vue");
|
|
|
|
it("creates tasks with scoped runtime fields and dynamic image defaults", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain("const createNode = async (kind) => {");
|
|
expect(source).toContain('task: "New task"');
|
|
expect(source).toContain('gate_type: "ALWAYS"');
|
|
expect(source).toContain("lane: parseNullableInt(filters.value.lane_id) || 0");
|
|
expect(source).toContain("product: parseNullableInt(filters.value.vehicle_type_id) || 0");
|
|
expect(source).toContain("machine_type_id: parseNullableInt(filters.value.machine_type_id)");
|
|
expect(source).toContain("services: []");
|
|
expect(source).toContain("buttons: []");
|
|
});
|
|
|
|
it("normalizes task services and mapped buttons before saving inspector edits", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain('inspectorForm.value.gate_type ||= "ALWAYS"');
|
|
expect(source).toContain("inspectorForm.value.services = normalizeServiceList(inspectorForm.value.services)");
|
|
expect(source).toContain("inspectorForm.value.buttons = normalizeButtonList(inspectorForm.value.buttons)");
|
|
expect(source).toContain("data.services = normalizeServiceList(data.services)");
|
|
expect(source).toContain("data.buttons = normalizeButtonList(data.buttons)");
|
|
});
|
|
|
|
it("renders mapped dynamic image task buttons in the inspector", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain("SELF_SERVE_TASK_BUTTON_OPTIONS");
|
|
expect(source).toContain('data-testid="studio-task-button-picker"');
|
|
expect(source).toContain('v-model="inspectorForm.buttons"');
|
|
expect(source).toContain("taskButtonIsSelected(inspectorForm.buttons, option.id)");
|
|
expect(source).toContain("taskButtonSummary(rawFor(data).buttons)");
|
|
});
|
|
|
|
it("renders dynamic image previews in simulator results", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain("getSelfServeDynamicImageButtonsToPress(simulatorPreviewTasks.value)");
|
|
expect(source).toContain(
|
|
"const simulatorDynamicImageCurrentStep = computed(() => getSelfServeCompletedDynamicImageStep("
|
|
);
|
|
expect(source).toContain("const simulatorDynamicImageUrl = computed(() => {");
|
|
expect(source).toContain("buildSelfServeDynamicImageUrl({");
|
|
expect(source).toContain("currentStep: simulatorDynamicImageCurrentStep.value");
|
|
expect(source).toContain("const buildSimulatorPayload = () => {");
|
|
expect(source).toContain('const scheduleSimulatorRun = (reason = "change", delayMs = 250) => {');
|
|
expect(source).toContain('data-testid="studio-simulator-steps"');
|
|
expect(source).toContain('data-testid="studio-simulator-dynamic-image"');
|
|
expect(source).toContain('data-testid="studio-simulator-dynamic-image-reason"');
|
|
expect(source).toContain('@error="onSimulatorDynamicImageError"');
|
|
});
|
|
|
|
it("renders the simulator as an auto-running full-width workspace", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain("'is-simulator-panel': activePanel === 'simulator'");
|
|
expect(source).toContain('data-testid="studio-simulator-workspace"');
|
|
expect(source).toContain("const focusSimulatorNodes = async (nodeIds = [], openInspector = false) => {");
|
|
expect(source).toContain('activePanel.value = openInspector ? "inspector" : "flow";');
|
|
expect(source).toContain('const scheduleSimulatorRun = (reason = "change", delayMs = 250) => {');
|
|
expect(source).toContain('aria-label="Simulator controls"');
|
|
expect(source).not.toContain("<span>Run</span>");
|
|
expect(source).not.toContain('@submit.prevent="runSimulator');
|
|
});
|
|
|
|
it("requires lane and vehicle scope with URL and simulator local persistence", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain("const scopeFiltersFromRoute = () => ({");
|
|
expect(source).toContain("const showScopeGuide = computed(() => (");
|
|
expect(source).toContain("const applyScopeGuideSelection = () => {");
|
|
expect(source).toContain("const writeScopeToRoute = () => {");
|
|
expect(source).toContain("const simulatorStorageKey = computed(() => [");
|
|
expect(source).toContain("const restoreSimulatorStateFromStorage = () => {");
|
|
expect(source).toContain("const persistSimulatorStateToStorage = () => {");
|
|
expect(source).toContain('data-testid="studio-scope-guide"');
|
|
expect(source).toContain('data-testid="studio-scope-apply"');
|
|
expect(source).toContain('<option value="" disabled>Select lane</option>');
|
|
expect(source).toContain('<option value="" disabled>Select vehicle type</option>');
|
|
});
|
|
|
|
it("renders task attachments in the flow nodes and simulator without saving them into draft tasks", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain("const taskAttachmentList = (dataOrRaw) => {");
|
|
expect(source).toContain("const selectedTaskAttachments = computed(() => (");
|
|
expect(source).toContain("const uploadSelectedTaskAttachment = async (event) => {");
|
|
expect(source).toContain("const deleteSelectedTaskAttachment = async (attachment) => {");
|
|
expect(source).toContain('data-testid="studio-task-node-attachments"');
|
|
expect(source).toContain('data-testid="studio-task-attachment-manager"');
|
|
expect(source).toContain('data-testid="studio-task-attachment-upload"');
|
|
expect(source).toContain('class="studio-debug-attachment-list"');
|
|
expect(source).toContain('@click.stop="openTaskAttachment(attachment)"');
|
|
expect(source).toContain("delete data.attachments");
|
|
});
|
|
|
|
it("renders machine type and dynamic image filters in the studio toolbar", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain('dynamic_image_id: ""');
|
|
expect(source).toContain('data-testid="studio-filter-machine-type"');
|
|
expect(source).toContain('data-testid="studio-filter-dynamic-image"');
|
|
expect(source).toContain("lookupRows('dynamic_images')");
|
|
expect(source).toContain(
|
|
'matchesScopeFilter(node, "machine_type", normalizeFilterValue(filters.value.machine_type_id))'
|
|
);
|
|
expect(source).toContain(
|
|
'matchesScopeFilter(node, "dynamic_image", normalizeFilterValue(filters.value.dynamic_image_id))'
|
|
);
|
|
expect(source).toContain("laneLookupForNode(node)?.machine_type_id");
|
|
expect(source).toContain("laneLookupForNode(node)?.dynamic_image_id");
|
|
});
|
|
|
|
it("manages lanes and previews the selected lane dynamic image", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain('{ id: "lanes", label: "Lanes"');
|
|
expect(source).toContain("const saveLane = async () => {");
|
|
expect(source).toContain('entity: "lane"');
|
|
expect(source).toContain("selfserve_enabled: true");
|
|
expect(source).toContain("selfserve_enabled: normalizeSelfServeEnabledFlag(form?.selfserve_enabled)");
|
|
expect(source).toContain("const buildLaneDynamicImagePreviewUrl = (laneId, dynamicImageId) => {");
|
|
expect(source).toContain("dynamicImageId: normalizedDynamicImageId");
|
|
expect(source).toContain('data-testid="studio-lane-selfserve-enabled"');
|
|
expect(source).toContain('data-testid="studio-inspector-lane-selfserve-enabled"');
|
|
expect(source).toContain('data-testid="studio-lane-dynamic-image"');
|
|
expect(source).toContain('data-testid="studio-lane-dynamic-image-preview"');
|
|
expect(source).toContain('data-testid="studio-inspector-lane-dynamic-image-preview"');
|
|
});
|
|
|
|
it("supports task priority reordering from the task panel", () => {
|
|
const source = studioSource();
|
|
|
|
expect(source).toContain("const reorderTask = async (taskNode, direction) => {");
|
|
expect(source).toContain("order_priority: index + 1");
|
|
expect(source).toContain('action: "reorder"');
|
|
expect(source).toContain('@click="reorderTask(node, -1)"');
|
|
expect(source).toContain('@click="reorderTask(node, 1)"');
|
|
});
|
|
});
|