Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions robotframework_dashboard/dashboard.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from os.path import join, abspath, dirname
from os.path import join, abspath, dirname, relpath
from pathlib import Path
from datetime import datetime
from json import dumps
Expand Down Expand Up @@ -51,8 +51,12 @@ def generate_dashboard(
dashboard_data = dashboard_data.replace(
'"placeholder_version"', __version__
)
if use_logs and not server:
runs_data = self._make_paths_relative(data["runs"], name_dashboard)
else:
runs_data = data["runs"]
dashboard_data = dashboard_data.replace(
'"placeholder_runs"', f'"{self._compress_and_encode(data["runs"])}"'
'"placeholder_runs"', f'"{self._compress_and_encode(runs_data)}"'
)
dashboard_data = dashboard_data.replace(
'"placeholder_suites"', f'"{self._compress_and_encode(data["suites"])}"'
Expand Down Expand Up @@ -114,6 +118,20 @@ def generate_dashboard(
if len(data["runs"]) == 0:
print(" WARNING: There are no runs so the dashboard will be empty!")

def _make_paths_relative(self, runs, dashboard_name):
dashboard_dir = dirname(abspath(dashboard_name))
result = []
for run in runs:
if run.get("path"):
try:
relative = relpath(run["path"], dashboard_dir).replace("\\", "/")
run = dict(run)
run["path"] = relative
except ValueError:
pass # different Windows drive — keep absolute path as-is
result.append(run)
return result

def _compress_and_encode(self, obj):
json_data = dumps(obj).encode("utf-8")
compressed = compress(json_data)
Expand Down
12 changes: 8 additions & 4 deletions robotframework_dashboard/js/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@ function open_log_from_path(path) {
var fileUrl = ""
if (server) { // server url
fileUrl = `/log?path=${path}`
} else if (window.location.href.includes("file:///")) { // local machine url
fileUrl = `file:///${path}`
} else { // remote machine file server
fileUrl = combine_paths(window.location.href, path)
} else if (/^[A-Za-z]:/.test(path) || path.startsWith("/")) { // absolute path
if (window.location.href.includes("file:///")) { // local machine url
fileUrl = `file:///${path}`
} else { // remote machine file server
fileUrl = combine_paths(window.location.href, path)
}
} else { // relative path — resolve against the dashboard's own location
fileUrl = new URL(path, window.location.href).href
}
const win = window.open(fileUrl, "_blank")
}
Expand Down
142 changes: 142 additions & 0 deletions tests/python/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,145 @@ def test_generate_dashboard_subdirectory_created(tmp_path):
no_autoupdate=False,
)
assert subdir_output.exists()


# --- _make_paths_relative ---

import os


def _make_rel(dashboard_path, runs):
return DashboardGenerator()._make_paths_relative(runs, str(dashboard_path))


def test_make_paths_relative_same_dir(tmp_path):
output_xml = tmp_path / "output.xml"
dashboard = tmp_path / "dashboard.html"
runs = [{"run_start": "2025-01-01", "path": str(output_xml)}]
result = _make_rel(dashboard, runs)
assert result[0]["path"] == "output.xml"


def test_make_paths_relative_subdirectory(tmp_path):
reports = tmp_path / "reports"
reports.mkdir()
output_xml = reports / "output.xml"
dashboard = tmp_path / "dashboard.html"
runs = [{"run_start": "2025-01-01", "path": str(output_xml)}]
result = _make_rel(dashboard, runs)
assert result[0]["path"] == "reports/output.xml"


def test_make_paths_relative_parent_directory(tmp_path):
subdir = tmp_path / "dash"
subdir.mkdir()
output_xml = tmp_path / "output.xml"
dashboard = subdir / "dashboard.html"
runs = [{"run_start": "2025-01-01", "path": str(output_xml)}]
result = _make_rel(dashboard, runs)
assert result[0]["path"] == "../output.xml"


def test_make_paths_relative_empty_path_unchanged(tmp_path):
dashboard = tmp_path / "dashboard.html"
runs = [{"run_start": "2025-01-01", "path": ""}]
result = _make_rel(dashboard, runs)
assert result[0]["path"] == ""


def test_make_paths_relative_missing_path_key_unchanged(tmp_path):
dashboard = tmp_path / "dashboard.html"
runs = [{"run_start": "2025-01-01"}]
result = _make_rel(dashboard, runs)
assert result[0] == {"run_start": "2025-01-01"}


def test_make_paths_relative_does_not_mutate_originals(tmp_path):
output_xml = tmp_path / "output.xml"
dashboard = tmp_path / "dashboard.html"
original_path = str(output_xml)
runs = [{"run_start": "2025-01-01", "path": original_path}]
_make_rel(dashboard, runs)
assert runs[0]["path"] == original_path


def test_make_paths_relative_uses_forward_slashes(tmp_path):
reports = tmp_path / "a" / "b"
reports.mkdir(parents=True)
output_xml = reports / "output.xml"
dashboard = tmp_path / "dashboard.html"
runs = [{"run_start": "2025-01-01", "path": str(output_xml)}]
result = _make_rel(dashboard, runs)
assert "\\" not in result[0]["path"]


def test_make_paths_relative_multiple_runs(tmp_path):
reports = tmp_path / "reports"
reports.mkdir()
dashboard = tmp_path / "dashboard.html"
runs = [
{"run_start": "2025-01-01", "path": str(tmp_path / "output1.xml")},
{"run_start": "2025-01-02", "path": str(reports / "output2.xml")},
{"run_start": "2025-01-03", "path": ""},
]
result = _make_rel(dashboard, runs)
assert result[0]["path"] == "output1.xml"
assert result[1]["path"] == "reports/output2.xml"
assert result[2]["path"] == ""


def test_generate_dashboard_uselogs_embeds_relative_paths(tmp_path):
import json, zlib, base64, re
output_xml = tmp_path / "output.xml"
dashboard = tmp_path / "dashboard.html"
data = {"runs": [{"run_start": "2025-01-01", "path": str(output_xml)}],
"suites": [], "tests": [], "keywords": []}
DashboardGenerator().generate_dashboard(
name_dashboard=str(dashboard),
data=data,
generation_datetime=datetime(2025, 1, 1),
dashboard_title="",
server=False,
json_config=None,
message_config=[],
quantity=20,
use_logs=True,
offline=False,
force_json_config=False,
no_autoupdate=False,
)
content = dashboard.read_text(encoding="utf-8")
# Extract the base64 payload for runs
match = re.search(r'const runs = decode_and_decompress\("([^"]+)"\)', content)
assert match, "runs payload not found in dashboard"
decoded = json.loads(zlib.decompress(base64.b64decode(match.group(1))))
assert decoded[0]["path"] == "output.xml"
assert str(tmp_path).replace("\\", "/") not in decoded[0]["path"]


def test_generate_dashboard_server_mode_keeps_absolute_paths(tmp_path):
import json, zlib, base64, re
output_xml = tmp_path / "output.xml"
dashboard = tmp_path / "dashboard.html"
abs_path = str(output_xml)
data = {"runs": [{"run_start": "2025-01-01", "path": abs_path}],
"suites": [], "tests": [], "keywords": []}
DashboardGenerator().generate_dashboard(
name_dashboard=str(dashboard),
data=data,
generation_datetime=datetime(2025, 1, 1),
dashboard_title="",
server=True,
json_config=None,
message_config=[],
quantity=20,
use_logs=True,
offline=False,
force_json_config=False,
no_autoupdate=False,
)
content = dashboard.read_text(encoding="utf-8")
match = re.search(r'const runs = decode_and_decompress\("([^"]+)"\)', content)
assert match, "runs payload not found in dashboard"
decoded = json.loads(zlib.decompress(base64.b64decode(match.group(1))))
assert decoded[0]["path"] == abs_path
Loading