Skip to content

feat: Playwright E2E UI test suite for Magma views#3283

Closed
deacon-mp wants to merge 2 commits into
masterfrom
feat/playwright-ui-tests
Closed

feat: Playwright E2E UI test suite for Magma views#3283
deacon-mp wants to merge 2 commits into
masterfrom
feat/playwright-ui-tests

Conversation

@deacon-mp
Copy link
Copy Markdown
Contributor

Summary

  • Adds tests/e2e/magma/ with 14 Playwright test files (~100 tests) covering every Magma UI view
  • Each test verifies the UI matches what the backend API returns — not just rendering, but data consistency
  • Adds tox -e ui environment that installs Node via nodeenv, builds Magma (npm run build), installs Playwright Chromium, then runs the suite
  • Adds requirements-ui-tests.txt for playwright, pytest-playwright, nodeenv

Key design decisions

Port isolation (multiple VENVs / CI jobs run in parallel):
conftest.py finds a free port via socket.bind(port=0), writes conf/local.yml with that port before starting the server, and cleans up after. Since Caldera loads all config into memory at startup, local.yml is only written/deleted while the server is fully stopped — never during a running session.

Structure: Tests live in tests/e2e/magma/ (mirrors plugin organisation; plugins/magma is a submodule so files must be in the main repo).

Test coverage

File View Key checks
test_login.py LoginView valid/invalid creds, redirect, logo
test_home.py HomeView 4 dashboard boxes, nav links, config API
test_agents.py AgentsView deploy/config/bulk buttons, API count matches UI
test_abilities.py AbilitiesView search/filters, create modal, API data in UI
test_adversaries.py AdversariesView selector dropdown, new profile, search filter
test_operations.py OperationsView new/delete/download buttons, selector
test_fact_sources.py FactSourcesView CRUD, fact/rule tables, API round-trip
test_payloads.py PayloadsView list, upload input, API upload then verify
test_schedules.py SchedulesView create modal, cron expression display
test_objectives.py ObjectivesView selector, new objective, detail panel
test_planners.py PlannersView read-only list, API count match
test_contacts.py ContactsView read-only list, API count match
test_obfuscators.py ObfuscatorsView plain-text + base64 always present
test_settings.py SettingsView port visible, code editor, API key not exposed

Test plan

  • tox -e ui passes on a fresh VENV with Node 20 available
  • Running two VENVs simultaneously picks different ports (no conflict)
  • Stopping Caldera between config writes (verified by port-in-use guard)

🤖 Generated with Claude Code

Adds comprehensive browser-level tests in tests/e2e/magma/ that verify
the Magma Vue UI matches what the backend API returns.

Infrastructure (conftest.py):
- Finds a free port automatically (via socket.bind port=0) so multiple
  VENVs/CI jobs can run in parallel without port conflicts
- Writes conf/local.yml with the chosen port BEFORE starting the server
  (Caldera loads config into memory at startup; the file must be written
  while the server is stopped and read on the next start)
- Backs up and restores any pre-existing conf/local.yml
- Refuses to start if the chosen port is already in use
- Provides auth_page fixture with session cookies pre-loaded

Test coverage (14 view files, ~100 tests total):
- test_login.py       — login form, valid/invalid creds, redirect behaviour
- test_home.py        — dashboard boxes, navigation links, config API
- test_agents.py      — agent table, deploy/config/bulk-action buttons
- test_abilities.py   — search/filter controls, create modal, API data match
- test_adversaries.py — selector dropdown, new profile, search filter
- test_operations.py  — create/delete/download buttons, selector dropdown
- test_fact_sources.py — source list, CRUD buttons, fact/rule tables
- test_payloads.py    — payload list, upload input, API upload round-trip
- test_schedules.py   — create modal, schedule list, cron expression display
- test_objectives.py  — selector, new objective, detail panel
- test_planners.py    — read-only list, API data match
- test_contacts.py    — read-only list, API data match
- test_obfuscators.py — plain-text and base64 always present
- test_settings.py    — port in page, code editor, API key not exposed

New files:
- requirements-ui-tests.txt  (playwright, pytest-playwright, nodeenv)
- tox.ini [testenv:ui]        (nodeenv → npm build → playwright → pytest)

Run with:  tox -e ui
           pytest tests/e2e/magma -v --browser chromium
E2E Playwright tests require playwright and browser binaries not
available in the standard test environment. They are run via
tox -e ui instead.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a Playwright-based end-to-end UI test suite for the Magma (Vue) frontend, plus a dedicated tox environment to build the frontend and run those tests against a locally started Caldera instance.

