railiance-fabric/tests/test_graph_mode_switch.py
codex b47dd43b04
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Complete local coordination view switching and record remaining blockers
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e3b8-9815-70f2-9ac5-37bcde2c5090
2026-09-27 18:44:21 +02:00

70 lines
3.3 KiB
Python

"""Execute the browser transition against a small graph/DOM harness."""
import shutil
import subprocess
import pytest
from railiance_fabric.graph_explorer_ui import graph_explorer_page
def test_graph_switch_replaces_data_and_retains_previous_graph_on_failure(tmp_path):
if not shutil.which("node"):
pytest.skip("Node.js is required for the UI transition test")
page = graph_explorer_page()
transition = page.split(" let graphRequest = 0;", 1)[1].split(
" const handleFilterChange", 1
)[0]
script = r'''
const assert = require("node:assert/strict");
let graphMode = "full", includeResidualsParam = false, activeMode = "full";
let selected, selectedAnchor, selectedZoneId, focusSet, manualOverrides, filterRules;
let editingRuleId, currentProfileId, allEdgeTypes;
const layerColors = {}, allNodeTypes = [], nodeTypeLabels = {};
const collapsedZoneSnapshots = new Map(), zoneContainerState = new Map(), zoneSummaries = new Map();
const modeSelect = {}, profileSelect = {}, nodeTypeFilter = {}, edgeTypeFilter = {};
const popup = {style: {}}, profileSummary = {};
const controls = {};
const document = {getElementById(id) {return controls[id] ||= {style: {}};}};
let data = [{data: {id: "old"}}], urls = [], updated = 0;
const cy = {elements: () => ({remove() {data = [];}}), add(elements) {data.push(...elements);}};
const renderChecklist = () => {}, refreshRuleBuilder = () => {}, renderRules = () => {};
const syncFilterSummaries = () => {}, applyFilters = () => {}, runLayout = () => {};
const updateUrlState = () => {updated++;}, showDetails = () => {};
const updateProfileControls = () => {}, updateProfileSummary = () => {};
const applyViewState = () => {};
let response = {ok: true, json: async () => ({elements: [{data: {id: "workplan:a"}}]})};
let fetch = async (url) => {urls.push(url); return response;};
let graphRequest = 0;
'''
script += transition + r'''
(async () => {
await switchGraph("coordination", true);
assert.equal(data[0].data.id, "workplan:a");
assert.equal(modeSelect.value, "coordination");
assert.equal(controls["include-residuals-wrap"].style.display, "");
assert.match(urls[0], /include_residuals=true/);
response = {ok: false, status: 503};
await switchGraph("full");
assert.equal(data[0].data.id, "workplan:a");
assert.equal(modeSelect.value, "coordination");
assert.match(profileSummary.textContent, /503/);
response = {ok: true, json: async () => ({elements: []})};
await switchGraph("by-access-zone");
assert.deepEqual(data, []);
assert.equal(activeMode, "by-access-zone");
assert.equal(graphMode, "full");
assert.equal(controls["include-residuals-wrap"].style.display, "none");
let resolveOld;
fetch = () => new Promise(resolve => {resolveOld = resolve;});
const oldRequest = switchGraph("coordination");
fetch = async () => ({ok: true, json: async () => ({elements: [{data: {id: "latest"}}]})});
await switchGraph("full");
resolveOld({ok: true, json: async () => ({elements: [{data: {id: "stale"}}]})});
await oldRequest;
assert.equal(data[0].data.id, "latest");
assert.equal(updated, 3);
})().catch(error => {console.error(error); process.exitCode = 1;});
'''
path = tmp_path / "switch.cjs"
path.write_text(script)
subprocess.run(["node", str(path)], check=True, capture_output=True, text=True)