Skip to content
Merged
20 changes: 12 additions & 8 deletions .github/workflows/test_tools_dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ jobs:
include:

# macos tests
- os: macos-latest
python-version: "3.11"
- os: "macos-14"
python-version: "3.12"
shell: bash

# linux tests
Expand All @@ -43,7 +43,7 @@ jobs:

# windows tests
- os: windows-latest
python-version: "3.11"
python-version: "3.12"
shell: cmd
pytest_args: '-m "not forked"'

Expand Down Expand Up @@ -82,8 +82,8 @@ jobs:
run: uv sync ${{ matrix.uv_sync_args }} --extra duckdb --extra workspace --group sentry-sdk --group pipeline --group sources --group dashboard-tests
if: matrix.python-version != '3.14.0-beta.4'

- name: Install playwright
run: playwright install
- name: Install playwright & deps
run: playwright install && playwright install-deps
if: matrix.python-version != '3.14.0-beta.4'

# Run pipeline dashboard unit tests
Expand All @@ -95,10 +95,14 @@ jobs:
# Mac is also disabled for the time being
- name: Run dashboard e2e
run: |
marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines-dir _storage/.dlt/pipelines/ --with_test_identifiers true &
sleep 10
marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines-dir _storage/.dlt/pipelines/ --with_test_identifiers true & pytest --browser chromium tests/e2e
if: matrix.python-version != '3.9' && matrix.python-version != '3.14.0-beta.4' && matrix.os != 'windows-latest'

- name: Run dashboard e2e windows
run: |
start marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines-dir _storage\.dlt\pipelines\ --with_test_identifiers true
pytest --browser chromium tests/e2e
if: matrix.python-version != '3.9' && matrix.os != 'windows-latest' && matrix.os != 'macos-latest' && matrix.python-version != '3.14.0-beta.4'
if: matrix.python-version != '3.9' && matrix.python-version != '3.14.0-beta.4' && matrix.os == 'windows-latest'

matrix_job_required_check:
name: common | common tests
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,6 @@ test-e2e-dashboard-headed:
start-dlt-dashboard-e2e:
uv run marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines-dir _storage/.dlt/pipelines --with_test_identifiers true

# creates the dashboard test pipelines globally for manual testingn of the dashboard app and cli
# creates the dashboard test pipelines globally for manual testing of the dashboard app and cli
create-test-pipelines:
uv run python tests/helpers/dashboard/example_pipelines.py
45 changes: 30 additions & 15 deletions tests/e2e/helpers/dashboard/test_e2e.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Any, Literal
import asyncio #
import sys

import dlt
import pytest
Expand All @@ -20,6 +22,10 @@

from dlt.helpers.dashboard import strings as app_strings

# NOTE: The line below is needed for playwright to work on windows
if sys.platform.startswith("win"):
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())


@pytest.fixture()
def simple_incremental_pipeline() -> Any:
Expand Down Expand Up @@ -90,6 +96,11 @@ def broken_resource():
#


def _normpath(path: str) -> str:
"""normalize path to unix style and lowercase for windows tests"""
return path.replace("/", "\\").lower() if sys.platform.startswith("win") else path


def _go_home(page: Page) -> None:
page.goto("http://localhost:2718")

Expand Down Expand Up @@ -141,7 +152,7 @@ def test_exception_pipeline(page: Page, failed_pipeline: Any):

# overview page
_open_section(page, "overview")
expect(page.get_by_text("_storage/.dlt/pipelines/failed_pipeline")).to_be_visible()
expect(page.get_by_text(_normpath("_storage/.dlt/pipelines/failed_pipeline"))).to_be_visible()
expect(
page.get_by_text("Exception encountered during last pipeline run in step").nth(0)
).to_be_visible()
Expand All @@ -166,8 +177,8 @@ def test_exception_pipeline(page: Page, failed_pipeline: Any):
_open_section(page, "loads")
expect(page.get_by_text(app_strings.loads_loading_failed_text[0:20])).to_be_visible()

_open_section(page, "ibis")
expect(page.get_by_text(app_strings.ibis_backend_error_text[0:20])).to_be_visible()
# _open_section(page, "ibis")
# expect(page.get_by_text(app_strings.ibis_backend_error_text[0:20])).to_be_visible()


