diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb0b7c..807321d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## 1.11.0 - 2026-09-15 + +Changes since 1.10.1: + +### Added + +- Added export of a scenario as a standalone, plug-and-play Playwright e2e test project (zip archive with `package.json`, `playwright.config.ts`, `test.spec.ts`, `README.md`, `.env`, `credentials.json`, `snippets/*.js`, and any attached scenario files) that runs entirely independently of the liqa backend. Available as a "Export e2e test" button on the scenario detail page, `GET /scenarios/:id/export-e2e`, and the `export_e2e_test` MCP tool. +- The exported test only bundles the credentials, snippets, and attached files actually referenced (directly or transitively) by the scenario's steps. +- Added a "Used Snippets" section on the scenario detail page listing the snippets referenced by the scenario's steps, linking to each snippet's detail page. Backed by `GET /scenarios/:id/used-snippets`. + ## 1.10.1 - 2026-09-15 Changes since 1.10.0: diff --git a/client/src/api/client.ts b/client/src/api/client.ts index 9a3d7c9..945247a 100644 --- a/client/src/api/client.ts +++ b/client/src/api/client.ts @@ -261,6 +261,12 @@ export const scenarios = { body: JSON.stringify(payload), }); }, + exportE2eTestUrl(id: string): string { + return `${BASE_URL}/scenarios/${id}/export-e2e`; + }, + usedSnippets(id: string): Promise { + return request(`/scenarios/${id}/used-snippets`); + }, }; // ── Runs ───────────────────────────────────────────────────────────────────── diff --git a/client/src/i18n/locales/en.json b/client/src/i18n/locales/en.json index b5856a1..f11faa6 100644 --- a/client/src/i18n/locales/en.json +++ b/client/src/i18n/locales/en.json @@ -149,6 +149,7 @@ "action_edit": "Edit", "action_runs": "Runs", "action_export": "Export", + "action_export_e2e": "Export e2e test", "action_import": "Import", "import_error": "Failed to import scenario", "action_save": "Create", @@ -165,6 +166,7 @@ "form_name_placeholder": "e.g. Login flow", "form_name_required": "Name is required", "credentials_heading": "Credentials", + "used_snippets_heading": "Used Snippets", "cred_empty": "No credentials assigned.", "cred_col_alias": "Alias", "cred_col_name": "Credential", diff --git a/client/src/pages/scenario/ScenarioDetailPage.tsx b/client/src/pages/scenario/ScenarioDetailPage.tsx index eb61602..36ab9d7 100644 --- a/client/src/pages/scenario/ScenarioDetailPage.tsx +++ b/client/src/pages/scenario/ScenarioDetailPage.tsx @@ -12,6 +12,7 @@ import { Upload, GripVertical, ClipboardList, + FileArchive, } from 'lucide-react'; import { environments, @@ -28,6 +29,7 @@ import type { Credential, Environment, FileMetadata, + Snippet, } from '../../api'; import { Breadcrumbs, @@ -94,6 +96,8 @@ export function ScenarioDetailPage() { const [uploadExpiresAt, setUploadExpiresAt] = useState(''); const [uploading, setUploading] = useState(false); + const [usedSnippets, setUsedSnippets] = useState([]); + const loadFiles = useCallback(async () => { if (!id) return; setFilesLoading(true); @@ -116,6 +120,10 @@ export function ScenarioDetailPage() { }) .finally(() => setLoading(false)); void loadFiles(); + scenarios + .usedSnippets(id) + .then(setUsedSnippets) + .catch(() => {}); credentialsApi .list(1, 200) .then((r) => setAllCredentials(r.data)) @@ -142,6 +150,10 @@ export function ScenarioDetailPage() { const s = await scenarios.get(id); setScenario(s); setScenarioCreds(s.scenarioCredentials ?? []); + scenarios + .usedSnippets(id) + .then(setUsedSnippets) + .catch(() => {}); }; const handleRun = async () => { @@ -456,6 +468,16 @@ export function ScenarioDetailPage() { {t('scenarios.action_export')} +