154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
"""
|
|
Discover Playwright tests (.spec.ts files).
|
|
"""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass
|
|
class PlaywrightTestInfo:
|
|
"""Information about a discovered Playwright test."""
|
|
id: str
|
|
name: str
|
|
file_path: str
|
|
test_name: str
|
|
description: Optional[str] = None
|
|
gherkin_feature: Optional[str] = None
|
|
gherkin_scenario: Optional[str] = None
|
|
tags: list[str] = None
|
|
|
|
def __post_init__(self):
|
|
if self.tags is None:
|
|
self.tags = []
|
|
|
|
|
|
def discover_playwright_tests(tests_dir: Path) -> list[PlaywrightTestInfo]:
|
|
"""
|
|
Discover all Playwright tests in the frontend-tests directory.
|
|
|
|
Parses .spec.ts files to extract:
|
|
- test() calls
|
|
- describe() blocks
|
|
- Gherkin metadata from comments
|
|
- Tags from comments
|
|
"""
|
|
if not tests_dir.exists():
|
|
return []
|
|
|
|
tests = []
|
|
|
|
# Find all .spec.ts files
|
|
for spec_file in tests_dir.rglob("*.spec.ts"):
|
|
relative_path = spec_file.relative_to(tests_dir)
|
|
|
|
# Read file content
|
|
try:
|
|
content = spec_file.read_text()
|
|
except Exception:
|
|
continue
|
|
|
|
# Extract describe blocks and tests
|
|
tests_in_file = _parse_playwright_file(content, spec_file, relative_path)
|
|
tests.extend(tests_in_file)
|
|
|
|
return tests
|
|
|
|
|
|
def _parse_playwright_file(
|
|
content: str,
|
|
file_path: Path,
|
|
relative_path: Path
|
|
) -> list[PlaywrightTestInfo]:
|
|
"""Parse a Playwright test file to extract test information."""
|
|
tests = []
|
|
|
|
# Pattern to match test() calls
|
|
# test('test name', async ({ page }) => { ... })
|
|
# test.only('test name', ...)
|
|
test_pattern = re.compile(
|
|
r"test(?:\.\w+)?\s*\(\s*['\"]([^'\"]+)['\"]",
|
|
re.MULTILINE
|
|
)
|
|
|
|
# Pattern to match describe() blocks
|
|
describe_pattern = re.compile(
|
|
r"describe\s*\(\s*['\"]([^'\"]+)['\"]",
|
|
re.MULTILINE
|
|
)
|
|
|
|
# Extract metadata from comments above tests
|
|
# Looking for JSDoc-style comments with metadata
|
|
metadata_pattern = re.compile(
|
|
r"/\*\*\s*\n((?:\s*\*.*\n)+)\s*\*/\s*\n\s*test",
|
|
re.MULTILINE
|
|
)
|
|
|
|
# Find all describe blocks to use as context
|
|
describes = describe_pattern.findall(content)
|
|
describe_context = describes[0] if describes else None
|
|
|
|
# Find all tests
|
|
for match in test_pattern.finditer(content):
|
|
test_name = match.group(1)
|
|
|
|
# Look for metadata comment before this test
|
|
# Search backwards from the match position
|
|
before_test = content[:match.start()]
|
|
metadata_match = None
|
|
for m in metadata_pattern.finditer(before_test):
|
|
metadata_match = m
|
|
|
|
# Parse metadata if found
|
|
gherkin_feature = None
|
|
gherkin_scenario = None
|
|
tags = []
|
|
description = None
|
|
|
|
if metadata_match:
|
|
metadata_block = metadata_match.group(1)
|
|
|
|
# Extract Feature, Scenario, Tags from metadata
|
|
feature_match = re.search(r"\*\s*Feature:\s*(.+)", metadata_block)
|
|
scenario_match = re.search(r"\*\s*Scenario:\s*(.+)", metadata_block)
|
|
tags_match = re.search(r"\*\s*Tags:\s*(.+)", metadata_block)
|
|
desc_match = re.search(r"\*\s*@description\s+(.+)", metadata_block)
|
|
|
|
if feature_match:
|
|
gherkin_feature = feature_match.group(1).strip()
|
|
if scenario_match:
|
|
gherkin_scenario = scenario_match.group(1).strip()
|
|
if tags_match:
|
|
tags_str = tags_match.group(1).strip()
|
|
tags = [t.strip() for t in re.findall(r"@[\w-]+", tags_str)]
|
|
if desc_match:
|
|
description = desc_match.group(1).strip()
|
|
|
|
# Build test ID
|
|
module_name = str(relative_path).replace("/", ".").replace(".spec.ts", "")
|
|
test_id = f"frontend.{module_name}.{_sanitize_test_name(test_name)}"
|
|
|
|
tests.append(PlaywrightTestInfo(
|
|
id=test_id,
|
|
name=test_name,
|
|
file_path=str(relative_path),
|
|
test_name=test_name,
|
|
description=description or test_name,
|
|
gherkin_feature=gherkin_feature,
|
|
gherkin_scenario=gherkin_scenario,
|
|
tags=tags,
|
|
))
|
|
|
|
return tests
|
|
|
|
|
|
def _sanitize_test_name(name: str) -> str:
|
|
"""Convert test name to a valid identifier."""
|
|
# Replace spaces and special chars with underscores
|
|
sanitized = re.sub(r"[^\w]+", "_", name.lower())
|
|
# Remove leading/trailing underscores
|
|
sanitized = sanitized.strip("_")
|
|
return sanitized
|