From fab70c7dbaa01ca861509a10eea2f05af5f48c1a Mon Sep 17 00:00:00 2001 From: cepetr Date: Thu, 29 Feb 2024 09:24:25 +0100 Subject: [PATCH] feat(tests): show screenshot diff [no changelog] --- tests/ui_tests/reporting/html.py | 9 +++- tests/ui_tests/reporting/testreport.css | 6 +++ tests/ui_tests/reporting/testreport.js | 62 +++++++++++++++++++++++++ tests/ui_tests/reporting/testreport.py | 26 ++++++++++- 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/tests/ui_tests/reporting/html.py b/tests/ui_tests/reporting/html.py index 119970513..1c933b671 100644 --- a/tests/ui_tests/reporting/html.py +++ b/tests/ui_tests/reporting/html.py @@ -56,6 +56,12 @@ def image_column(hash: str | None, cur_dir: Path, img_id: str | None = None) -> i("missing") +def diff_column() -> None: + """Put diff image into table as one cell.""" + with td(bgcolor="white"): + a("Click to show") + + def _relative_path(cur_dir: Path, path_to: Path) -> str: """Find best relative path to refer to path_to from cur_dir.""" cur_dir = cur_dir.resolve() @@ -91,6 +97,7 @@ def diff_table(diff: Iterable[tuple[str | None, str | None]], cur_dir: Path) -> background = "white" else: background = "red" - with tr(bgcolor=background): + with tr(bgcolor=background, onclick="createDiff(this)"): image_column(left, cur_dir) image_column(right, cur_dir) + diff_column() diff --git a/tests/ui_tests/reporting/testreport.css b/tests/ui_tests/reporting/testreport.css index 24b53ce50..262e1e433 100644 --- a/tests/ui_tests/reporting/testreport.css +++ b/tests/ui_tests/reporting/testreport.css @@ -69,6 +69,12 @@ tr.bad a:visited { width: 256px; } +.model-TR canvas { + image-rendering: pixelated; + width: 256px; +} + + /* GIF styling */ /* Style the input field */ diff --git a/tests/ui_tests/reporting/testreport.js b/tests/ui_tests/reporting/testreport.js index bf192774e..7dbdbd319 100644 --- a/tests/ui_tests/reporting/testreport.js +++ b/tests/ui_tests/reporting/testreport.js @@ -162,5 +162,67 @@ function onLoad() { } } +var module = {}; + +function getImageData(image) { + // Get original image size + const width = image.naturalWidth; + const height = image.naturalHeight; + + // Create 2D canvas + let canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + let context = canvas.getContext("2d"); + + // Draw the image + context.drawImage(image, 0, 0); + + // Return image raw data + return context.getImageData(0, 0, width, height); +} + + +function createTableDiff(table) { + // Process all rows in the table\ + // (if the row doesn't contain two images, it's skipped) + table.querySelectorAll("tr").forEach((row) => { + createRowDiff(row); + }); +} + +function createRowDiff(row) { + // Find an element with recorded image + recImg = row.querySelector("td:nth-child(1) > img"); + // Find an element with the current image + curImg = row.querySelector("td:nth-child(2) > img"); + // Skip if we haven't found two images + if (recImg == null || curImg == null) { + return; + } + + // Get images's raw data + recData = getImageData(recImg); + curData = getImageData(curImg); + + const width = recImg.naturalWidth; + const height = recImg.naturalHeight; + + // Create canvas for diff result + let difImg = document.createElement('canvas') + difImg.width = width; + difImg.height = height; + let difCtx = difImg.getContext("2d") + + // Process differences + const difData = difCtx.createImageData(width, height); + options = {threshold: 0.0, includeAA: true}; + pixelmatch(recData.data, curData.data, difData.data, width, height, options); + difCtx.putImageData(difData, 0, 0); + + // Put the result into the 3rd column + row.querySelector("td:nth-child(3)").replaceChildren(difImg) +} + window.onload = onLoad diff --git a/tests/ui_tests/reporting/testreport.py b/tests/ui_tests/reporting/testreport.py index 76d2282cf..969589d1c 100644 --- a/tests/ui_tests/reporting/testreport.py +++ b/tests/ui_tests/reporting/testreport.py @@ -7,7 +7,22 @@ from pathlib import Path import dominate import dominate.tags as t -from dominate.tags import a, div, h1, h2, hr, i, p, span, strong, table, td, th, tr +from dominate.tags import ( + a, + div, + h1, + h2, + hr, + i, + p, + script, + span, + strong, + table, + td, + th, + tr, +) from dominate.util import text from ..common import FixturesType, TestCase, TestResult @@ -338,7 +353,13 @@ def failed(result: TestResult) -> Path: doc = document( title=result.test.id, actual_hash=result.actual_hash, model=result.test.model ) + with doc.head: + script( + type="text/javascript", src="https://cdn.jsdelivr.net/npm/pixelmatch@5.3.0" + ) + with doc: + _header(result.test.id, result.expected_hash, result.actual_hash) with div(id="markbox", _class="script-hidden"): @@ -353,10 +374,11 @@ def failed(result: TestResult) -> Path: strong("WARNING:") text(" failed to download recorded fixtures. Is this a new test case?") - with table(border=1, width=600): + with table(border=1, width=600, onclick="createTableDiff(this)"): with tr(): th("Expected") th("Actual") + th("Diff") html.diff_table(result.diff_lines(), TESTREPORT_PATH / "failed")