Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QA] Move integration tests to vizro #950

Merged
merged 35 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
007e19b
moved tests for filters, pages and themes
l0uden Jan 13, 2025
2e11cff
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Jan 13, 2025
40b382c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 13, 2025
33141fe
add gunicorn to hatch.toml
l0uden Jan 13, 2025
87229fb
Merge branch 'qa/move_integration_tests_to_vizro' of https://github.c…
l0uden Jan 13, 2025
7033db5
add pythonpath to start dashboard
l0uden Jan 13, 2025
10d33c5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 13, 2025
db51b4f
changed port
l0uden Jan 13, 2025
c6466f1
Merge branch 'qa/move_integration_tests_to_vizro' of https://github.c…
l0uden Jan 13, 2025
f77c394
changed port
l0uden Jan 13, 2025
a2101e5
moved tests to separate folder
l0uden Jan 14, 2025
db94441
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 14, 2025
9a26fe1
linting fix
l0uden Jan 14, 2025
43503fc
Merge branch 'qa/move_integration_tests_to_vizro' of https://github.c…
l0uden Jan 14, 2025
b410746
reworked tests structure
l0uden Jan 23, 2025
d3b8ce9
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Jan 23, 2025
61a48de
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Jan 27, 2025
c2dd5e9
dash testing method. folder structure changed
l0uden Jan 29, 2025
b770685
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Jan 29, 2025
43295cd
failed artifacts fix
l0uden Jan 29, 2025
71233ab
failed artifacts fix
l0uden Jan 29, 2025
af2faf5
failed artifacts fix
l0uden Jan 29, 2025
81897f0
failed artifacts fix
l0uden Jan 29, 2025
d80377c
failed artifacts fix
l0uden Jan 29, 2025
23f7e45
failed artifacts fix
l0uden Jan 29, 2025
8ed5bff
fix grey style
l0uden Jan 29, 2025
e4007a6
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Feb 3, 2025
8e76053
custom component change to support pydantic v2
l0uden Feb 3, 2025
351dbbd
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Feb 3, 2025
02f98d9
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Feb 4, 2025
2b36c14
Merge branch 'main' of https://github.com/mckinsey/vizro into qa/move…
l0uden Feb 4, 2025
e8c483d
Merge branch 'main' into qa/move_integration_tests_to_vizro
l0uden Feb 4, 2025
e70ad1a
added reruns
l0uden Feb 4, 2025
9d7e830
added reruns
l0uden Feb 4, 2025
7ebae43
added pytest-rerunfailures
l0uden Feb 4, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ runs:
- name: Copy failed screenshots
shell: bash
run: |
mkdir ${{ env.PROJECT_PATH }}failed_screenshots/
mkdir ${{ env.PROJECT_PATH }}failed_screenshots
cd ${{ env.PROJECT_PATH }}
cp *.png failed_screenshots
cp -r /tmp/dash_artifacts failed_screenshots
cp *.png failed_screenshots || true

