From ea31d693d2a72be5c64e1cc7726d320f4b0dbdfe Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Wed, 13 Nov 2024 18:58:16 +0100 Subject: [PATCH 01/18] component library tests --- .../test-component-library-vizro-core.yml | 39 ++++++++ vizro-core/hatch.toml | 9 ++ vizro-core/tests/__init__.py | 0 .../test_component_library.py | 93 ++++++++++++++++++ vizro-core/tests/helpers/__init__.py | 0 vizro-core/tests/helpers/common.py | 50 ++++++++++ .../tests/screenshots/base_kpi_comp_lib.png | Bin 0 -> 36174 bytes 7 files changed, 191 insertions(+) create mode 100644 .github/workflows/test-component-library-vizro-core.yml create mode 100644 vizro-core/tests/__init__.py create mode 100644 vizro-core/tests/component_library/test_component_library.py create mode 100644 vizro-core/tests/helpers/__init__.py create mode 100644 vizro-core/tests/helpers/common.py create mode 100644 vizro-core/tests/screenshots/base_kpi_comp_lib.png diff --git a/.github/workflows/test-component-library-vizro-core.yml b/.github/workflows/test-component-library-vizro-core.yml new file mode 100644 index 000000000..46cbe21e4 --- /dev/null +++ b/.github/workflows/test-component-library-vizro-core.yml @@ -0,0 +1,39 @@ +name: Integration tests Component Library + +defaults: + run: + working-directory: vizro-core + +on: + push: + branches: [main] + pull_request: + branches: + - main + +env: + PYTHONUNBUFFERED: 1 + FORCE_COLOR: 1 + +jobs: + test-component-library-vizro-core: + name: test-component-library-vizro-core + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - name: Install Hatch + run: pip install hatch + + - name: Show dependency tree + run: hatch run tests:pip tree + + - name: Run integration tests + run: hatch run tests:test-component-library diff --git a/vizro-core/hatch.toml b/vizro-core/hatch.toml index a8165f25e..da92b5d9c 100644 --- a/vizro-core/hatch.toml +++ b/vizro-core/hatch.toml @@ -56,6 +56,7 @@ schema-check = ["python schemas/generate.py --check"] # See comments added in https://github.com/mckinsey/vizro/pull/444. test = "pytest tests --headless {args}" test-integration = "pytest tests/integration --headless {args}" +test-component-library = "pytest tests/component_library --headless {args}" test-js = "./tools/run_jest.sh {args}" test-unit = "pytest tests/unit {args}" test-unit-coverage = [ @@ -118,6 +119,14 @@ extra-dependencies = [ features = ["kedro"] python = "3.9" +[envs.tests] +extra-dependencies = [ + "imutils", + "opencv-python", + "pyhamcrest" +] +python = "3.12" + [publish.index] disable = true diff --git a/vizro-core/tests/__init__.py b/vizro-core/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vizro-core/tests/component_library/test_component_library.py b/vizro-core/tests/component_library/test_component_library.py new file mode 100644 index 000000000..cc8b23d98 --- /dev/null +++ b/vizro-core/tests/component_library/test_component_library.py @@ -0,0 +1,93 @@ +# ruff: noqa: F403, F405 +import dash_bootstrap_components as dbc +import pandas as pd +import pytest +from dash import Dash, html +from vizro.figures.library import kpi_card, kpi_card_reference + +from tests.helpers.common import compare_images + +df_kpi = pd.DataFrame( + { + "Actual": [100, 200, 700], + "Reference": [100, 300, 500], + "Category": ["A", "B", "C"], + } +) + +example_cards = [ + kpi_card(data_frame=df_kpi, value_column="Actual", title="KPI with value"), + kpi_card( + data_frame=df_kpi, + value_column="Actual", + title="KPI with aggregation", + agg_func="median", + ), + kpi_card( + data_frame=df_kpi, + value_column="Actual", + title="KPI formatted", + value_format="${value:.2f}", + ), + kpi_card( + data_frame=df_kpi, + value_column="Actual", + title="KPI with icon", + icon="shopping_cart", + ), +] + +example_reference_cards = [ + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + title="KPI ref. (pos)", + ), + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + agg_func="median", + title="KPI ref. (neg)", + ), + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + title="KPI ref. formatted", + value_format="{value}€", + reference_format="{delta}€ vs. last year ({reference}€)", + ), + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + title="KPI ref. with icon", + icon="shopping_cart", + ), +] + + +@pytest.mark.filterwarnings("ignore:HTTPResponse.getheader():DeprecationWarning") +@pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") +@pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") +def test_kpi_card(dash_duo): + app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) + app.layout = dbc.Container( + [ + html.H1(children="KPI Cards"), + dbc.Stack( + children=[ + dbc.Row([dbc.Col(kpi_card) for kpi_card in example_cards]), + dbc.Row([dbc.Col(kpi_card) for kpi_card in example_reference_cards]), + ], + gap=4, + ), + ] + ) + dash_duo.start_server(app) + dash_duo.wait_for_page(timeout=20) + dash_duo.wait_for_element("div[class='card-kpi card']") + compare_images(dash_duo.driver, "base_kpi_comp_lib.png", "tests_kpi_comp_lib") + assert dash_duo.get_logs() == [], "browser console should contain no error" diff --git a/vizro-core/tests/helpers/__init__.py b/vizro-core/tests/helpers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vizro-core/tests/helpers/common.py b/vizro-core/tests/helpers/common.py new file mode 100644 index 000000000..3ad032a57 --- /dev/null +++ b/vizro-core/tests/helpers/common.py @@ -0,0 +1,50 @@ +import subprocess + +import cv2 +import imutils +from hamcrest import assert_that, equal_to + + +def comparison_logic(original_image, new_image): + """Comparison process.""" + difference = cv2.subtract(original_image, new_image) + blue, green, red = cv2.split(difference) + assert_that(cv2.countNonZero(blue), equal_to(0), reason="Blue channel is different") + assert_that( + cv2.countNonZero(green), equal_to(0), reason="Green channel is different" + ) + assert_that(cv2.countNonZero(red), equal_to(0), reason="Red channel is different") + + +def create_image_difference(original, new): + """Creates new image with diff of images comparison.""" + diff = original.copy() + cv2.absdiff(original, new, diff) + gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) + for i in range(0, 3): + dilated = cv2.dilate(gray.copy(), None, iterations=i + 1) + (t_var, thresh) = cv2.threshold(dilated, 3, 255, cv2.THRESH_BINARY) + cnts = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + cnts = imutils.grab_contours(cnts) + for contour in cnts: + (x, y, width, height) = cv2.boundingRect(contour) + cv2.rectangle(new, (x, y), (x + width, y + height), (0, 255, 0), 2) + return new + + +def compare_images(browserdriver, base_image, test_image_name): + """Comparison logic and diff files creation.""" + browserdriver.save_screenshot(f"{test_image_name}_branch.png") + original = cv2.imread(f"screenshots/{base_image}") + new = cv2.imread(f"{test_image_name}_branch.png") + try: + comparison_logic(original, new) + subprocess.call(f"rm -rf {test_image_name}_branch.png", shell=True) + except (AssertionError, AttributeError) as exp: + subprocess.call(f"cp {test_image_name}_branch.png {base_image}", shell=True) + diff = create_image_difference(original=new, new=original) + cv2.imwrite(f"{test_image_name}_diff_main.png", diff) + raise Exception("pictures are not the same") from exp + except cv2.error as exp: + subprocess.call(f"cp {test_image_name}_branch.png {base_image}", shell=True) + raise Exception("pictures has different sizes") from exp \ No newline at end of file diff --git a/vizro-core/tests/screenshots/base_kpi_comp_lib.png b/vizro-core/tests/screenshots/base_kpi_comp_lib.png new file mode 100644 index 0000000000000000000000000000000000000000..bc95505784a983e438a1852f653e572dbdcc930d GIT binary patch literal 36174 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%0D5o)LLyMdr4T!yL6J$#L`7 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 literal 0 HcmV?d00001 From c6565368a8cc068075643f11e7f42938d0c787d1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:01:10 +0000 Subject: [PATCH 02/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- vizro-core/hatch.toml | 2 +- .../tests/component_library/test_component_library.py | 4 ++-- vizro-core/tests/helpers/common.py | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/vizro-core/hatch.toml b/vizro-core/hatch.toml index da92b5d9c..0cecae8be 100644 --- a/vizro-core/hatch.toml +++ b/vizro-core/hatch.toml @@ -55,8 +55,8 @@ schema-check = ["python schemas/generate.py --check"] # fix this, but we don't actually use `hatch run test` anywhere right now. # See comments added in https://github.com/mckinsey/vizro/pull/444. test = "pytest tests --headless {args}" -test-integration = "pytest tests/integration --headless {args}" test-component-library = "pytest tests/component_library --headless {args}" +test-integration = "pytest tests/integration --headless {args}" test-js = "./tools/run_jest.sh {args}" test-unit = "pytest tests/unit {args}" test-unit-coverage = [ diff --git a/vizro-core/tests/component_library/test_component_library.py b/vizro-core/tests/component_library/test_component_library.py index cc8b23d98..a41a613e4 100644 --- a/vizro-core/tests/component_library/test_component_library.py +++ b/vizro-core/tests/component_library/test_component_library.py @@ -3,10 +3,10 @@ import pandas as pd import pytest from dash import Dash, html -from vizro.figures.library import kpi_card, kpi_card_reference - from tests.helpers.common import compare_images +from vizro.figures.library import kpi_card, kpi_card_reference + df_kpi = pd.DataFrame( { "Actual": [100, 200, 700], diff --git a/vizro-core/tests/helpers/common.py b/vizro-core/tests/helpers/common.py index 3ad032a57..5da5b1441 100644 --- a/vizro-core/tests/helpers/common.py +++ b/vizro-core/tests/helpers/common.py @@ -10,9 +10,7 @@ def comparison_logic(original_image, new_image): difference = cv2.subtract(original_image, new_image) blue, green, red = cv2.split(difference) assert_that(cv2.countNonZero(blue), equal_to(0), reason="Blue channel is different") - assert_that( - cv2.countNonZero(green), equal_to(0), reason="Green channel is different" - ) + assert_that(cv2.countNonZero(green), equal_to(0), reason="Green channel is different") assert_that(cv2.countNonZero(red), equal_to(0), reason="Red channel is different") @@ -47,4 +45,4 @@ def compare_images(browserdriver, base_image, test_image_name): raise Exception("pictures are not the same") from exp except cv2.error as exp: subprocess.call(f"cp {test_image_name}_branch.png {base_image}", shell=True) - raise Exception("pictures has different sizes") from exp \ No newline at end of file + raise Exception("pictures has different sizes") from exp From 40a293ecf2ab15db237a5ead7f1370a44bd4ab3b Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Thu, 14 Nov 2024 11:35:13 +0100 Subject: [PATCH 03/18] failed artifacts and slack notifications --- .../test-component-library-vizro-core.yml | 27 +++++++++++++++++++ .github/workflows/vizro-qa-tests-trigger.yml | 4 --- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-component-library-vizro-core.yml b/.github/workflows/test-component-library-vizro-core.yml index 46cbe21e4..99bb6557a 100644 --- a/.github/workflows/test-component-library-vizro-core.yml +++ b/.github/workflows/test-component-library-vizro-core.yml @@ -37,3 +37,30 @@ jobs: - name: Run integration tests run: hatch run tests:test-component-library + + - name: Copy failed screenshots + if: failure() + run: | + mkdir /home/runner/work/vizro/vizro/vizro-core/failed_screenshots/ + cp *.png failed_screenshots + + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + if: failure() + with: + name: Failed screenshots + path: | + /home/runner/work/vizro/vizro/vizro-core/failed_screenshots/*.png + + - name: Send custom JSON data to Slack + id: slack + uses: slackapi/slack-github-action@v1.26.0 + if: failure() + with: + payload: | + { + "text": "Vizro component tests build result: ${{ job.status }}\nBranch: ${{ env.head_ref }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/.github/workflows/vizro-qa-tests-trigger.yml b/.github/workflows/vizro-qa-tests-trigger.yml index f3078b622..01e539abf 100644 --- a/.github/workflows/vizro-qa-tests-trigger.yml +++ b/.github/workflows/vizro-qa-tests-trigger.yml @@ -21,7 +21,6 @@ jobs: include: - label: integration tests - label: vizro-ai ui tests - - label: component library tests steps: - name: Passed fork step run: echo "Success!" @@ -36,7 +35,6 @@ jobs: include: - label: integration tests - label: vizro-ai ui tests - - label: component library tests steps: - uses: actions/checkout@v4 - name: Tests trigger @@ -48,8 +46,6 @@ jobs: export INPUT_WORKFLOW_FILE_NAME=${{ secrets.VIZRO_QA_INTEGRATION_TESTS_WORKFLOW }} elif [ "${{ matrix.label }}" == "vizro-ai ui tests" ]; then export INPUT_WORKFLOW_FILE_NAME=${{ secrets.VIZRO_QA_VIZRO_AI_UI_TESTS_WORKFLOW }} - elif [ "${{ matrix.label }}" == "component library tests" ]; then - export INPUT_WORKFLOW_FILE_NAME=${{ secrets.VIZRO_QA_VIZRO_COMPONENT_LIBRARY_TESTS_WORKFLOW }} fi export INPUT_GITHUB_TOKEN=${{ secrets.VIZRO_SVC_PAT }} export INPUT_REF=main # because we should send existent branch to dispatch workflow From 3283d744b9faa9f32476d93be6a59ae452f0a063 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Thu, 14 Nov 2024 11:47:40 +0100 Subject: [PATCH 04/18] branch in notification --- .github/workflows/test-component-library-vizro-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-component-library-vizro-core.yml b/.github/workflows/test-component-library-vizro-core.yml index 99bb6557a..995ba6651 100644 --- a/.github/workflows/test-component-library-vizro-core.yml +++ b/.github/workflows/test-component-library-vizro-core.yml @@ -59,7 +59,7 @@ jobs: with: payload: | { - "text": "Vizro component tests build result: ${{ job.status }}\nBranch: ${{ env.head_ref }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + "text": "Vizro component tests build result: ${{ job.status }}\nBranch: ${{ github.head_ref }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" } env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} From 3cff9096653741d83cf1fb1de9768a27a8bbfb56 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Thu, 14 Nov 2024 13:28:18 +0100 Subject: [PATCH 05/18] delete screenshot --- .../tests/screenshots/base_kpi_comp_lib.png | Bin 36174 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 vizro-core/tests/screenshots/base_kpi_comp_lib.png diff --git a/vizro-core/tests/screenshots/base_kpi_comp_lib.png b/vizro-core/tests/screenshots/base_kpi_comp_lib.png deleted file mode 100644 index bc95505784a983e438a1852f653e572dbdcc930d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36174 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%0D5o)LLyMdr4T!yL6J$#L`7 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 From 8fe38125b92ab07d47b9c04815d0b90e28db5a19 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Thu, 14 Nov 2024 13:31:50 +0100 Subject: [PATCH 06/18] add screenshot --- .../tests/screenshots/base_kpi_comp_lib.png | Bin 0 -> 36174 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 vizro-core/tests/screenshots/base_kpi_comp_lib.png diff --git a/vizro-core/tests/screenshots/base_kpi_comp_lib.png b/vizro-core/tests/screenshots/base_kpi_comp_lib.png new file mode 100644 index 0000000000000000000000000000000000000000..bc95505784a983e438a1852f653e572dbdcc930d GIT binary patch literal 36174 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%0D5o)LLyMdr4T!yL6J$#L`7 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 literal 0 HcmV?d00001 From ab843c8826e58ec622fbefcb84d6d0fb4394cf02 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Thu, 14 Nov 2024 13:38:25 +0100 Subject: [PATCH 07/18] fix screenshot url --- vizro-core/tests/helpers/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vizro-core/tests/helpers/common.py b/vizro-core/tests/helpers/common.py index 5da5b1441..1eaa55d78 100644 --- a/vizro-core/tests/helpers/common.py +++ b/vizro-core/tests/helpers/common.py @@ -33,7 +33,7 @@ def create_image_difference(original, new): def compare_images(browserdriver, base_image, test_image_name): """Comparison logic and diff files creation.""" browserdriver.save_screenshot(f"{test_image_name}_branch.png") - original = cv2.imread(f"screenshots/{base_image}") + original = cv2.imread(f"tests/screenshots/{base_image}") new = cv2.imread(f"{test_image_name}_branch.png") try: comparison_logic(original, new) From d74645fa9f0d4a9fe503bd855f93f8d213ab8330 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Thu, 14 Nov 2024 13:49:15 +0100 Subject: [PATCH 08/18] changelog --- ...9_alexey_snigir_component_library_tests.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 vizro-core/changelog.d/20241114_134849_alexey_snigir_component_library_tests.md diff --git a/vizro-core/changelog.d/20241114_134849_alexey_snigir_component_library_tests.md b/vizro-core/changelog.d/20241114_134849_alexey_snigir_component_library_tests.md new file mode 100644 index 000000000..7c0d58d4f --- /dev/null +++ b/vizro-core/changelog.d/20241114_134849_alexey_snigir_component_library_tests.md @@ -0,0 +1,48 @@ + + + + + + + + + From 9dd267a1c0bf6b4fad172109c9420c19014b1b2e Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Tue, 19 Nov 2024 17:19:17 +0100 Subject: [PATCH 09/18] review changes --- ...test-e2e-component-library-vizro-core.yml} | 17 +++++----- vizro-core/hatch.toml | 15 +++------ vizro-core/pyproject.toml | 8 ++++- vizro-core/tests/__init__.py | 0 vizro-core/tests/e2e/conftest.py | 6 ++++ .../base_kpi_card_component_library.png} | Bin .../test_component_library.py | 12 +++----- vizro-core/tests/helpers/__init__.py | 0 vizro-core/tests/integration/test_examples.py | 7 ----- .../common.py => tests_utils/e2e_asserts.py} | 29 +++++++++--------- 10 files changed, 46 insertions(+), 48 deletions(-) rename .github/workflows/{test-component-library-vizro-core.yml => test-e2e-component-library-vizro-core.yml} (75%) delete mode 100644 vizro-core/tests/__init__.py create mode 100644 vizro-core/tests/e2e/conftest.py rename vizro-core/tests/{screenshots/base_kpi_comp_lib.png => e2e/screenshots/base_kpi_card_component_library.png} (100%) rename vizro-core/tests/{component_library => e2e}/test_component_library.py (84%) delete mode 100644 vizro-core/tests/helpers/__init__.py rename vizro-core/tests/{helpers/common.py => tests_utils/e2e_asserts.py} (63%) diff --git a/.github/workflows/test-component-library-vizro-core.yml b/.github/workflows/test-e2e-component-library-vizro-core.yml similarity index 75% rename from .github/workflows/test-component-library-vizro-core.yml rename to .github/workflows/test-e2e-component-library-vizro-core.yml index 995ba6651..01bd4496e 100644 --- a/.github/workflows/test-component-library-vizro-core.yml +++ b/.github/workflows/test-e2e-component-library-vizro-core.yml @@ -1,4 +1,4 @@ -name: Integration tests Component Library +name: e2e component library tests defaults: run: @@ -14,29 +14,30 @@ on: env: PYTHONUNBUFFERED: 1 FORCE_COLOR: 1 + PYTHON_VERSION: "3.12" jobs: - test-component-library-vizro-core: - name: test-component-library-vizro-core + test-e2e-component-library-vizro-core: + name: test-e2e-component-library-vizro-core runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.12 + - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v5 with: - python-version: 3.12 + python-version: ${{ env.PYTHON_VERSION }} - name: Install Hatch run: pip install hatch - name: Show dependency tree - run: hatch run tests:pip tree + run: hatch run all.py${{ env.PYTHON_VERSION }}:pip tree - - name: Run integration tests - run: hatch run tests:test-component-library + - name: Run e2e component library tests + run: hatch run all.py${{ env.PYTHON_VERSION }}:test-e2e-component-library - name: Copy failed screenshots if: failure() diff --git a/vizro-core/hatch.toml b/vizro-core/hatch.toml index 0cecae8be..83da1b30d 100644 --- a/vizro-core/hatch.toml +++ b/vizro-core/hatch.toml @@ -30,7 +30,10 @@ dependencies = [ "openpyxl", "jupyter", "pre-commit", - "PyGithub" + "PyGithub", + "imutils", + "opencv-python", + "pyhamcrest" ] installer = "uv" @@ -55,7 +58,7 @@ schema-check = ["python schemas/generate.py --check"] # fix this, but we don't actually use `hatch run test` anywhere right now. # See comments added in https://github.com/mckinsey/vizro/pull/444. test = "pytest tests --headless {args}" -test-component-library = "pytest tests/component_library --headless {args}" +test-e2e-component-library = "pytest tests/e2e/test_component_library.py --headless {args}" test-integration = "pytest tests/integration --headless {args}" test-js = "./tools/run_jest.sh {args}" test-unit = "pytest tests/unit {args}" @@ -119,14 +122,6 @@ extra-dependencies = [ features = ["kedro"] python = "3.9" -[envs.tests] -extra-dependencies = [ - "imutils", - "opencv-python", - "pyhamcrest" -] -python = "3.12" - [publish.index] disable = true diff --git a/vizro-core/pyproject.toml b/vizro-core/pyproject.toml index a1992ddc8..9bb97aa6c 100644 --- a/vizro-core/pyproject.toml +++ b/vizro-core/pyproject.toml @@ -79,7 +79,13 @@ filterwarnings = [ # Ignore warning when using the fig.layout.title inside examples: "ignore:Using the `title` argument in your Plotly chart function may cause misalignment:UserWarning", # Ignore warning for Pydantic v1 API and Python 3.13: - "ignore:Failing to pass a value to the 'type_params' parameter of 'typing.ForwardRef._evaluate' is deprecated:DeprecationWarning" + "ignore:Failing to pass a value to the 'type_params' parameter of 'typing.ForwardRef._evaluate' is deprecated:DeprecationWarning", + # Ignore deprecation warning until this is solved: https://github.com/plotly/dash/issues/2590 + # The `features` examples do add_type, which ideally we would clean up afterwards to restore vizro.models to + # its previous state. Since we don't currently do this, `hatch run test` fails. + # This is difficult to fix fully by un-importing vizro.models though, since we use `import vizro.models as vm` - see + # https://stackoverflow.com/questions/437589/how-do-i-unload-reload-a-python-module. + "ignore:HTTPResponse.getheader():DeprecationWarning" ] norecursedirs = ["tests/tests_utils", "tests/js"] pythonpath = ["tests/tests_utils"] diff --git a/vizro-core/tests/__init__.py b/vizro-core/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/vizro-core/tests/e2e/conftest.py b/vizro-core/tests/e2e/conftest.py new file mode 100644 index 000000000..285f17c08 --- /dev/null +++ b/vizro-core/tests/e2e/conftest.py @@ -0,0 +1,6 @@ +import pytest + + +@pytest.fixture +def get_test_name(request): + return request.node.name diff --git a/vizro-core/tests/screenshots/base_kpi_comp_lib.png b/vizro-core/tests/e2e/screenshots/base_kpi_card_component_library.png similarity index 100% rename from vizro-core/tests/screenshots/base_kpi_comp_lib.png rename to vizro-core/tests/e2e/screenshots/base_kpi_card_component_library.png diff --git a/vizro-core/tests/component_library/test_component_library.py b/vizro-core/tests/e2e/test_component_library.py similarity index 84% rename from vizro-core/tests/component_library/test_component_library.py rename to vizro-core/tests/e2e/test_component_library.py index a41a613e4..1865a63c5 100644 --- a/vizro-core/tests/component_library/test_component_library.py +++ b/vizro-core/tests/e2e/test_component_library.py @@ -1,9 +1,7 @@ -# ruff: noqa: F403, F405 import dash_bootstrap_components as dbc import pandas as pd -import pytest from dash import Dash, html -from tests.helpers.common import compare_images +from e2e_asserts import assert_image_equal from vizro.figures.library import kpi_card, kpi_card_reference @@ -69,10 +67,8 @@ ] -@pytest.mark.filterwarnings("ignore:HTTPResponse.getheader():DeprecationWarning") -@pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") -@pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") -def test_kpi_card(dash_duo): +def test_kpi_card_component_library(dash_duo, get_test_name): + app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) app.layout = dbc.Container( [ @@ -89,5 +85,5 @@ def test_kpi_card(dash_duo): dash_duo.start_server(app) dash_duo.wait_for_page(timeout=20) dash_duo.wait_for_element("div[class='card-kpi card']") - compare_images(dash_duo.driver, "base_kpi_comp_lib.png", "tests_kpi_comp_lib") + assert_image_equal(dash_duo.driver, get_test_name) assert dash_duo.get_logs() == [], "browser console should contain no error" diff --git a/vizro-core/tests/helpers/__init__.py b/vizro-core/tests/helpers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/vizro-core/tests/integration/test_examples.py b/vizro-core/tests/integration/test_examples.py index a983469c3..b87c327c3 100644 --- a/vizro-core/tests/integration/test_examples.py +++ b/vizro-core/tests/integration/test_examples.py @@ -1,4 +1,3 @@ -# ruff: noqa: F403, F405 import os import runpy from pathlib import Path @@ -40,12 +39,6 @@ def dashboard(request, monkeypatch): examples_path = Path(__file__).parents[2] / "examples" -# Ignore deprecation warning until this is solved: https://github.com/plotly/dash/issues/2590 -# The `features` examples do add_type, which ideally we would clean up afterwards to restore vizro.models to -# its previous state. Since we don't currently do this, `hatch run test` fails. -# This is difficult to fix fully by un-importing vizro.models though, since we use `import vizro.models as vm` - see -# https://stackoverflow.com/questions/437589/how-do-i-unload-reload-a-python-module. -@pytest.mark.filterwarnings("ignore:HTTPResponse.getheader():DeprecationWarning") # Ignore as it doesn't affect the test run @pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") diff --git a/vizro-core/tests/helpers/common.py b/vizro-core/tests/tests_utils/e2e_asserts.py similarity index 63% rename from vizro-core/tests/helpers/common.py rename to vizro-core/tests/tests_utils/e2e_asserts.py index 1eaa55d78..03b188725 100644 --- a/vizro-core/tests/helpers/common.py +++ b/vizro-core/tests/tests_utils/e2e_asserts.py @@ -1,11 +1,11 @@ -import subprocess - import cv2 import imutils +import shutil from hamcrest import assert_that, equal_to +from pathlib import Path -def comparison_logic(original_image, new_image): +def _compare_images(original_image, new_image): """Comparison process.""" difference = cv2.subtract(original_image, new_image) blue, green, red = cv2.split(difference) @@ -14,7 +14,7 @@ def comparison_logic(original_image, new_image): assert_that(cv2.countNonZero(red), equal_to(0), reason="Red channel is different") -def create_image_difference(original, new): +def _create_image_difference(original, new): """Creates new image with diff of images comparison.""" diff = original.copy() cv2.absdiff(original, new, diff) @@ -30,19 +30,20 @@ def create_image_difference(original, new): return new -def compare_images(browserdriver, base_image, test_image_name): +def assert_image_equal(browserdriver, test_image_name): """Comparison logic and diff files creation.""" + base_image_name = f"{test_image_name.replace('test', 'base')}.png" browserdriver.save_screenshot(f"{test_image_name}_branch.png") - original = cv2.imread(f"tests/screenshots/{base_image}") + original = cv2.imread(f"tests/e2e/screenshots/{base_image_name}") new = cv2.imread(f"{test_image_name}_branch.png") try: - comparison_logic(original, new) - subprocess.call(f"rm -rf {test_image_name}_branch.png", shell=True) - except (AssertionError, AttributeError) as exp: - subprocess.call(f"cp {test_image_name}_branch.png {base_image}", shell=True) - diff = create_image_difference(original=new, new=original) + _compare_images(original, new) + Path(f"{test_image_name}_branch.png").unlink() + except AssertionError as exp: + shutil.copy(f"{test_image_name}_branch.png", base_image_name) + diff = _create_image_difference(original=new, new=original) cv2.imwrite(f"{test_image_name}_diff_main.png", diff) - raise Exception("pictures are not the same") from exp + raise AssertionError("pictures are not the same") from exp except cv2.error as exp: - subprocess.call(f"cp {test_image_name}_branch.png {base_image}", shell=True) - raise Exception("pictures has different sizes") from exp + shutil.copy(f"{test_image_name}_branch.png", base_image_name) + raise cv2.error("pictures has different sizes") from exp From a37ecb2b32805307a9da4f1f06d1ae19541a8342 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:20:22 +0000 Subject: [PATCH 10/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- vizro-core/tests/e2e/test_component_library.py | 1 - vizro-core/tests/tests_utils/e2e_asserts.py | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vizro-core/tests/e2e/test_component_library.py b/vizro-core/tests/e2e/test_component_library.py index 1865a63c5..bb1c38793 100644 --- a/vizro-core/tests/e2e/test_component_library.py +++ b/vizro-core/tests/e2e/test_component_library.py @@ -68,7 +68,6 @@ def test_kpi_card_component_library(dash_duo, get_test_name): - app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) app.layout = dbc.Container( [ diff --git a/vizro-core/tests/tests_utils/e2e_asserts.py b/vizro-core/tests/tests_utils/e2e_asserts.py index 03b188725..95913539e 100644 --- a/vizro-core/tests/tests_utils/e2e_asserts.py +++ b/vizro-core/tests/tests_utils/e2e_asserts.py @@ -1,8 +1,9 @@ +import shutil +from pathlib import Path + import cv2 import imutils -import shutil from hamcrest import assert_that, equal_to -from pathlib import Path def _compare_images(original_image, new_image): @@ -40,7 +41,7 @@ def assert_image_equal(browserdriver, test_image_name): _compare_images(original, new) Path(f"{test_image_name}_branch.png").unlink() except AssertionError as exp: - shutil.copy(f"{test_image_name}_branch.png", base_image_name) + shutil.copy(f"{test_image_name}_branch.png", base_image_name) diff = _create_image_difference(original=new, new=original) cv2.imwrite(f"{test_image_name}_diff_main.png", diff) raise AssertionError("pictures are not the same") from exp From 3d9f148fc2692e2b836c0689f19969abc0d27481 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Wed, 20 Nov 2024 14:11:08 +0100 Subject: [PATCH 11/18] review changes round 2 --- .../test-e2e-component-library-vizro-core.yml | 7 ++- vizro-core/pyproject.toml | 6 +-- vizro-core/tests/e2e/conftest.py | 6 --- ...ng => main_kpi_card_component_library.png} | Bin .../tests/e2e/test_component_library.py | 7 +-- vizro-core/tests/integration/test_examples.py | 4 ++ vizro-core/tests/tests_utils/e2e_asserts.py | 50 ++++++++++-------- 7 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 vizro-core/tests/e2e/conftest.py rename vizro-core/tests/e2e/screenshots/{base_kpi_card_component_library.png => main_kpi_card_component_library.png} (100%) diff --git a/.github/workflows/test-e2e-component-library-vizro-core.yml b/.github/workflows/test-e2e-component-library-vizro-core.yml index 01bd4496e..d887a3451 100644 --- a/.github/workflows/test-e2e-component-library-vizro-core.yml +++ b/.github/workflows/test-e2e-component-library-vizro-core.yml @@ -1,4 +1,4 @@ -name: e2e component library tests +name: e2e tests of component library for Vizro defaults: run: @@ -18,7 +18,6 @@ env: jobs: test-e2e-component-library-vizro-core: - name: test-e2e-component-library-vizro-core runs-on: ubuntu-latest @@ -34,10 +33,10 @@ jobs: run: pip install hatch - name: Show dependency tree - run: hatch run all.py${{ env.PYTHON_VERSION }}:pip tree + run: hatch run pip tree - name: Run e2e component library tests - run: hatch run all.py${{ env.PYTHON_VERSION }}:test-e2e-component-library + run: hatch run test-e2e-component-library - name: Copy failed screenshots if: failure() diff --git a/vizro-core/pyproject.toml b/vizro-core/pyproject.toml index b66eecac3..383e4ac96 100644 --- a/vizro-core/pyproject.toml +++ b/vizro-core/pyproject.toml @@ -80,11 +80,7 @@ filterwarnings = [ "ignore:Using the `title` argument in your Plotly chart function may cause misalignment:UserWarning", # Ignore warning for Pydantic v1 API and Python 3.13: "ignore:Failing to pass a value to the 'type_params' parameter of 'typing.ForwardRef._evaluate' is deprecated:DeprecationWarning", - # Ignore deprecation warning until this is solved: https://github.com/plotly/dash/issues/2590 - # The `features` examples do add_type, which ideally we would clean up afterwards to restore vizro.models to - # its previous state. Since we don't currently do this, `hatch run test` fails. - # This is difficult to fix fully by un-importing vizro.models though, since we use `import vizro.models as vm` - see - # https://stackoverflow.com/questions/437589/how-do-i-unload-reload-a-python-module. + # Ignore deprecation warning until this is solved: https://github.com/plotly/dash/issues/2590: "ignore:HTTPResponse.getheader():DeprecationWarning" ] norecursedirs = ["tests/tests_utils", "tests/js"] diff --git a/vizro-core/tests/e2e/conftest.py b/vizro-core/tests/e2e/conftest.py deleted file mode 100644 index 285f17c08..000000000 --- a/vizro-core/tests/e2e/conftest.py +++ /dev/null @@ -1,6 +0,0 @@ -import pytest - - -@pytest.fixture -def get_test_name(request): - return request.node.name diff --git a/vizro-core/tests/e2e/screenshots/base_kpi_card_component_library.png b/vizro-core/tests/e2e/screenshots/main_kpi_card_component_library.png similarity index 100% rename from vizro-core/tests/e2e/screenshots/base_kpi_card_component_library.png rename to vizro-core/tests/e2e/screenshots/main_kpi_card_component_library.png diff --git a/vizro-core/tests/e2e/test_component_library.py b/vizro-core/tests/e2e/test_component_library.py index 1865a63c5..f019aee40 100644 --- a/vizro-core/tests/e2e/test_component_library.py +++ b/vizro-core/tests/e2e/test_component_library.py @@ -1,7 +1,7 @@ import dash_bootstrap_components as dbc import pandas as pd from dash import Dash, html -from e2e_asserts import assert_image_equal +from e2e_asserts import assert_image_equal, make_screenshot_and_paths from vizro.figures.library import kpi_card, kpi_card_reference @@ -67,7 +67,7 @@ ] -def test_kpi_card_component_library(dash_duo, get_test_name): +def test_kpi_card_component_library(dash_duo, request): app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) app.layout = dbc.Container( @@ -85,5 +85,6 @@ def test_kpi_card_component_library(dash_duo, get_test_name): dash_duo.start_server(app) dash_duo.wait_for_page(timeout=20) dash_duo.wait_for_element("div[class='card-kpi card']") - assert_image_equal(dash_duo.driver, get_test_name) + result_image_path, expected_image_path, expected_image_name = make_screenshot_and_paths(dash_duo.driver, request) + assert_image_equal(result_image_path, expected_image_path, expected_image_name) assert dash_duo.get_logs() == [], "browser console should contain no error" diff --git a/vizro-core/tests/integration/test_examples.py b/vizro-core/tests/integration/test_examples.py index b87c327c3..1acdd4372 100644 --- a/vizro-core/tests/integration/test_examples.py +++ b/vizro-core/tests/integration/test_examples.py @@ -44,6 +44,10 @@ def dashboard(request, monkeypatch): @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") # Ignore for lower bounds because of plotly==5.12.0 @pytest.mark.filterwarnings("ignore:The behavior of DatetimeProperties.to_pydatetime is deprecated:FutureWarning") +# The `features` examples do add_type, which ideally we would clean up afterwards to restore vizro.models to +# its previous state. Since we don't currently do this, `hatch run test` fails. +# This is difficult to fix fully by un-importing vizro.models though, since we use `import vizro.models as vm` - see +# https://stackoverflow.com/questions/437589/how-do-i-unload-reload-a-python-module. @pytest.mark.parametrize( "example_path, version", [ diff --git a/vizro-core/tests/tests_utils/e2e_asserts.py b/vizro-core/tests/tests_utils/e2e_asserts.py index 03b188725..5f5626e2d 100644 --- a/vizro-core/tests/tests_utils/e2e_asserts.py +++ b/vizro-core/tests/tests_utils/e2e_asserts.py @@ -5,19 +5,19 @@ from pathlib import Path -def _compare_images(original_image, new_image): +def _compare_images(expected_image, result_image): """Comparison process.""" - difference = cv2.subtract(original_image, new_image) + difference = cv2.subtract(expected_image, result_image) blue, green, red = cv2.split(difference) assert_that(cv2.countNonZero(blue), equal_to(0), reason="Blue channel is different") assert_that(cv2.countNonZero(green), equal_to(0), reason="Green channel is different") assert_that(cv2.countNonZero(red), equal_to(0), reason="Red channel is different") -def _create_image_difference(original, new): +def _create_image_difference(expected_image, result_image): """Creates new image with diff of images comparison.""" - diff = original.copy() - cv2.absdiff(original, new, diff) + diff = expected_image.copy() + cv2.absdiff(expected_image, result_image, diff) gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) for i in range(0, 3): dilated = cv2.dilate(gray.copy(), None, iterations=i + 1) @@ -26,24 +26,30 @@ def _create_image_difference(original, new): cnts = imutils.grab_contours(cnts) for contour in cnts: (x, y, width, height) = cv2.boundingRect(contour) - cv2.rectangle(new, (x, y), (x + width, y + height), (0, 255, 0), 2) - return new + cv2.rectangle(result_image, (x, y), (x + width, y + height), (0, 255, 0), 2) + return result_image -def assert_image_equal(browserdriver, test_image_name): +def make_screenshot_and_paths(browserdriver, request): + result_image_path = f"{request.node.name}_branch.png" + expected_image_name = f"{request.node.name.replace('test', 'main')}.png" + expected_image_path = f"tests/e2e/screenshots/{expected_image_name}" + browserdriver.save_screenshot(result_image_path) + return result_image_path, expected_image_path, expected_image_name + + +def assert_image_equal(result_image_path, expected_image_path, expected_image_name): """Comparison logic and diff files creation.""" - base_image_name = f"{test_image_name.replace('test', 'base')}.png" - browserdriver.save_screenshot(f"{test_image_name}_branch.png") - original = cv2.imread(f"tests/e2e/screenshots/{base_image_name}") - new = cv2.imread(f"{test_image_name}_branch.png") + expected_image = cv2.imread(expected_image_path) + result_image = cv2.imread(result_image_path) try: - _compare_images(original, new) - Path(f"{test_image_name}_branch.png").unlink() - except AssertionError as exp: - shutil.copy(f"{test_image_name}_branch.png", base_image_name) - diff = _create_image_difference(original=new, new=original) - cv2.imwrite(f"{test_image_name}_diff_main.png", diff) - raise AssertionError("pictures are not the same") from exp - except cv2.error as exp: - shutil.copy(f"{test_image_name}_branch.png", base_image_name) - raise cv2.error("pictures has different sizes") from exp + _compare_images(expected_image, result_image) + Path(result_image_path).unlink() + except AssertionError as exc: + shutil.copy(result_image_path, expected_image_name) + diff = _create_image_difference(expected_image=expected_image, result_image=result_image) + cv2.imwrite(f"{result_image_path}_difference_from_main.png", diff) + raise AssertionError("pictures are not the same") from exc + except cv2.error as exc: + shutil.copy(result_image_path, expected_image_name) + raise cv2.error("pictures has different sizes") from exc From 868df894837bbb8027b9cdf8bf586595b95cc92a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:14:19 +0000 Subject: [PATCH 12/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .github/workflows/test-e2e-component-library-vizro-core.yml | 1 - vizro-core/tests/e2e/test_component_library.py | 1 - vizro-core/tests/tests_utils/e2e_asserts.py | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test-e2e-component-library-vizro-core.yml b/.github/workflows/test-e2e-component-library-vizro-core.yml index d887a3451..37e3ce968 100644 --- a/.github/workflows/test-e2e-component-library-vizro-core.yml +++ b/.github/workflows/test-e2e-component-library-vizro-core.yml @@ -18,7 +18,6 @@ env: jobs: test-e2e-component-library-vizro-core: - runs-on: ubuntu-latest steps: diff --git a/vizro-core/tests/e2e/test_component_library.py b/vizro-core/tests/e2e/test_component_library.py index f019aee40..1688a5de2 100644 --- a/vizro-core/tests/e2e/test_component_library.py +++ b/vizro-core/tests/e2e/test_component_library.py @@ -68,7 +68,6 @@ def test_kpi_card_component_library(dash_duo, request): - app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) app.layout = dbc.Container( [ diff --git a/vizro-core/tests/tests_utils/e2e_asserts.py b/vizro-core/tests/tests_utils/e2e_asserts.py index 5d0f1cf93..4d1a80f37 100644 --- a/vizro-core/tests/tests_utils/e2e_asserts.py +++ b/vizro-core/tests/tests_utils/e2e_asserts.py @@ -47,7 +47,7 @@ def assert_image_equal(result_image_path, expected_image_path, expected_image_na _compare_images(expected_image, result_image) Path(result_image_path).unlink() except AssertionError as exc: - shutil.copy(result_image_path, expected_image_name) + shutil.copy(result_image_path, expected_image_name) diff = _create_image_difference(expected_image=expected_image, result_image=result_image) cv2.imwrite(f"{result_image_path}_difference_from_main.png", diff) raise AssertionError("pictures are not the same") from exc From 921c9bb03e48b08b36af0ca0412e0e40d0e650de Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Wed, 20 Nov 2024 16:30:13 +0100 Subject: [PATCH 13/18] review changes round 3 --- vizro-core/tests/e2e/test_component_library.py | 4 ++-- vizro-core/tests/tests_utils/e2e_asserts.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vizro-core/tests/e2e/test_component_library.py b/vizro-core/tests/e2e/test_component_library.py index f019aee40..5eaa3c444 100644 --- a/vizro-core/tests/e2e/test_component_library.py +++ b/vizro-core/tests/e2e/test_component_library.py @@ -85,6 +85,6 @@ def test_kpi_card_component_library(dash_duo, request): dash_duo.start_server(app) dash_duo.wait_for_page(timeout=20) dash_duo.wait_for_element("div[class='card-kpi card']") - result_image_path, expected_image_path, expected_image_name = make_screenshot_and_paths(dash_duo.driver, request) - assert_image_equal(result_image_path, expected_image_path, expected_image_name) + result_image_path, expected_image_path = make_screenshot_and_paths(dash_duo.driver, request.node.name) + assert_image_equal(result_image_path, expected_image_path) assert dash_duo.get_logs() == [], "browser console should contain no error" diff --git a/vizro-core/tests/tests_utils/e2e_asserts.py b/vizro-core/tests/tests_utils/e2e_asserts.py index 5d0f1cf93..9f25d1bfc 100644 --- a/vizro-core/tests/tests_utils/e2e_asserts.py +++ b/vizro-core/tests/tests_utils/e2e_asserts.py @@ -31,17 +31,17 @@ def _create_image_difference(expected_image, result_image): return result_image -def make_screenshot_and_paths(browserdriver, request): - result_image_path = f"{request.node.name}_branch.png" - expected_image_name = f"{request.node.name.replace('test', 'main')}.png" - expected_image_path = f"tests/e2e/screenshots/{expected_image_name}" +def make_screenshot_and_paths(browserdriver, request_node_name): + result_image_path = f"{request_node_name}_branch.png" + expected_image_path = f"tests/e2e/screenshots/{request_node_name.replace('test', 'main')}.png" browserdriver.save_screenshot(result_image_path) - return result_image_path, expected_image_path, expected_image_name + return result_image_path, expected_image_path -def assert_image_equal(result_image_path, expected_image_path, expected_image_name): +def assert_image_equal(result_image_path, expected_image_path): """Comparison logic and diff files creation.""" expected_image = cv2.imread(expected_image_path) + expected_image_name = Path(expected_image_path).name result_image = cv2.imread(result_image_path) try: _compare_images(expected_image, result_image) From 9734fa2e8dcee71e60860e84dd739a1b418d5848 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Mon, 25 Nov 2024 15:31:26 +0100 Subject: [PATCH 14/18] test new artifacts and notifications failure logic --- .../actions.yml | 29 ++++++++++++++++++ .../test-e2e-component-library-vizro-core.yml | 27 ++-------------- ...png => main_kpi_card_component_librar.png} | Bin vizro-core/tests/tests_utils/e2e_asserts.py | 19 ++++++++++-- 4 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 .github/actions/failed-artifacts-and-slack-notifications/actions.yml rename vizro-core/tests/e2e/screenshots/{main_kpi_card_component_library.png => main_kpi_card_component_librar.png} (100%) diff --git a/.github/actions/failed-artifacts-and-slack-notifications/actions.yml b/.github/actions/failed-artifacts-and-slack-notifications/actions.yml new file mode 100644 index 000000000..4ef116f60 --- /dev/null +++ b/.github/actions/failed-artifacts-and-slack-notifications/actions.yml @@ -0,0 +1,29 @@ +name: "Create artifacts and slack notifications" + +runs: + using: "composite" + steps: + - name: Copy failed screenshots + shell: bash + run: | + mkdir /home/runner/work/vizro/vizro/vizro-core/failed_screenshots/ + cp *.png failed_screenshots + + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + with: + name: Failed screenshots + path: | + /home/runner/work/vizro/vizro/vizro-core/failed_screenshots/*.png + + - name: Send custom JSON data to Slack + id: slack + uses: slackapi/slack-github-action@v1.26.0 + with: + payload: | + { + "text": "${{ env.TESTS_NAME }} build result: ${{ job.status }}\nBranch: ${{ github.head_ref }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/.github/workflows/test-e2e-component-library-vizro-core.yml b/.github/workflows/test-e2e-component-library-vizro-core.yml index 37e3ce968..b8600cfdb 100644 --- a/.github/workflows/test-e2e-component-library-vizro-core.yml +++ b/.github/workflows/test-e2e-component-library-vizro-core.yml @@ -37,29 +37,8 @@ jobs: - name: Run e2e component library tests run: hatch run test-e2e-component-library - - name: Copy failed screenshots + - name: Create artifacts and slack notifications if: failure() - run: | - mkdir /home/runner/work/vizro/vizro/vizro-core/failed_screenshots/ - cp *.png failed_screenshots - - - name: Archive production artifacts - uses: actions/upload-artifact@v4 - if: failure() - with: - name: Failed screenshots - path: | - /home/runner/work/vizro/vizro/vizro-core/failed_screenshots/*.png - - - name: Send custom JSON data to Slack - id: slack - uses: slackapi/slack-github-action@v1.26.0 - if: failure() - with: - payload: | - { - "text": "Vizro component tests build result: ${{ job.status }}\nBranch: ${{ github.head_ref }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - } + uses: ./.github/actions/failed-artifacts-and-slack-notifications env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + TESTS_NAME: Vizro e2e component library tests diff --git a/vizro-core/tests/e2e/screenshots/main_kpi_card_component_library.png b/vizro-core/tests/e2e/screenshots/main_kpi_card_component_librar.png similarity index 100% rename from vizro-core/tests/e2e/screenshots/main_kpi_card_component_library.png rename to vizro-core/tests/e2e/screenshots/main_kpi_card_component_librar.png diff --git a/vizro-core/tests/tests_utils/e2e_asserts.py b/vizro-core/tests/tests_utils/e2e_asserts.py index 209b0fbef..e16eb40eb 100644 --- a/vizro-core/tests/tests_utils/e2e_asserts.py +++ b/vizro-core/tests/tests_utils/e2e_asserts.py @@ -8,8 +8,11 @@ def _compare_images(expected_image, result_image): """Comparison process.""" + # Subtract two images difference = cv2.subtract(expected_image, result_image) + # Splitting image into separate channels blue, green, red = cv2.split(difference) + # Counting non-zero pixels and comparing it to zero assert_that(cv2.countNonZero(blue), equal_to(0), reason="Blue channel is different") assert_that(cv2.countNonZero(green), equal_to(0), reason="Green channel is different") assert_that(cv2.countNonZero(red), equal_to(0), reason="Red channel is different") @@ -17,21 +20,28 @@ def _compare_images(expected_image, result_image): def _create_image_difference(expected_image, result_image): """Creates new image with diff of images comparison.""" - diff = expected_image.copy() - cv2.absdiff(expected_image, result_image, diff) + # Calculate the difference between the two images + diff = cv2.absdiff(expected_image, result_image) + # Convert image to grayscale gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) for i in range(0, 3): + # Dilation of the image dilated = cv2.dilate(gray.copy(), None, iterations=i + 1) + # Apply threshold to the dilated image (t_var, thresh) = cv2.threshold(dilated, 3, 255, cv2.THRESH_BINARY) + # Calculate difference contours for the image cnts = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) for contour in cnts: + # Calculate bounding rectangles around detected contour (x, y, width, height) = cv2.boundingRect(contour) - cv2.rectangle(result_image, (x, y), (x + width, y + height), (0, 255, 0), 2) + # Draw red rectangle around difference area + cv2.rectangle(result_image, (x, y), (x + width, y + height), (0, 0, 255), 2) return result_image def make_screenshot_and_paths(browserdriver, request_node_name): + """Creates image paths and makes screenshot during the test run.""" result_image_path = f"{request_node_name}_branch.png" expected_image_path = f"tests/e2e/screenshots/{request_node_name.replace('test', 'main')}.png" browserdriver.save_screenshot(result_image_path) @@ -45,10 +55,13 @@ def assert_image_equal(result_image_path, expected_image_path): result_image = cv2.imread(result_image_path) try: _compare_images(expected_image, result_image) + # Deleting created branch image to leave only failed for github artifacts Path(result_image_path).unlink() except AssertionError as exc: + # Copy created branch image to the one with the name from main for easier replacement in the repo shutil.copy(result_image_path, expected_image_name) diff = _create_image_difference(expected_image=expected_image, result_image=result_image) + # Writing image with differences to a new file cv2.imwrite(f"{result_image_path}_difference_from_main.png", diff) raise AssertionError("pictures are not the same") from exc except cv2.error as exc: From 21d4f6d4b44a5cfdb5b6e13e94d10db60e0e9fe0 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Mon, 25 Nov 2024 15:51:36 +0100 Subject: [PATCH 15/18] fixed filename --- .../{actions.yml => action.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/actions/failed-artifacts-and-slack-notifications/{actions.yml => action.yml} (100%) diff --git a/.github/actions/failed-artifacts-and-slack-notifications/actions.yml b/.github/actions/failed-artifacts-and-slack-notifications/action.yml similarity index 100% rename from .github/actions/failed-artifacts-and-slack-notifications/actions.yml rename to .github/actions/failed-artifacts-and-slack-notifications/action.yml From ac64a8d955631fb72738596af8dadb1ead131919 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Mon, 25 Nov 2024 15:57:17 +0100 Subject: [PATCH 16/18] passed secret as env --- .../actions/failed-artifacts-and-slack-notifications/action.yml | 2 +- .github/workflows/test-e2e-component-library-vizro-core.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/failed-artifacts-and-slack-notifications/action.yml b/.github/actions/failed-artifacts-and-slack-notifications/action.yml index 4ef116f60..571c61a7f 100644 --- a/.github/actions/failed-artifacts-and-slack-notifications/action.yml +++ b/.github/actions/failed-artifacts-and-slack-notifications/action.yml @@ -25,5 +25,5 @@ runs: "text": "${{ env.TESTS_NAME }} build result: ${{ job.status }}\nBranch: ${{ github.head_ref }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" } env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/.github/workflows/test-e2e-component-library-vizro-core.yml b/.github/workflows/test-e2e-component-library-vizro-core.yml index b8600cfdb..85c08a0e6 100644 --- a/.github/workflows/test-e2e-component-library-vizro-core.yml +++ b/.github/workflows/test-e2e-component-library-vizro-core.yml @@ -42,3 +42,4 @@ jobs: uses: ./.github/actions/failed-artifacts-and-slack-notifications env: TESTS_NAME: Vizro e2e component library tests + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} From 677a64e216ae1cd5dbba3d65e93c07173e50e8a6 Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Mon, 25 Nov 2024 16:17:38 +0100 Subject: [PATCH 17/18] add path for file copying --- .../actions/failed-artifacts-and-slack-notifications/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/failed-artifacts-and-slack-notifications/action.yml b/.github/actions/failed-artifacts-and-slack-notifications/action.yml index 571c61a7f..a0b1ce6de 100644 --- a/.github/actions/failed-artifacts-and-slack-notifications/action.yml +++ b/.github/actions/failed-artifacts-and-slack-notifications/action.yml @@ -1,4 +1,5 @@ name: "Create artifacts and slack notifications" +description: "Creates failed artifacts with screenshots and sends slack notifications if build failed" runs: using: "composite" @@ -7,6 +8,7 @@ runs: shell: bash run: | mkdir /home/runner/work/vizro/vizro/vizro-core/failed_screenshots/ + cd /home/runner/work/vizro/vizro/vizro-core/ cp *.png failed_screenshots - name: Archive production artifacts From 52b87ba393489e07d692737487b794066f7f195c Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Mon, 25 Nov 2024 16:27:47 +0100 Subject: [PATCH 18/18] return correct file name --- ...brar.png => main_kpi_card_component_library.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename vizro-core/tests/e2e/screenshots/{main_kpi_card_component_librar.png => main_kpi_card_component_library.png} (100%) diff --git a/vizro-core/tests/e2e/screenshots/main_kpi_card_component_librar.png b/vizro-core/tests/e2e/screenshots/main_kpi_card_component_library.png similarity index 100% rename from vizro-core/tests/e2e/screenshots/main_kpi_card_component_librar.png rename to vizro-core/tests/e2e/screenshots/main_kpi_card_component_library.png