diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 5648dea6..22e8a5e1 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -73,14 +73,10 @@ jobs:
run: sudo apt update && sudo apt install --no-install-recommends libfftw3-dev quantum-espresso
- name: Install Python dependencies
- run: pip install -e .[pre-commit,tests]
-
- - name: Copy custom node file, add custom node path to sys path
run: |
- mkdir ~/.scinode
- mkdir ~/.scinode/custom_node
- export PYTHONPATH=~/.scinode/custom_node/:$PYTHONPATH
- echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV
+ pip install -e .[pre-commit,tests]
+ playwright install
+
- name: Create AiiDA profile
run: verdi setup -n --config .github/config/profile.yaml
diff --git a/.gitignore b/.gitignore
index 1040b180..c7697311 100644
--- a/.gitignore
+++ b/.gitignore
@@ -131,3 +131,4 @@ dmypy.json
#
*.pdf
tests/work
+/tests/**/*.png
diff --git a/aiida_worktree/web/frontend/public/index.html b/aiida_worktree/web/frontend/public/index.html
index aa069f27..b2c0d7c8 100644
--- a/aiida_worktree/web/frontend/public/index.html
+++ b/aiida_worktree/web/frontend/public/index.html
@@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
-
React App
+ AiiDA-WorkTree App
diff --git a/pyproject.toml b/pyproject.toml
index 28294757..ed2924c3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -58,6 +58,7 @@ pre-commit = [
tests = [
"pytest~=7.0",
"pytest-cov~=2.7,<2.11",
+ "playwright",
]
[project.scripts]
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 00000000..9b1367fa
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,8 @@
+
+## Requirements
+
+In order to run the test for the web frontend, one need to install
+
+```console
+playwright install
+```
diff --git a/tests/web/conftest.py b/tests/web/conftest.py
new file mode 100644
index 00000000..09e765cf
--- /dev/null
+++ b/tests/web/conftest.py
@@ -0,0 +1,27 @@
+import pytest
+from fastapi.testclient import TestClient
+from aiida_worktree.web.backend.app.api import app
+from playwright.sync_api import sync_playwright
+
+
+# Define a fixture for the FastAPI app client
+@pytest.fixture(scope="module")
+def client():
+ return TestClient(app)
+
+
+# Define a fixture for the browser
+@pytest.fixture(scope="module")
+def browser():
+ with sync_playwright() as p:
+ browser = p.chromium.launch()
+ yield browser
+ browser.close()
+
+
+# Define a fixture for the page
+@pytest.fixture(scope="module")
+def page(browser):
+ with browser.new_page() as page:
+ yield page
+ page.close()
diff --git a/tests/web/test_backend.py b/tests/web/test_backend.py
new file mode 100644
index 00000000..f563720d
--- /dev/null
+++ b/tests/web/test_backend.py
@@ -0,0 +1,9 @@
+# Sample test case for the root route
+def test_root_route(client):
+ response = client.get("/api")
+ assert response.status_code == 200
+ assert response.json() == {"message": "Welcome to AiiDA-WorkTree."}
+
+
+# Add more test cases for your other routes and features
+# For example, testing authentication, database interactions, etc.
diff --git a/tests/web/test_frontend.py b/tests/web/test_frontend.py
new file mode 100644
index 00000000..506b1e90
--- /dev/null
+++ b/tests/web/test_frontend.py
@@ -0,0 +1,63 @@
+import pytest
+
+
+def test_homepage(page):
+ page.goto("http://localhost:3000")
+
+ assert page.title() == "AiiDA-WorkTree App"
+
+ # Check for the existence of a specific element on the page
+ # Attempt to locate the element
+ element = page.locator("a[href='/worktree']")
+
+ # Check if the element is found
+ if not element.is_visible():
+ pytest.fail("Element 'a[href='/wortre']' not found on the page")
+
+
+def test_worktree(page):
+ page.goto("http://localhost:3000/worktree")
+
+ # Check for the existence of a specific element on the page
+
+ # Verify the presence of the WorkTreeTable heading
+ assert page.locator("h2").inner_text() == "WorkTree"
+
+ # Verify the presence of the search input
+ assert page.locator(".search-input").is_visible()
+
+ # Verify the presence of the table header columns
+ # Verify the presence of the table header columns
+ assert page.locator("th:has-text('PK')").is_visible()
+ assert page.locator("th:has-text('Created')").is_visible()
+ assert page.locator("th:has-text('Process Label')").is_visible()
+ assert page.locator("th:has-text('State')").is_visible()
+ assert page.locator("th:has-text('Actions')").is_visible()
+
+ # Verify the presence of pagination controls
+ assert page.locator(".pagination").is_visible()
+
+ # Verify the presence of at least one row in the table
+ assert page.locator("tr").count() >= 2 # Including header row
+
+
+def test_worktree_item(page, wt_calcfunction):
+
+ wt = wt_calcfunction
+ wt.submit(wait=True)
+ page.goto("http://localhost:3000/worktree/{}".format(wt.pk))
+ page.wait_for_timeout(3000)
+
+ page.get_by_text("sumdiff3").is_visible()
+
+ # Simulate user interaction (e.g., clicking a button)
+ # Replace the selector with the actual selector of the button you want to click
+ # You should identify the button that triggers an action in your component
+ page.get_by_role("button", name="Arrange").click()
+ page.wait_for_timeout(3000)
+ # Capture a screenshot
+ screenshot = page.screenshot()
+
+ # Save the screenshot to a file
+ with open("screenshot.png", "wb") as f:
+ f.write(screenshot)