Skip to content

Add e2e tests for login, survey and dashboard #681

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

Open
wants to merge 9 commits into
base: staging
Choose a base branch
from
48 changes: 40 additions & 8 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,59 @@
name: Playwright Tests
name : ZenML Playwright test pipelines

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
types: [opened, synchronize, ready_for_review]

jobs:
test:
timeout-minutes: 60
run_zenml_tests:
runs-on: ubuntu-latest

env:
ZENML_ANALYTICS_OPT_IN: "false"

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: lts/*

- name: Install dependencies
run: npm install -g pnpm && pnpm install

- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
- name: Run Playwright tests
run: pnpm exec playwright test

- name: Set up Python 3.12
uses: actions/setup-python@v4
with:
python-version: "3.12"

- name: Install ZenML
run: |
pip install --upgrade pip
pip install zenml[server]

- name: Verify ZenML Installation
run: zenml version

- name: Run ZenML Simple Pipeline
run: python e2e-tests/fixtures/simple-pipeline.py

- name: ZenMl up
run: |
zenml init
zenml up

- name: Run Playwright Tests
working-directory: e2e-tests
run: npx playwright test --workers=1

- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30


45 changes: 45 additions & 0 deletions e2e-tests/00-survey.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { test } from "@playwright/test";
import { login } from "./utils";

test.describe("Survey", () => {
test("Fill survey for first time", async ({ page }) => {
await login(page);

const isVisible = await page.locator("text=Add your account details").isVisible();

if (!isVisible) {
return;
}

//survey form - Step 1
await page.fill('input[name="fullName"]', "test");
await page.fill('input[name="email"]', "[email protected]");
await page
.getByLabel("I want to receive news and recommendations about how to use ZenML")
.check();
await page.click('button span:has-text("Continue")');
await page.waitForSelector("text=What will be your primary use for ZenML?");

//survey form - Step 2
await page.click('div label:has-text("Personal")');
await page.click('button span:has-text("Continue")');
await page.waitForSelector("text=What best describes your current situation with ZenML?");

//survey form - Step 3
await page.check('label:has-text("I\'m new to MLOps and exploring")');
await page.click('button span:has-text("Continue")');
await page.waitForSelector("text=What is your current infrastructure?");

//survey form - Step 4
await page.check('label:has-text("GCP") button');
await page.check('label:has-text("Azure")');
await page.check('label:has-text("Openshift")');
await page.check('label:has-text("AWS")');
await page.check('label:has-text("Native Kubernetes")');
await page.click('button span:has-text("Continue")');
await page.waitForSelector("text=Join The ZenML Slack Community");

//survey form - Step 5
await page.click('button span:has-text("Skip")');
});
});
8 changes: 8 additions & 0 deletions e2e-tests/01-login.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { test } from "@playwright/test";
import { login } from "./utils";

test.describe("Login", () => {
test("Login with default username", async ({ page }) => {
await login(page);
});
});
32 changes: 32 additions & 0 deletions e2e-tests/02-dashboard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { test, expect } from "@playwright/test";
import { login } from "./utils";

test.describe("Dashboard", () => {
test("Check dashboard and Navbar", async ({ page }) => {
await login(page);

const overviewLink = await page.locator('a:has-text("Overview")').isVisible();

if (!overviewLink) {
return;
}
Comment on lines +6 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using an assertion instead of an early return for the "Overview" link.

While checking for the "Overview" link visibility is a good practice, using an early return might lead to silently skipping important test assertions if the link is not visible.

Consider replacing the visibility check and early return with an assertion:

await expect(page.locator('a:has-text("Overview")')).toBeVisible({ timeout: 5000 });

This change will make the test fail explicitly if the "Overview" link is not visible, providing clearer feedback about the application's state.

//Visible the navbar
await expect(page.locator('a:has-text("Pipelines")')).toBeVisible();
await expect(page.locator('a:has-text("Models")')).toBeVisible();
await expect(page.locator('a:has-text("Artifacts")')).toBeVisible();
await expect(page.locator('a:has-text("Stacks")')).toBeVisible();

//Change the URL by clicking each nav item
await page.click('a:has-text("Pipelines")');
await expect(page).toHaveURL(/\/pipelines\?tab=pipelines/);

await page.click('a:has-text("Models")');
await expect(page).toHaveURL(/\/models/);

await page.click('a:has-text("Artifacts")');
await expect(page).toHaveURL(/\/artifacts/);

await page.click('a:has-text("Stacks")');
await expect(page).toHaveURL(/\/stacks/);
});
});
File renamed without changes.
22 changes: 22 additions & 0 deletions e2e-tests/fixtures/simple-pipeline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Annotated, Tuple
from zenml import pipeline, step


@step
def step_1() -> Tuple[int, Annotated[int, "custom_artifact_name"]]:
return 0, 1


@step
def step_2(input_0: int) -> None:
pass


@pipeline(enable_cache=False)
def ui_test_pipeline():
output_0, _ = step_1()
step_2(output_0)


if __name__ == "__main__":
ui_test_pipeline()
10 changes: 10 additions & 0 deletions e2e-tests/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Page, expect } from "@playwright/test";

// reusable login function
export async function login(page: Page) {
await page.goto("http://127.0.0.1:8237/");
await expect(page.locator('h1:has-text("Log in to your account")')).toBeVisible();
await page.fill('input[name="username"]', "default");
await page.fill('input[name="password"]', "");
await page.click('button span:has-text("Login")');
}
Loading