tester fully decoupled
This commit is contained in:
17
build.py
17
build.py
@@ -271,16 +271,17 @@ def copy_cfg(output_dir: Path, room: str):
|
|||||||
# Now in cfg/<room>/soleprint/
|
# Now in cfg/<room>/soleprint/
|
||||||
room_soleprint = room_cfg / "soleprint"
|
room_soleprint = room_cfg / "soleprint"
|
||||||
if room_soleprint.exists():
|
if room_soleprint.exists():
|
||||||
|
systems = {"artery", "atlas", "station"}
|
||||||
for item in room_soleprint.iterdir():
|
for item in room_soleprint.iterdir():
|
||||||
if item.is_file():
|
if item.name in systems:
|
||||||
|
# Merge system dirs into already-copied framework code
|
||||||
|
log.info(f" Merging {room} {item.name}...")
|
||||||
|
merge_into(item, output_dir / item.name)
|
||||||
|
elif item.is_file():
|
||||||
|
copy_path(item, output_dir / item.name)
|
||||||
|
elif item.is_dir():
|
||||||
|
# Copy non-system dirs as-is (nginx/, etc.)
|
||||||
copy_path(item, output_dir / item.name)
|
copy_path(item, output_dir / item.name)
|
||||||
|
|
||||||
# Merge room-specific system configs from soleprint subfolder
|
|
||||||
for system in ["artery", "atlas", "station"]:
|
|
||||||
room_system = room_soleprint / system
|
|
||||||
if room_system.exists():
|
|
||||||
log.info(f" Merging {room} {system}...")
|
|
||||||
merge_into(room_system, output_dir / system)
|
|
||||||
|
|
||||||
|
|
||||||
def build_soleprint(output_dir: Path, room: str):
|
def build_soleprint(output_dir: Path, room: str):
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
|
|||||||
@app.get("/", response_class=HTMLResponse)
|
@app.get("/", response_class=HTMLResponse)
|
||||||
def index(request: Request):
|
def index(request: Request):
|
||||||
"""Mock configuration UI."""
|
"""Mock configuration UI."""
|
||||||
return templates.TemplateResponse("index.html", {"request": request})
|
return templates.TemplateResponse(request, "index.html")
|
||||||
|
|
||||||
# Include router at root (matches real Amar API structure)
|
# Include router at root (matches real Amar API structure)
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
|
|||||||
@@ -1,164 +1,4 @@
|
|||||||
"""
|
"""Re-export from parent — room tests import from here."""
|
||||||
Pure HTTP Contract Tests - Base Class
|
from ..base import ContractTestCase, get_base_url
|
||||||
|
|
||||||
Framework-agnostic: works against ANY backend implementation.
|
__all__ = ["ContractTestCase", "get_base_url"]
|
||||||
Does NOT manage database - expects a ready environment.
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
- Server running at CONTRACT_TEST_URL
|
|
||||||
- Database migrated and seeded
|
|
||||||
- Test user exists OR CONTRACT_TEST_TOKEN provided
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
CONTRACT_TEST_URL=http://127.0.0.1:8000 pytest
|
|
||||||
CONTRACT_TEST_TOKEN=your_jwt_token pytest
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import unittest
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from .endpoints import Endpoints
|
|
||||||
|
|
||||||
|
|
||||||
def get_base_url():
|
|
||||||
"""Get base URL from environment (required)"""
|
|
||||||
url = os.environ.get("CONTRACT_TEST_URL", "")
|
|
||||||
if not url:
|
|
||||||
raise ValueError("CONTRACT_TEST_URL environment variable required")
|
|
||||||
return url.rstrip("/")
|
|
||||||
|
|
||||||
|
|
||||||
class ContractTestCase(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Base class for pure HTTP contract tests.
|
|
||||||
|
|
||||||
Features:
|
|
||||||
- Framework-agnostic (works with Django, FastAPI, Node, etc.)
|
|
||||||
- Pure HTTP via requests library
|
|
||||||
- No database access - all data through API
|
|
||||||
- JWT authentication
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Auth credentials - override via environment
|
|
||||||
TEST_USER_EMAIL = os.environ.get("CONTRACT_TEST_USER", "contract_test@example.com")
|
|
||||||
TEST_USER_PASSWORD = os.environ.get("CONTRACT_TEST_PASSWORD", "testpass123")
|
|
||||||
|
|
||||||
# Class-level cache
|
|
||||||
_base_url = None
|
|
||||||
_token = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
"""Set up once per test class"""
|
|
||||||
super().setUpClass()
|
|
||||||
cls._base_url = get_base_url()
|
|
||||||
|
|
||||||
# Use provided token or fetch one
|
|
||||||
cls._token = os.environ.get("CONTRACT_TEST_TOKEN", "")
|
|
||||||
if not cls._token:
|
|
||||||
cls._token = cls._fetch_token()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _fetch_token(cls):
|
|
||||||
"""Get JWT token for authentication"""
|
|
||||||
url = f"{cls._base_url}{Endpoints.TOKEN}"
|
|
||||||
try:
|
|
||||||
response = httpx.post(url, json={
|
|
||||||
"username": cls.TEST_USER_EMAIL,
|
|
||||||
"password": cls.TEST_USER_PASSWORD,
|
|
||||||
}, timeout=10)
|
|
||||||
if response.status_code == 200:
|
|
||||||
return response.json().get("access", "")
|
|
||||||
else:
|
|
||||||
print(f"Warning: Token request failed with {response.status_code}")
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
print(f"Warning: Token request failed: {e}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def base_url(self):
|
|
||||||
return self._base_url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def token(self):
|
|
||||||
return self._token
|
|
||||||
|
|
||||||
def _auth_headers(self):
|
|
||||||
"""Get authorization headers"""
|
|
||||||
if self.token:
|
|
||||||
return {"Authorization": f"Bearer {self.token}"}
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# =========================================================================
|
|
||||||
# HTTP helpers
|
|
||||||
# =========================================================================
|
|
||||||
|
|
||||||
def get(self, path: str, params: dict = None, **kwargs):
|
|
||||||
"""GET request"""
|
|
||||||
url = f"{self.base_url}{path}"
|
|
||||||
headers = {**self._auth_headers(), **kwargs.pop("headers", {})}
|
|
||||||
response = httpx.get(url, params=params, headers=headers, timeout=30, **kwargs)
|
|
||||||
return self._wrap_response(response)
|
|
||||||
|
|
||||||
def post(self, path: str, data: dict = None, **kwargs):
|
|
||||||
"""POST request with JSON"""
|
|
||||||
url = f"{self.base_url}{path}"
|
|
||||||
headers = {**self._auth_headers(), **kwargs.pop("headers", {})}
|
|
||||||
response = httpx.post(url, json=data, headers=headers, timeout=30, **kwargs)
|
|
||||||
return self._wrap_response(response)
|
|
||||||
|
|
||||||
def put(self, path: str, data: dict = None, **kwargs):
|
|
||||||
"""PUT request with JSON"""
|
|
||||||
url = f"{self.base_url}{path}"
|
|
||||||
headers = {**self._auth_headers(), **kwargs.pop("headers", {})}
|
|
||||||
response = httpx.put(url, json=data, headers=headers, timeout=30, **kwargs)
|
|
||||||
return self._wrap_response(response)
|
|
||||||
|
|
||||||
def patch(self, path: str, data: dict = None, **kwargs):
|
|
||||||
"""PATCH request with JSON"""
|
|
||||||
url = f"{self.base_url}{path}"
|
|
||||||
headers = {**self._auth_headers(), **kwargs.pop("headers", {})}
|
|
||||||
response = httpx.patch(url, json=data, headers=headers, timeout=30, **kwargs)
|
|
||||||
return self._wrap_response(response)
|
|
||||||
|
|
||||||
def delete(self, path: str, **kwargs):
|
|
||||||
"""DELETE request"""
|
|
||||||
url = f"{self.base_url}{path}"
|
|
||||||
headers = {**self._auth_headers(), **kwargs.pop("headers", {})}
|
|
||||||
response = httpx.delete(url, headers=headers, timeout=30, **kwargs)
|
|
||||||
return self._wrap_response(response)
|
|
||||||
|
|
||||||
def _wrap_response(self, response):
|
|
||||||
"""Add .data attribute for consistency with DRF responses"""
|
|
||||||
try:
|
|
||||||
response.data = response.json()
|
|
||||||
except Exception:
|
|
||||||
response.data = None
|
|
||||||
return response
|
|
||||||
|
|
||||||
# =========================================================================
|
|
||||||
# Assertion helpers
|
|
||||||
# =========================================================================
|
|
||||||
|
|
||||||
def assert_status(self, response, expected_status: int):
|
|
||||||
"""Assert response has expected status code"""
|
|
||||||
self.assertEqual(
|
|
||||||
response.status_code,
|
|
||||||
expected_status,
|
|
||||||
f"Expected {expected_status}, got {response.status_code}. "
|
|
||||||
f"Response: {response.data if hasattr(response, 'data') else response.content[:500]}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def assert_has_fields(self, data: dict, *fields: str):
|
|
||||||
"""Assert dictionary has all specified fields"""
|
|
||||||
missing = [f for f in fields if f not in data]
|
|
||||||
self.assertEqual(missing, [], f"Missing fields: {missing}. Got: {list(data.keys())}")
|
|
||||||
|
|
||||||
def assert_is_list(self, data, min_length: int = 0):
|
|
||||||
"""Assert data is a list with minimum length"""
|
|
||||||
self.assertIsInstance(data, list)
|
|
||||||
self.assertGreaterEqual(len(data), min_length)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["ContractTestCase"]
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
|
|||||||
@app.get("/", response_class=HTMLResponse)
|
@app.get("/", response_class=HTMLResponse)
|
||||||
def index(request: Request):
|
def index(request: Request):
|
||||||
"""Mock configuration UI."""
|
"""Mock configuration UI."""
|
||||||
return templates.TemplateResponse("index.html", {"request": request})
|
return templates.TemplateResponse(request, "index.html")
|
||||||
|
|
||||||
# Include router at root (matches real MercadoPago API structure)
|
# Include router at root (matches real MercadoPago API structure)
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
|
|||||||
@@ -512,9 +512,17 @@ def station_index(request: Request):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Mount station tool routers
|
||||||
|
try:
|
||||||
|
from station.tools.tester.api import router as tester_router
|
||||||
|
app.include_router(tester_router, prefix="/station")
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"Warning: Could not load tester router: {e}")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/station/{path:path}")
|
@app.get("/station/{path:path}")
|
||||||
def station_route(path: str):
|
def station_route(path: str):
|
||||||
"""Station sub-routes."""
|
"""Station sub-routes (fallback)."""
|
||||||
return {"system": "station", "path": path}
|
return {"system": "station", "path": path}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
FastAPI router for tester tool.
|
FastAPI router for tester tool.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -69,8 +70,7 @@ def index(request: Request):
|
|||||||
tests_tree = get_tests_tree()
|
tests_tree = get_tests_tree()
|
||||||
tests_list = discover_tests()
|
tests_list = discover_tests()
|
||||||
|
|
||||||
return templates.TemplateResponse("index.html", {
|
return templates.TemplateResponse(request, "index.html", context={
|
||||||
"request": request,
|
|
||||||
"config": config,
|
"config": config,
|
||||||
"tests_tree": tests_tree,
|
"tests_tree": tests_tree,
|
||||||
"total_tests": len(tests_list),
|
"total_tests": len(tests_list),
|
||||||
@@ -86,8 +86,7 @@ def health():
|
|||||||
@router.get("/filters", response_class=HTMLResponse)
|
@router.get("/filters", response_class=HTMLResponse)
|
||||||
def test_filters(request: Request):
|
def test_filters(request: Request):
|
||||||
"""Show filterable test view with multiple filter options."""
|
"""Show filterable test view with multiple filter options."""
|
||||||
return templates.TemplateResponse("filters.html", {
|
return templates.TemplateResponse(request, "filters.html", context={
|
||||||
"request": request,
|
|
||||||
"config": config,
|
"config": config,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -95,8 +94,7 @@ def test_filters(request: Request):
|
|||||||
@router.get("/filters_v2", response_class=HTMLResponse)
|
@router.get("/filters_v2", response_class=HTMLResponse)
|
||||||
def test_filters_v2(request: Request):
|
def test_filters_v2(request: Request):
|
||||||
"""Show Gherkin-driven filter view (v2 with pulse variables)."""
|
"""Show Gherkin-driven filter view (v2 with pulse variables)."""
|
||||||
return templates.TemplateResponse("filters_v2.html", {
|
return templates.TemplateResponse(request, "filters_v2.html", context={
|
||||||
"request": request,
|
|
||||||
"config": config,
|
"config": config,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -140,10 +138,18 @@ def select_environment(env_id: str):
|
|||||||
if not env:
|
if not env:
|
||||||
raise HTTPException(status_code=404, detail=f"Environment {env_id} not found")
|
raise HTTPException(status_code=404, detail=f"Environment {env_id} not found")
|
||||||
|
|
||||||
# Update config (in memory for this session)
|
# Update config and env vars (tests read from os.environ)
|
||||||
config["CONTRACT_TEST_URL"] = env["url"]
|
config["CONTRACT_TEST_URL"] = env["url"]
|
||||||
config["CONTRACT_TEST_API_KEY"] = env.get("api_key", "")
|
config["CONTRACT_TEST_API_KEY"] = env.get("api_key", "")
|
||||||
|
|
||||||
|
os.environ["CONTRACT_TEST_URL"] = env["url"]
|
||||||
|
if env.get("api_key"):
|
||||||
|
os.environ["CONTRACT_TEST_API_KEY"] = env["api_key"]
|
||||||
|
os.environ["CONTRACT_TEST_AUTH_TYPE"] = "api-key"
|
||||||
|
else:
|
||||||
|
os.environ.pop("CONTRACT_TEST_API_KEY", None)
|
||||||
|
os.environ["CONTRACT_TEST_AUTH_TYPE"] = "bearer"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"environment": {
|
"environment": {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"name": "Local",
|
"name": "Local",
|
||||||
"url": "http://localhost:8000",
|
"url": "http://localhost:8000",
|
||||||
"api_key": "",
|
"api_key": "",
|
||||||
"description": "Local development server",
|
"description": "Local development server (bare-metal)",
|
||||||
"default": true
|
"default": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -340,8 +340,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1>Contract HTTP Tests - Filters</h1>
|
<h1>Contract HTTP Tests - Filters</h1>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="/tools/tester/">Runner</a>
|
<a href="/station/tools/tester/">Runner</a>
|
||||||
<a href="/tools/tester/filters" class="active">Filters</a>
|
<a href="/station/tools/tester/filters" class="active">Filters</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; align-items: center; gap: 12px; font-size: 0.875rem; color: #9ca3af;">
|
<div style="display: flex; align-items: center; gap: 12px; font-size: 0.875rem; color: #9ca3af;">
|
||||||
@@ -450,7 +450,7 @@
|
|||||||
// Load tests on page load
|
// Load tests on page load
|
||||||
async function loadTests() {
|
async function loadTests() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/tests');
|
const response = await fetch('/station/tools/tester/api/tests');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
allTests = data.tests;
|
allTests = data.tests;
|
||||||
|
|
||||||
@@ -505,12 +505,12 @@
|
|||||||
|
|
||||||
async function loadLastRunResults() {
|
async function loadLastRunResults() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/runs');
|
const response = await fetch('/station/tools/tester/api/runs');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.runs && data.runs.length > 0) {
|
if (data.runs && data.runs.length > 0) {
|
||||||
const lastRunId = data.runs[0];
|
const lastRunId = data.runs[0];
|
||||||
const runResponse = await fetch(`/tools/tester/api/run/${lastRunId}`);
|
const runResponse = await fetch(`/station/tools/tester/api/run/${lastRunId}`);
|
||||||
const runData = await runResponse.json();
|
const runData = await runResponse.json();
|
||||||
|
|
||||||
runData.results.forEach(result => {
|
runData.results.forEach(result => {
|
||||||
@@ -774,7 +774,7 @@
|
|||||||
const testIds = Array.from(selectedTests);
|
const testIds = Array.from(selectedTests);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/run', {
|
const response = await fetch('/station/tools/tester/api/run', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ test_ids: testIds }),
|
body: JSON.stringify({ test_ids: testIds }),
|
||||||
@@ -798,7 +798,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to main runner with filters applied
|
// Redirect to main runner with filters applied
|
||||||
window.location.href = `/tools/tester/?${params.toString()}`;
|
window.location.href = `/station/tools/tester/?${params.toString()}`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to start run:', error);
|
console.error('Failed to start run:', error);
|
||||||
alert('Failed to start test run');
|
alert('Failed to start test run');
|
||||||
@@ -814,7 +814,7 @@
|
|||||||
// Load environments
|
// Load environments
|
||||||
async function loadEnvironments() {
|
async function loadEnvironments() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/environments');
|
const response = await fetch('/station/tools/tester/api/environments');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const selector = document.getElementById('environmentSelector');
|
const selector = document.getElementById('environmentSelector');
|
||||||
const currentUrl = document.getElementById('currentUrl');
|
const currentUrl = document.getElementById('currentUrl');
|
||||||
@@ -835,7 +835,7 @@
|
|||||||
selector.addEventListener('change', async (e) => {
|
selector.addEventListener('change', async (e) => {
|
||||||
const envId = e.target.value;
|
const envId = e.target.value;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/tools/tester/api/environment/select?env_id=${envId}`, {
|
const response = await fetch(`/station/tools/tester/api/environment/select?env_id=${envId}`, {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|||||||
@@ -464,9 +464,9 @@
|
|||||||
<span class="version-badge">Beta</span>
|
<span class="version-badge">Beta</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="nav-links" style="margin-top: 8px;">
|
<div class="nav-links" style="margin-top: 8px;">
|
||||||
<a href="/tools/tester/">Runner</a>
|
<a href="/station/tools/tester/">Runner</a>
|
||||||
<a href="/tools/tester/filters">Filters v1</a>
|
<a href="/station/tools/tester/filters">Filters v1</a>
|
||||||
<a href="/tools/tester/filters_v2" class="active">Filters v2</a>
|
<a href="/station/tools/tester/filters_v2" class="active">Filters v2</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; align-items: center; gap: 12px; font-size: 0.875rem; color: #9ca3af;">
|
<div style="display: flex; align-items: center; gap: 12px; font-size: 0.875rem; color: #9ca3af;">
|
||||||
@@ -704,7 +704,7 @@
|
|||||||
// Load environments
|
// Load environments
|
||||||
async function loadEnvironments() {
|
async function loadEnvironments() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/environments');
|
const response = await fetch('/station/tools/tester/api/environments');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const selector = document.getElementById('environmentSelector');
|
const selector = document.getElementById('environmentSelector');
|
||||||
const currentUrl = document.getElementById('currentUrl');
|
const currentUrl = document.getElementById('currentUrl');
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
selector.addEventListener('change', async (e) => {
|
selector.addEventListener('change', async (e) => {
|
||||||
const envId = e.target.value;
|
const envId = e.target.value;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/tools/tester/api/environment/select?env_id=${envId}`, {
|
const response = await fetch(`/station/tools/tester/api/environment/select?env_id=${envId}`, {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -747,10 +747,10 @@
|
|||||||
async function loadFeatures() {
|
async function loadFeatures() {
|
||||||
try {
|
try {
|
||||||
// First sync features
|
// First sync features
|
||||||
await fetch('/tools/tester/api/features/sync', { method: 'POST' });
|
await fetch('/station/tools/tester/api/features/sync', { method: 'POST' });
|
||||||
|
|
||||||
// Then load them
|
// Then load them
|
||||||
const response = await fetch('/tools/tester/api/features');
|
const response = await fetch('/station/tools/tester/api/features');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
allFeatures = data.features;
|
allFeatures = data.features;
|
||||||
|
|
||||||
@@ -766,7 +766,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Load tags
|
// Load tags
|
||||||
const tagsResponse = await fetch('/tools/tester/api/features/tags');
|
const tagsResponse = await fetch('/station/tools/tester/api/features/tags');
|
||||||
const tagsData = await tagsResponse.json();
|
const tagsData = await tagsResponse.json();
|
||||||
|
|
||||||
const tagFilters = document.getElementById('tagFilters');
|
const tagFilters = document.getElementById('tagFilters');
|
||||||
@@ -787,7 +787,7 @@
|
|||||||
// Load tests
|
// Load tests
|
||||||
async function loadTests() {
|
async function loadTests() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/tests');
|
const response = await fetch('/station/tools/tester/api/tests');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
// For now, all tests are backend (until we integrate Playwright discovery)
|
// For now, all tests are backend (until we integrate Playwright discovery)
|
||||||
@@ -1109,7 +1109,7 @@
|
|||||||
const testIds = Array.from(selectedTests);
|
const testIds = Array.from(selectedTests);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/run', {
|
const response = await fetch('/station/tools/tester/api/run', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ test_ids: testIds }),
|
body: JSON.stringify({ test_ids: testIds }),
|
||||||
@@ -1117,7 +1117,7 @@
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
// Redirect to runner
|
// Redirect to runner
|
||||||
window.location.href = `/tools/tester/?run=${data.run_id}`;
|
window.location.href = `/station/tools/tester/?run=${data.run_id}`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to start run:', error);
|
console.error('Failed to start run:', error);
|
||||||
alert('Failed to start test run');
|
alert('Failed to start test run');
|
||||||
|
|||||||
@@ -402,8 +402,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1>Contract HTTP Tests</h1>
|
<h1>Contract HTTP Tests</h1>
|
||||||
<div style="display: flex; gap: 12px; margin-top: 8px; font-size: 0.875rem;">
|
<div style="display: flex; gap: 12px; margin-top: 8px; font-size: 0.875rem;">
|
||||||
<a href="/tools/tester/" style="color: #60a5fa; text-decoration: none; font-weight: 600;">Runner</a>
|
<a href="/station/tools/tester/" style="color: #60a5fa; text-decoration: none; font-weight: 600;">Runner</a>
|
||||||
<a href="/tools/tester/filters" style="color: #60a5fa; text-decoration: none;">Filters</a>
|
<a href="/station/tools/tester/filters" style="color: #60a5fa; text-decoration: none;">Filters</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-info">
|
<div class="config-info">
|
||||||
@@ -602,7 +602,7 @@
|
|||||||
// Load environments
|
// Load environments
|
||||||
async function loadEnvironments() {
|
async function loadEnvironments() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/environments');
|
const response = await fetch('/station/tools/tester/api/environments');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const selector = document.getElementById('environmentSelector');
|
const selector = document.getElementById('environmentSelector');
|
||||||
const currentUrl = document.getElementById('currentUrl');
|
const currentUrl = document.getElementById('currentUrl');
|
||||||
@@ -627,7 +627,7 @@
|
|||||||
selector.addEventListener('change', async (e) => {
|
selector.addEventListener('change', async (e) => {
|
||||||
const envId = e.target.value;
|
const envId = e.target.value;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/tools/tester/api/environment/select?env_id=${envId}`, {
|
const response = await fetch(`/station/tools/tester/api/environment/select?env_id=${envId}`, {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -709,7 +709,7 @@
|
|||||||
document.getElementById('resultsList').innerHTML = '';
|
document.getElementById('resultsList').innerHTML = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/tools/tester/api/run', {
|
const response = await fetch('/station/tools/tester/api/run', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ test_ids: testIds }),
|
body: JSON.stringify({ test_ids: testIds }),
|
||||||
@@ -731,7 +731,7 @@
|
|||||||
if (!currentRunId) return;
|
if (!currentRunId) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/tools/tester/api/run/${currentRunId}`);
|
const response = await fetch(`/station/tools/tester/api/run/${currentRunId}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
updateUI(data);
|
updateUI(data);
|
||||||
|
|||||||
Reference in New Issue
Block a user