def test_multi_schema_selection(page: Page, multi_schema_pipeline: Any):
Expand Down Expand Up @@ -211,7 +222,7 @@ def test_simple_incremental_pipeline(page: Page, simple_incremental_pipeline: An

# overview page
_open_section(page, "overview")
expect(page.get_by_text("_storage/.dlt/pipelines/one_two_three")).to_be_visible()
expect(page.get_by_text(_normpath("_storage/.dlt/pipelines/one_two_three"))).to_be_visible()

# check schema info (this is the yaml part)
_open_section(page, "schema")
Expand Down Expand Up @@ -261,8 +272,8 @@ def test_simple_incremental_pipeline(page: Page, simple_incremental_pipeline: An
).to_be_visible() # this is in the loads table

# ibis page
_open_section(page, "ibis")
expect(page.get_by_text(app_strings.ibis_backend_connected_text)).to_be_visible()
# _open_section(page, "ibis")
# expect(page.get_by_text(app_strings.ibis_backend_connected_text)).to_be_visible()


def test_fruit_pipeline(page: Page, fruit_pipeline: Any):
Expand All @@ -272,7 +283,7 @@ def test_fruit_pipeline(page: Page, fruit_pipeline: Any):

# overview page
_open_section(page, "overview")
expect(page.get_by_text("_storage/.dlt/pipelines/fruit_pipeline")).to_be_visible()
expect(page.get_by_text(_normpath("_storage/.dlt/pipelines/fruit_pipeline"))).to_be_visible()

# check schema info (this is the yaml part)
_open_section(page, "schema")
Expand Down Expand Up @@ -301,15 +312,17 @@ def test_fruit_pipeline(page: Page, fruit_pipeline: Any):
).to_be_visible() # this is in the loads table

# ibis page
_open_section(page, "ibis")
expect(page.get_by_text(app_strings.ibis_backend_connected_text)).to_be_visible()
# _open_section(page, "ibis")
# expect(page.get_by_text(app_strings.ibis_backend_connected_text)).to_be_visible()


def test_never_run_pipeline(page: Page, never_run_pipeline: Any):
_go_home(page)
page.get_by_role("link", name="never_run_pipeline").click()

expect(page.get_by_text("_storage/.dlt/pipelines/never_run_pipeline")).to_be_visible()
expect(
page.get_by_text(_normpath("_storage/.dlt/pipelines/never_run_pipeline"))
).to_be_visible()

# check schema info (this is the yaml part)
_open_section(page, "schema")
Expand All @@ -330,16 +343,18 @@ def test_never_run_pipeline(page: Page, never_run_pipeline: Any):
_open_section(page, "loads")
expect(page.get_by_text(app_strings.loads_loading_failed_text[0:20])).to_be_visible()

_open_section(page, "ibis")
expect(page.get_by_text(app_strings.ibis_backend_error_text[0:20])).to_be_visible()
# _open_section(page, "ibis")
# expect(page.get_by_text(app_strings.ibis_backend_error_text[0:20])).to_be_visible()


def test_no_destination_pipeline(page: Page, no_destination_pipeline: Any):
# check no destination pipeline
_go_home(page)
page.get_by_role("link", name="no_destination_pipeline").click()

expect(page.get_by_text("_storage/.dlt/pipelines/no_destination_pipeline")).to_be_visible()
expect(
page.get_by_text(_normpath("_storage/.dlt/pipelines/no_destination_pipeline"))
).to_be_visible()

# check schema info (this is the yaml part)
_open_section(page, "schema")
Expand All @@ -365,5 +380,5 @@ def test_no_destination_pipeline(page: Page, no_destination_pipeline: Any):
page.get_by_text("execution_context").nth(0)
).to_be_visible() # this is only shown in trace yaml

_open_section(page, "ibis")
expect(page.get_by_text(app_strings.ibis_backend_error_text[0:20])).to_be_visible()
# _open_section(page, "ibis")
# expect(page.get_by_text(app_strings.ibis_backend_error_text[0:20])).to_be_visible()
49 changes: 49 additions & 0 deletions tests/helpers/dashboard/test_ui_elemens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import marimo as mo
import dlt


from dlt.helpers.dashboard.ui_elements import (
build_title_and_subtitle,
build_page_header,
build_error_callout,
)


def test_build_title_and_subtitle():
title = "Test Title"
subtitle = "Test Subtitle"
html = build_title_and_subtitle(title, subtitle).text
assert title in html
assert subtitle in html


def test_build_page_header(success_pipeline_duckdb: dlt.Pipeline):
title = "Test Title"
subtitle = "Test Subtitle"
subtitle_long = "Test Alternative Subtitle Long"

# not opened
html = build_page_header(
success_pipeline_duckdb, title, subtitle, subtitle_long, mo.ui.button()
)[0].text
assert title in html
assert subtitle in html
assert subtitle_long not in html

# opened
html = build_page_header(
success_pipeline_duckdb, title, subtitle, subtitle_long, mo.ui.button(value=True)
)[0].text
assert title in html
assert subtitle not in html
assert subtitle_long in html


def test_build_error_callout():
message = "Test Message"
code = "Test Code"
traceback_string = "Test Traceback String"
html = build_error_callout(message, code, traceback_string).text
assert message in html
assert code in html
assert traceback_string in html
Loading