- name: Archive production artifacts
uses: actions/upload-artifact@v4
with:
name: Failed screenshots
path: |
${{ env.PROJECT_PATH }}failed_screenshots/*.png
${{ env.PROJECT_PATH }}failed_screenshots

- name: Send custom JSON data to Slack
id: slack
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/test-e2e-vizro-dom-elements.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: e2e vizro dom elements tests for Vizro

defaults:
run:
working-directory: vizro-core

on:
push:
branches: [main]
pull_request:
branches:
- main

env:
PYTHONUNBUFFERED: 1
FORCE_COLOR: 1
PYTHON_VERSION: "3.12"

jobs:
test-e2e-vizro-dom-elements:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install Hatch
run: pip install hatch

- name: Show dependency tree
run: hatch run pip tree

- name: Run e2e vizro-dom-elements tests
run: |
hatch run test-e2e-vizro-dom-elements

- name: Create artifacts and slack notifications
if: failure()
uses: ./.github/actions/failed-artifacts-and-slack-notifications
env:
TESTS_NAME: Vizro e2e vizro dom elements tests
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
PROJECT_PATH: /home/runner/work/vizro/vizro/vizro-core/
14 changes: 12 additions & 2 deletions vizro-core/hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies = [
"coverage[toml]>=6.5",
"pytest",
"pytest-mock",
"pytest-rerunfailures",
"freezegun>=1.5.0",
"dash[testing]",
"chromedriver-autoinstaller>=0.6.4",
Expand All @@ -33,10 +34,14 @@ dependencies = [
"PyGithub",
"imutils",
"opencv-python",
"pyhamcrest"
"pyhamcrest",
"gunicorn"
]
installer = "uv"

[envs.default.env-vars]
PYTHONPATH = "tests/e2e/vizro/dashboards/default:tests/tests_utils"

[envs.default.scripts]
example = "hatch run examples:example {args:scratch_dev}" # shortcut script to underlying example environment script.
lint = "pre-commit run {args} --all-files"
Expand All @@ -63,7 +68,12 @@ templates-check = ["python src/vizro/_themes/generate_plotly_templates.py --chec
# 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-e2e-component-library = "pytest tests/e2e/test_component_library.py --headless {args}"
test-e2e-component-library = "pytest tests/e2e/component_library/test_component_library.py --headless {args}"
test-e2e-vizro-dom-elements = [
"gunicorn dashboard:app -b 0.0.0.0:5002 -w 1 --timeout 90 &",
"tests/tests_utils/e2e/vizro/dashboards/wait-for-it.sh 127.0.0.1:5002 -t 30",
"pytest -vs --reruns 1 tests/e2e/vizro/test_dom_elements --headless {args}"
]
test-integration = "pytest tests/integration --headless {args}"
test-js = "./tools/run_jest.sh {args}"
test-unit = "pytest tests/unit {args}"
Expand Down
Original file line number Diff line number Diff line change
@@ -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, make_screenshot_and_paths
from e2e.asserts import assert_image_equal, make_screenshot_and_paths

from vizro.figures.library import kpi_card, kpi_card_reference

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Optional

from vizro.models.types import capture


@capture("action")
def my_custom_action(click_data: Optional[dict] = None):
"""Custom action."""
if click_data:
return f'Scatter chart clicked data:\n### Species: "{click_data["points"][0]["customdata"][0]}"'
return "### No data clicked."
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import vizro.plotly.express as px
from vizro.models.types import capture


@capture("graph")
def bar_with_highlight(data_frame, x, highlight_bar=None):
"""Custom chart to test using DatePicker with Parameter."""
fig = px.bar(data_frame=data_frame, x=x)
fig["data"][0]["marker"]["color"] = ["orange" if c == highlight_bar else "blue" for c in fig["data"][0]["x"]]
return fig
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from typing import Annotated, Literal, Optional, Union

from dash import dcc, html
from pydantic import AfterValidator, Field, PlainSerializer, PrivateAttr

import vizro.models as vm
from vizro.models import Action
from vizro.models._action._actions_chain import _action_validator_factory
from vizro.models._base import VizroBaseModel, _log_call

# Case 2: Entirely new component (actually exists, but for ease of explanation chosen)
SingleOptionType = Union[bool, float, str]
MultiOptionType = Union[list[bool], list[float], list[str]]


class NewDropdown(VizroBaseModel):
"""Categorical single/multi-selector `Dropdown` to be provided to `Filter`."""

type: Literal["new-dropdown"] = "new-dropdown"
options: Optional[MultiOptionType] = Field(default=None, description="Possible options the user can select from")
value: Optional[Union[SingleOptionType, MultiOptionType]] = Field(
default=None, description="Options that are selected by default"
)
multi: bool = Field(default=True, description="Whether to allow selection of multiple values")
actions: Annotated[
list[Action],
AfterValidator(_action_validator_factory("value")),
PlainSerializer(lambda x: x[0].actions),
Field(default=[]),
]
title: Optional[str] = Field(None, description="Title to be displayed")

# Component properties for actions and interactions
_input_property: str = PrivateAttr("value")

@_log_call
def build(self):
full_options = self.options

return html.Div(
[
html.P(self.title) if self.title else None,
dcc.Dropdown(
id=self.id,
options=full_options,
value=self.value or full_options[0],
multi=self.multi,
persistence=True,
clearable=False,
),
],
)


# Important: Add new components to expected type - here the selector of the parent components
vm.Filter.add_type("selector", NewDropdown)
vm.Parameter.add_type("selector", NewDropdown)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Literal

import vizro.models as vm


# Case 1: Based on existing component
class RangeSliderNonCross(vm.RangeSlider):
"""Custom numeric multi-selector `RangeSliderNonCross` to be provided to `Filter`."""

type: Literal["other_range_slider"] = "other_range_slider"

def build(self):
range_slider_build_obj = super().build()
range_slider_build_obj[self.id].allowCross = False
range_slider_build_obj[self.id].tooltip = {
"always_visible": True,
"placement": "bottom",
}
return range_slider_build_obj


# Important: Add new components to expected type - here the selector of the parent components
vm.Filter.add_type("selector", RangeSliderNonCross)
vm.Parameter.add_type("selector", RangeSliderNonCross)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
h1,
h2,
.heading-1-400,
.heading-2-400 {
color: hotpink;
}
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions vizro-core/tests/e2e/vizro/dashboards/default/dashboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import e2e.vizro.constants as cnst
from pages.ag_grid_page import ag_grid_page
from pages.datepicker_page import datepicker_page
from pages.filters_page import filters_page
from pages.homepage import homepage
from pages.kpi_indicators_page import kpi_indicators_page
from pages.parameters_page import parameters_page

import vizro.models as vm
from vizro import Vizro

dashboard = vm.Dashboard(
title="Vizro dashboard for integration testing",
pages=[homepage, filters_page, parameters_page, kpi_indicators_page, datepicker_page, ag_grid_page],
navigation=vm.Navigation(
pages={
cnst.GENERAL_ACCORDION: [
cnst.HOME_PAGE_ID,
cnst.FILTERS_PAGE,
cnst.PARAMETERS_PAGE,
cnst.KPI_INDICATORS_PAGE,
],
cnst.DATEPICKER_ACCORDION: [cnst.DATEPICKER_PAGE],
cnst.AG_GRID_ACCORDION: [cnst.TABLE_AG_GRID_PAGE],
}
),
theme="vizro_light",
)

app = Vizro(assets_folder="../assets").build(dashboard)

if __name__ == "__main__":
app.run(debug=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import e2e.vizro.constants as cnst

import vizro.models as vm
import vizro.plotly.express as px
from vizro.tables import dash_ag_grid

gapminder = px.data.gapminder()

ag_grid_page = vm.Page(
title=cnst.TABLE_AG_GRID_PAGE,
components=[
vm.Container(
id=cnst.TABLE_AG_GRID_CONTAINER,
title=cnst.TABLE_AG_GRID_CONTAINER,
layout=vm.Layout(grid=[[0, 1]], col_gap="0px"),
components=[
vm.AgGrid(
id=cnst.TABLE_AG_GRID_ID,
title="Equal Title One",
figure=dash_ag_grid(data_frame=gapminder, dashGridOptions={"pagination": True}),
),
vm.Graph(
id=cnst.BOX_AG_GRID_PAGE_ID,
figure=px.box(gapminder, x="continent", y="lifeExp", title="Equal Title One"),
),
],
)
],
controls=[
vm.Filter(
column="year",
targets=[cnst.TABLE_AG_GRID_ID],
selector=vm.Dropdown(value=2007),
),
vm.Filter(
column="continent",
targets=[cnst.TABLE_AG_GRID_ID],
selector=vm.RadioItems(options=["Europe", "Africa", "Americas"]),
),
vm.Filter(
column="continent",
targets=[cnst.TABLE_AG_GRID_ID],
selector=vm.Checklist(options=["Asia", "Oceania"]),
),
vm.Filter(
column="pop",
targets=[cnst.TABLE_AG_GRID_ID],
selector=vm.RangeSlider(step=1000000.0, min=1000000, max=10000000),
),
],
)
Loading