Repository navigation
fix: JS codebase review: bugs (B1-B28), dead code (D1-D8), performance (P1-P10) - #382
Merged
Merged
Conversation
Run identity - graphs key their runs on run_start and only show the run label: with run name labels runs sharing a name collapsed into one timeline column, compare picked the first run with that name, "All Suites Combined" summed different runs and log links opened the wrong run (B4) - filtering matches rows to runs on the stored run_start, so hiding milliseconds can't merge two runs; log lookup tries an exact match first (B25) - compare radar values are aligned to the suite labels (B5) Listeners and leaks - persistent template elements, the document and the dashboard nav get their listeners once instead of on every page rebuild (B1) - one log click listener per canvas (B2), one ResizeObserver per graph (B3) Filters and data - profiles compare custom filters and ignore option order (B7), no implicit global in apply_filter_profile (B8) - metadata is parsed with a regex and matched per item (B14, B15), the test section matches whole suite names and tags (B14) - "Only changes" ignores rerun and note borders (B20), message config keeps alias/run name tooltips (B21), suite folder reset rebuilds the suite select (B22), unknown graph types fall back to the default (B24) - skip-only runs count as skipped everywhere (B18), overview duration average excludes the card's own run by position (B17) - "Only last run" no longer crashes on empty data (B6), stats show 0 instead of NaN (B16), bar durations show the newest 30 (B11) UI - axis label clicks use the tick position (B9), server log paths are URL encoded (B10), confirm_action resolves on Esc (B12), an old alert timer no longer closes a new alert (B13), the logo is scaled to 256 px and storage errors no longer stop the dashboard (B23), clipboard copy is awaited and guarded (B26), ISO date strings (B27), typo (B28) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- the built-in run/suite/test/keyword stat widgets never render (their elements are never on the page), so their create/update functions and DOM writers no longer compute stats on every pass (D1); the graph metadata entries stay, they are part of the stored graph types - unused durationsByProject (D2), data.set writes that are never read (D3), a ternary with identical branches (D4) - update_overview_prefix_display's run card half matched no element, the cards get the prefix setting when they are created (D5) - update_donut_charts updated donuts that were just recreated (D6), the unused setup_dashboard_graphs alias (D7), an unused variable (D8) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- closing the filter or settings modal without a change no longer re-filters and rebuilds every graph (P1) - Chart.js graphs in a hidden or collapsed section are computed and drawn once their section is shown, not on every pass (P2) - Map/Set lookups instead of includes/indexOf in the test statistics timeline (P3); the overview caches the per project version checks and duration sums instead of scanning all runs per card (P4) - suites, tests, keywords and exceptions come sorted from the transform cache instead of being sorted on every filter pass (P5) - custom filter dimensions are collected once, the filter modal input handler is debounced (P6); stat widgets compute a scope once per pass (P7); several settings writes are batched into one (P8) - the overview nav only writes the DOM when something changed, so scrolling no longer wakes the navbar observer (P9) - heatmap max, suite path run set, filter_data run set and the exceptions timeline grouping are computed once (P10) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
S0: escape_html_for_merge moves to the leaf module js/escape.js as escape_html and also escapes single quotes. globals.js uses it, so it cannot live in common.js (circular import). S1: DataTables cells render escaped text; a test message starting with *HTML* renders as HTML without the marker, like the Robot log. The admin tables and server error alerts are escaped too. S2: the overview log link keeps the path in an escaped data attribute and opens it from one delegated click listener instead of an inline onclick. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…sion selector (S4-S5) S4: link widgets reject javascript:, data: and other schemes when added, and ignore them when opened from stored settings or a JSON config. The label, url, colour and id are escaped. S5: the project version checkbox selector uses CSS.escape. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Robot Framework stores names, tags, versions and messages raw, so the filter modal options, overview project cards, keyword library list, filter profile names, server error alerts, stat widget and custom section titles now go through escape_html. Escaped attribute values decode to the raw string, so the element ids stay the same. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…shboard Runs a payload test file, builds a dashboard from it with a payload run tag and project version, opens every page and the filter modal, and checks that only the *HTML* message ran its payload. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- one parser per format: parse_custom_filters and get_tz_suffix in common.js, the custom graph engine builds on them (C1) - clear_project_filter and update_filters_button_indicator move to filter/controls.js, donut.js imports previousFolder (C2) - table-driven section filter wiring with setup_switch (C3) - shared timeline run axis and tooltip (apply_timeline_run_axis, apply_timeline_defaults) and truncate_message; the compare tests timeline also uses timelineScale now (C4) - aggregate_by_folder and assign_fail_colors for the folder donuts, which keep the first-seen order of numeric suite names (C5) - overview cards sort on data attributes instead of their text (C6) - run tag readers use the checkbox value instead of the id (C7) - for...of over arrays, add_alert instead of alert(), fetch instead of XHR, toggles read event.target.checked, a graphs-finalized listener instead of a 500 ms timeout, stale setup_data_and_graphs runs are dropped on fast page switches (C8) - admin page: no implicit global in send_request, the log file upload uses its own spinner; three alert messages are now escaped Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- convert_timeline_data groups the bars of all items by their style (status colour, rerun or note border) instead of one dataset per item and style. Chart.js looks up the stack of every bar across all datasets, so the test statistics timeline of all suites of a 110 run database (860 datasets, 8557 bars) took about 5 s to update; it now has 5 datasets. The points keep their row in y, so tooltips, log clicks and notes are unchanged; the most time consuming tooltip reads raw.y instead of the dataset label. - chart_factory resolves barConfig's borderRadius function once per build, so Chart.js shares the bar options instead of resolving them per bar. - "Only changes" / "No changes" work on the points. - robot: the test statistics checks read the timeline rows, the rerun border keyword counts bars; the timeline snapshots are regenerated (same labels and points, fewer datasets). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes phase 2 and 3 (B1-B28) of the JS codebase review: correctness bugs found by reading the code, all re-checked against
mainat 2.5.0 first.Run identity
run_startand map it to the label only for display (get_run_label_by_run_startingraph_data/helpers.js). The compare selects userun_startas option value, and the bar graphs carry an alignedrunStartsarray for tooltips and log clicks.run_start(now kept on every source), so hiding milliseconds can't merge two runs. The log lookup tries an exact match first. The graph keys still use the shownrun_start.Listeners and leaks
setup_data_and_graphsruns on every page switch. The listeners on template elements (rerun view selects, section filters modal,document, dashboard nav, information popups) are now added once.Filters and data
JSON.parsecrash) and matched per item, soEnv: prodno longer matchesEnv: production. The test section matches whole suite names (is_test_in_suite) and parsed tags.get_run_status, moved tocommon.js).UI
#idanchor kept outside the encoding.confirm_actionresolvesfalseon Esc/backdrop and reuses the modal instance. B13: an old alert timer no longer closes a newer alert.Tform (not verified in Safari; the installed WebKit build doesn't match Playwright). B28: typo and a missing line break.Choices made
Tests
run_startcontract.03_dashboardtest for B1. 143 passed on the full run. The one failure wasValidate Compare Base View: B5 changes the radar data. The snapshot was regenerated in Docker, and then the suite passed. The diff puts each run's suite durations on its own suites (before, the WebshopAPI values sat on the first WebshopUI suite names) and stores them as numbers.run_start,aliasandrun_namelabel modes: no JS errors. Inrun_namemode the test statistics timeline keeps one column per run.Dead code (D1-D8) and performance (P1-P10)
Added as two more commits on top of the bug fixes, so they build on them without conflicts.
Dead code: the built-in run/suite/test/keyword stat widgets never render, but their create/update functions computed stats on every pass. Those functions are removed (D1), and a later commit also removes their 34 graph metadata entries and the matching information keys. Also removed: unused variables and writes (D2, D3, D8), an identical-branch ternary (D4), a selector that matched nothing (D5), a donut update right after recreation (D6) and an unused alias (D7).
Performance, measured on a 504-run / 41.7k-test dashboard built from the fixtures:
Tests: JS 527 passed (4 more for these items). Robot (Docker) 144/144 after each of the two commits.
Security (S0-S5)
Robot Framework stores names, tags, metadata and messages as-is (
output.xmlonly XML-escapes them), and Robot's ownlog.htmlescapes them at render time. The dashboard put them intoinnerHTMLunescaped. A probe with a payload test file ran script from the suite/run name, run tag and project version (Overview) and from the test name, plain message and*HTML*message (Tables). In server mode anyone who can upload an output could run script for every viewer, including/admin.escape_html(also escapes') in the new leaf modulejs/escape.js, used for every fix below. It replacesescape_html_for_mergefromvariables/globals.js.renderfunction on the Tables page and in the admin run and log tables. A test message that starts with*HTML*is still rendered as HTML, without the marker, like in the Robot log.onclickfrom the log path. The path is in an escapeddata-log-pathattribute with one delegated click listener.textContentfor filter modal options (metadata, run names, run tags, versions, custom filters), overview project names, the settings keyword library list, profile names, server error text in alerts, stat/link widget titles and colours, and custom section titles.http:,https:and relative URLs (is_safe_url), both when adding a widget and when opening one loaded from settings or a JSON config.CSS.escape.Tests: new robot suite
11_security.robotruns a payload test file, builds a dashboard with a payload run tag and project version, opens every page and the filter modal, and checks that only the*HTML*message ran its payload. JS unit tests forescape_htmlandis_safe_url. JS 533 passed, robot (Docker) 145/145.Code structure (C1-C8)
parse_custom_filtersandget_tz_suffixlive incommon.js(the timezone regex was copied 6 times), and the custom graph engine builds on them and onparse_metadata_items.clear_project_filterandupdate_filters_button_indicatormove tofilter/controls.js.controls.jsused them without importing them, which hid afilter_modal→controls→filter_modalcycle.donut.jsnow importspreviousFolder.section_filters.jsis table-driven (SECTION_GRAPH_UPDATES,setup_switch): 386 → 257 lines, one list per section instead of 5+ copies.apply_timeline_run_axis,apply_timeline_defaults) andtruncate_message. The compare tests timeline now also usestimelineScale, so its run labels are centred like on the other timelines.aggregate_by_folderandassign_fail_colorsreplace the duplicated folder donut code. Numeric suite names keep their order.data-run-number/passed/failed/skippedinstead of parsing their text.valueinstead of the id. Saved profiles store the same raw tag, so no migration. The ids stayrunTagCheckBox<tag>: the robot keywords use them, and since S3 they are escaped.for...ofover arrays,add_alertinstead ofalert(),fetchinstead of XHR, the skip/folder toggles readevent.target.checked, a one-timegraphs-finalizedlistener replaces a 500 ms timeout, and fast page switches drop stalesetup_data_and_graphsruns.Found on the way:
send_requeston the admin page assigned an undeclaredresponse, the log file upload showed the output upload spinner, and three alert messages (dashboard refresh error, JSON config error, settings load error) were not escaped. A JSON parse error can quote its input. Also fixed the server auth note inCLAUDE.mdand theserver-apiskill: the mutating endpoints and/refresh-dashboardneed credentials too.Tests: JS 541 passed (new tests for
get_tz_suffix,truncate_message,aggregate_by_folder,assign_fail_colors). Robot (Docker) 145/145. Example dashboard clicked through in headless Chrome (pages, fast page switches, every section filter and switch, overview sort, filter modal): no errors.Large timelines (performance) and comment cleanup
Measured on a 110-run database (1158 distinct tests, 26k test rows) with "All" selected in the test section:
convert_timeline_datamade one dataset per item and bar style (860 datasets for 8557 bars). Chart.js looks up the stack of every bar across all datasets, so the cost grew with bars × datasets. It now makes one dataset per bar style (status colour, rerun or note border): 5 datasets. Every point keeps its row iny, so tooltips, log clicks and notes are unchanged. The most time consuming tooltip readsraw.yinstead of the dataset label, and "Only changes" / "No changes" work on the points. This covers every timeline graph; test statistics and compare tests gain the most.barConfig.borderRadiusis a function, so Chart.js resolved the options of every bar separately.chart_factory.jsresolves it once per build (a rounding change rebuilds the graphs anyway). This helps every bar and timeline graph.Tests: JS 542 passed (a new test for the rounding resolution, the
convert_timeline_datatests updated to the new grouping). Robot (Docker) 145/145. Two test statistics checks read the timeline rows instead of the dataset labels, and the rerun border keyword counts bars instead of datasets. Four snapshots were regenerated (dashboardTestSection,dashboardSuiteSection,dashboardKeywordSection,compareSection). Every changed graph has the same labels and the same points as before, only in fewer datasets.🤖 Generated with Claude Code