Skip to content

Commit

Permalink
Adds a basic UI test to trackio (#341)
Browse files Browse the repository at this point in the history
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
2 people authored and GitHub committed Nov 7, 2025
1 parent ddc27d9 commit 4fd841f
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/social-dancers-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"trackio": patch
---

feat:Adds a basic UI test to `trackio`
15 changes: 13 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,18 @@ jobs:
run: |
uv pip install --system -e .[dev,tensorboard]
uv pip install --system pytest
- name: Install Playwright
run: |
playwright install
- name: Run tests
- name: Run backend unit tests
run: |
pytest --deselect=tests/ui
- name: Run ui/ux interaction tests
if: matrix.os == 'ubuntu-latest'
run: |
pytest
pytest tests/ui
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ repository = "https://github.com/gradio-app/trackio"

[project.optional-dependencies]
dev = [
"pytest",
"ruff==0.9.3",
"pyarrow>=21.0",
"pytest>=8.0.0,<9.0.0",
"playwright>=1.40.0,<2.0.0",
"pytest-playwright>=0.7.0,<1.0.0",
]
tensorboard = [
"tbparse==0.0.9",
Expand Down
6 changes: 3 additions & 3 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_format_timestamp():


@pytest.mark.parametrize(
"base_url, project, write_token, expected",
"api_url, project, write_token, expected",
[
(
"https://example.com",
Expand All @@ -107,8 +107,8 @@ def test_format_timestamp():
),
],
)
def test_get_full_url(base_url, project, write_token, expected):
result = utils.get_full_url(base_url, project, write_token)
def test_get_full_url(api_url, project, write_token, expected):
result = utils.get_full_url(api_url, project, write_token)
assert result == expected


Expand Down
39 changes: 39 additions & 0 deletions tests/ui/test_ui_display.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from playwright.sync_api import expect, sync_playwright

import trackio


def test_that_runs_are_displayed(temp_dir):
trackio.init(project="test_project", name="test_run")
trackio.log(metrics={"loss": 0.1})
trackio.log(metrics={"loss": 0.2, "acc": 0.9})
trackio.finish()

app, url, _, _ = trackio.show(block_thread=False, open_browser=False)

try:
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_default_timeout(1000)
page.goto(url)

# The project name and run name should be displayed
locator = page.get_by_label("Project")
expect(locator).to_be_visible()
locator = page.get_by_text("test_run")
expect(locator).to_be_visible()

# Initially, two line plots should be displayed
locator = page.locator(".vega-embed")
expect(locator).to_have_count(2)

# But if we uncheck the run, the line plots should be hidden
page.get_by_label("test_run").uncheck()
locator = page.locator(".vega-embed")
expect(locator).to_have_count(0)

browser.close()
finally:
trackio.delete_project("test_project", force=True)
app.close()
74 changes: 71 additions & 3 deletions trackio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from gradio.blocks import BUILT_IN_THEMES
from gradio.themes import Default as DefaultTheme
from gradio.themes import ThemeClass
from gradio.utils import TupleNoPrint
from gradio_client import Client
from huggingface_hub import SpaceStorage

Expand Down Expand Up @@ -41,6 +42,7 @@
"log",
"finish",
"show",
"delete_project",
"import_csv",
"import_tf_events",
"Image",
Expand Down Expand Up @@ -261,10 +263,57 @@ def finish():
run.finish()


def delete_project(project: str, force: bool = False) -> bool:
"""
Deletes a project by removing its local SQLite database.
Args:
project (`str`):
The name of the project to delete.
force (`bool`, *optional*, defaults to `False`):
If `True`, deletes the project without prompting for confirmation.
If `False`, prompts the user to confirm before deleting.
Returns:
`bool`: `True` if the project was deleted, `False` otherwise.
"""
db_path = SQLiteStorage.get_project_db_path(project)

if not db_path.exists():
print(f"* Project '{project}' does not exist.")
return False

if not force:
response = input(
f"Are you sure you want to delete project '{project}'? "
f"This will permanently delete all runs and metrics. (y/N): "
)
if response.lower() not in ["y", "yes"]:
print("* Deletion cancelled.")
return False

try:
db_path.unlink()

for suffix in ("-wal", "-shm"):
sidecar = Path(str(db_path) + suffix)
if sidecar.exists():
sidecar.unlink()

print(f"* Project '{project}' has been deleted.")
return True
except Exception as e:
print(f"* Error deleting project '{project}': {e}")
return False


def show(
project: str | None = None,
theme: str | ThemeClass | None = None,
mcp_server: bool | None = None,
*,
open_browser: bool = True,
block_thread: bool | None = None,
):
"""
Launches the Trackio dashboard.
Expand All @@ -284,6 +333,19 @@ def show(
functions will be added as MCP tools. If `None` (default behavior), then the
`GRADIO_MCP_SERVER` environment variable will be used to determine if the
MCP server should be enabled (which is `"True"` on Hugging Face Spaces).
open_browser (`bool`, *optional*, defaults to `True`):
If `True` and not in a notebook, a new browser tab will be opened with the dashboard.
If `False`, the browser will not be opened.
block_thread (`bool`, *optional*):
If `True`, the main thread will be blocked until the dashboard is closed.
If `None` (default behavior), then the main thread will not be blocked if the
dashboard is launched in a notebook, otherwise the main thread will be blocked.
Returns:
`app`: The Gradio app object corresponding to the dashboard launched by Trackio.
`url`: The local URL of the dashboard.
`share_url`: The public share URL of the dashboard.
`full_url`: The full URL of the dashboard including the write token (will use the public share URL if launched publicly, otherwise the local URL).
"""
theme = theme or os.environ.get("TRACKIO_THEME", DEFAULT_THEME)

Expand Down Expand Up @@ -316,7 +378,7 @@ def show(
else os.environ.get("GRADIO_MCP_SERVER", "False") == "True"
)

_, url, share_url = demo.launch(
app, url, share_url = demo.launch(
show_api=_mcp_server,
quiet=True,
inline=False,
Expand All @@ -333,7 +395,13 @@ def show(

if not utils.is_in_notebook():
print(f"* Trackio UI launched at: {full_url}")
webbrowser.open(full_url)
utils.block_main_thread_until_keyboard_interrupt()
if open_browser:
webbrowser.open(full_url)
block_thread = block_thread if block_thread is not None else True
else:
utils.embed_url_in_notebook(full_url)
block_thread = block_thread if block_thread is not None else False

if block_thread:
utils.block_main_thread_until_keyboard_interrupt()
return TupleNoPrint((demo, url, share_url, full_url))

0 comments on commit 4fd841f

Please sign in to comment.