From 43387a7f7bfafe23b258ce772ce68205e4d2bb70 Mon Sep 17 00:00:00 2001 From: Sergey Kozyrenko Date: Tue, 21 Jul 2026 05:04:40 +0700 Subject: [PATCH] test(e2e): cover the query-backed flow detail tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the Terminal tab had a spec; Tasks, Agents, Searches and Vector Store — all fed by the FlowDocument query — were uncovered. Add a populated flow cassette (one entry per tab) and a spec that opens each tab and asserts its seeded content renders. Files and Screenshots are REST-backed and left for a follow-up. Co-Authored-By: Claude Fable 5 --- frontend/e2e/mocks/cassettes/flows.ts | 91 ++++++++++++++++++++++++++- frontend/e2e/specs/flows/tabs.spec.ts | 29 +++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 frontend/e2e/specs/flows/tabs.spec.ts diff --git a/frontend/e2e/mocks/cassettes/flows.ts b/frontend/e2e/mocks/cassettes/flows.ts index 0c810130..f494cd18 100644 --- a/frontend/e2e/mocks/cassettes/flows.ts +++ b/frontend/e2e/mocks/cassettes/flows.ts @@ -8,7 +8,16 @@ import type { TerminalLogFragmentFragment, } from '@/graphql/types'; -import { MessageLogType, ProviderType, ResultFormat, StatusType, TerminalLogType, TerminalType } from '@/graphql/types'; +import { + AgentType, + MessageLogType, + ProviderType, + ResultFormat, + StatusType, + TerminalLogType, + TerminalType, + VectorStoreAction, +} from '@/graphql/types'; import type { Cassette } from '../cassette.ts'; @@ -184,3 +193,83 @@ export const variedMessagesCassette = (): Cassette => queries: { flow: [{ data: flowQueryData(FLOW_A, VARIED_MESSAGES), variables: { id: '5' } }] }, subscriptions: { messageLogAdded: [{ frames: [], variables: { flowId: '5' } }] }, }); + +const TABS_TASK = entity('Task', { + createdAt: T, + flowId: '5', + id: '11', + input: 'Enumerate the target', + result: 'Enumeration complete', + status: StatusType.Finished, + subtasks: [ + entity('Subtask', { + createdAt: T, + description: 'Run an nmap sweep', + id: '21', + result: 'Ports 22, 80 open', + status: StatusType.Finished, + taskId: '11', + title: 'E2E Subtask Scan', + updatedAt: T, + }), + ], + title: 'E2E Task Alpha', + updatedAt: T, +}); + +const TABS_AGENT_LOG = entity('AgentLog', { + createdAt: T, + executor: AgentType.Pentester, + flowId: '5', + id: '41', + initiator: AgentType.Adviser, + result: 'Reconnaissance summary ready', + subtaskId: null, + task: 'E2E agent reconnaissance', + taskId: '11', +}); + +const TABS_SEARCH_LOG = entity('SearchLog', { + createdAt: T, + engine: 'duckduckgo', + executor: AgentType.Searcher, + flowId: '5', + id: '51', + initiator: AgentType.Pentester, + query: 'E2E search for the CVE', + result: 'Found relevant advisories', + subtaskId: null, + taskId: '11', +}); + +const TABS_VECTOR_LOG = entity('VectorStoreLog', { + action: VectorStoreAction.Retrieve, + createdAt: T, + executor: AgentType.Memorist, + filter: '{}', + flowId: '5', + id: '61', + initiator: AgentType.Pentester, + query: 'E2E recall prior findings', + result: 'Recalled 3 memories', + subtaskId: null, + taskId: '11', +}); + +const flowTabsData: ResultOf = { + agentLogs: [TABS_AGENT_LOG], + flow: FLOW_A, + messageLogs: [], + screenshots: [], + searchLogs: [TABS_SEARCH_LOG], + tasks: [TABS_TASK], + terminalLogs: [], + vectorStoreLogs: [TABS_VECTOR_LOG], +}; + +/** Flow 5 with one populated entry per query-backed detail tab (tasks, agents, searches, vector store). */ +export const flowTabsCassette = (): Cassette => + flowsCassette({ + queries: { flow: [{ data: flowTabsData, variables: { id: '5' } }] }, + subscriptions: { messageLogAdded: [{ frames: [], variables: { flowId: '5' } }] }, + }); diff --git a/frontend/e2e/specs/flows/tabs.spec.ts b/frontend/e2e/specs/flows/tabs.spec.ts new file mode 100644 index 00000000..f456e3a2 --- /dev/null +++ b/frontend/e2e/specs/flows/tabs.spec.ts @@ -0,0 +1,29 @@ +import { expect, test } from '../../fixtures/test.ts'; +import { expectCleanPage } from '../../helpers/errors.ts'; +import { flowTabsCassette } from '../../mocks/cassettes/flows.ts'; + +// The query-backed detail tabs were uncovered — only Terminal had a spec. Each +// marker is unique to its tab's seeded data, so a visible marker proves that tab +// rendered its content. +const TABS = [ + { marker: 'E2E Task Alpha', name: 'Tasks' }, + { marker: 'E2E agent reconnaissance', name: 'Agents' }, + { marker: 'E2E search for the CVE', name: 'Searches' }, + { marker: 'E2E recall prior findings', name: 'Vector Store' }, +] as const; + +test.describe('flow detail tabs', { tag: '@flows' }, () => { + test.use({ cassette: flowTabsCassette() }); + + test('each query-backed tab renders its populated content', async ({ page, pageErrorLog }) => { + await page.goto('/flows/5'); + await expect(page.getByRole('button', { name: 'Flow actions' })).toBeVisible(); + + for (const { marker, name } of TABS) { + await page.getByRole('tab', { name }).click(); + await expect(page.getByText(marker)).toBeVisible(); + } + + expectCleanPage(pageErrorLog); + }); +});