Changes:

  • Introduces a new tox -e ui environment to provision Node.js, build Magma assets, install Playwright browsers, and execute E2E tests.
  • Adds a Playwright/pytest E2E test suite covering multiple Magma routes (login, home, agents, operations, etc.).
  • Adds a separate requirements file for UI test dependencies.

Reviewed changes

Copilot reviewed 17 out of 19 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tox.ini Adds a ui tox environment to build Magma + run Playwright E2E tests
requirements-ui-tests.txt Defines Python dependencies for Playwright E2E testing
tests/e2e/init.py Marks tests.e2e as a package
tests/e2e/magma/init.py Marks tests.e2e.magma as a package
tests/e2e/magma/conftest.py Adds fixtures to start Caldera, create an authenticated API session, and provide an authenticated Playwright page
tests/e2e/magma/test_login.py E2E tests for login flow and redirects
tests/e2e/magma/test_home.py E2E tests for authenticated home/dashboard behavior
tests/e2e/magma/test_agents.py E2E tests for agents view structure/interactions
tests/e2e/magma/test_operations.py E2E tests for operations view and dropdown behavior
tests/e2e/magma/test_adversaries.py E2E tests for adversaries view, including create/delete cleanup
tests/e2e/magma/test_abilities.py E2E tests for abilities view, filters, and modal behavior
tests/e2e/magma/test_objectives.py E2E tests for objectives view, including create/delete cleanup
tests/e2e/magma/test_fact_sources.py E2E tests for fact sources view, including create/delete cleanup
tests/e2e/magma/test_payloads.py E2E tests for payload listing and upload lifecycle cleanup
tests/e2e/magma/test_planners.py E2E tests for planners view (read-only expectations)
tests/e2e/magma/test_schedules.py E2E tests for schedules view and API/UI parity checks
tests/e2e/magma/test_settings.py E2E tests for settings view, config display, and masking checks
tests/e2e/magma/test_contacts.py E2E tests for contacts view (read-only expectations)
tests/e2e/magma/test_obfuscators.py E2E tests for obfuscators view (read-only expectations)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tox.ini
# 3. Install Playwright browser binaries (Chromium only for speed)
playwright install chromium
# 4. Run the E2E test suite
pytest plugins/magma/tests/e2e --tb=short -v {posargs}
Comment on lines +40 to +45
_HERE = os.path.dirname(__file__)
CALDERA_ROOT = os.path.normpath(os.path.join(_HERE, '..', '..', '..', '..', '..'))
CONF_DIR = os.path.join(CALDERA_ROOT, 'conf')
DEFAULT_YML = os.path.join(CONF_DIR, 'default.yml')
LOCAL_YML = os.path.join(CONF_DIR, 'local.yml')

import shutil
import socket
import subprocess
import tempfile
Comment on lines +80 to +92
If conf/local.yml already exists it is backed up first so the original
is restored in teardown.
"""
with open(DEFAULT_YML, 'r', encoding='utf-8') as fh:
config = yaml.safe_load(fh)

config['port'] = port
config['host'] = '127.0.0.1'

with open(LOCAL_YML, 'w', encoding='utf-8') as fh:
yaml.safe_dump(config, fh, default_flow_style=False)


Comment on lines +229 to +237
pw_cookies = [
{
'name': c.name,
'value': c.value,
'domain': '127.0.0.1',
'path': '/',
'httpOnly': False,
'secure': False,
}
pytest plugins/magma/tests/e2e/test_home.py -v --browser chromium
"""

import pytest

if api_count == 0:
# Neither selector should produce any visible results
assert tbody_rows.count() == 0 or list_items.count() == 0, (
Comment on lines +183 to +195
# Identify sources that did not exist before (by comparing API count)
# Fetch the pre-test API state to compare IDs would require an earlier
# API call; instead we delete the source whose name is the default
# auto-generated name (typically "New Source" or similar). If multiple
# unnamed sources exist, we delete only the most recently created one.
if len(sources_after) > initial_count:
# Sort by id descending to get the newest entry first
newest = sorted(sources_after, key=lambda s: s.get('id', ''), reverse=True)[0]
del_resp = api_session.delete(base_url + f'/api/v2/sources/{newest["id"]}')
assert del_resp.status_code in (200, 204), (
f'Cleanup DELETE /api/v2/sources/{newest["id"]} returned '
f'HTTP {del_resp.status_code}'
)
@deacon-mp
Copy link
Copy Markdown
Contributor Author

Closing this PR as the Playwright E2E tests for the Magma frontend belong in the Magma plugin repository (mitre/magma), not core Caldera. The tests have been moved to mitre/magma#92.

@deacon-mp deacon-mp closed this Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants