diff --git a/apps/report/rsbuild.config.ts b/apps/report/rsbuild.config.ts index f7c38b170..af4a37ff8 100644 --- a/apps/report/rsbuild.config.ts +++ b/apps/report/rsbuild.config.ts @@ -5,8 +5,19 @@ import { pluginLess } from '@rsbuild/plugin-less'; import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill'; import { pluginReact } from '@rsbuild/plugin-react'; -const testDataPath = path.join(__dirname, 'test-data', 'swag-lab.json'); -const testData = JSON.parse(fs.readFileSync(testDataPath, 'utf-8')); +// Read all JSON files from test-data directory +const testDataDir = path.join(__dirname, 'test-data'); +const jsonFiles = fs + .readdirSync(testDataDir) + .filter((file) => file.endsWith('.json')); +const allTestData = jsonFiles.map((file) => { + const filePath = path.join(testDataDir, file); + const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + return { + fileName: file, + data, + }; +}); const copyReportTemplate = () => ({ name: 'copy-report-template', @@ -34,21 +45,19 @@ export default defineConfig({ inject: 'body', tags: process.env.NODE_ENV === 'development' - ? [ - { - tag: 'script', - attrs: { - type: 'midscene_web_dump', - playwright_test_name: testData.groupName, - playwright_test_description: testData.groupDescription, - playwright_test_id: '8465e854a4d9a753cc87-1f096ece43c67754f95a', - playwright_test_title: 'test open new tab', - playwright_test_status: 'passed', - playwright_test_duration: '44274', - }, - children: JSON.stringify(testData), + ? allTestData.map((item) => ({ + tag: 'script', + attrs: { + type: 'midscene_web_dump', + playwright_test_name: item.data.groupName, + playwright_test_description: item.data.groupDescription, + playwright_test_id: '8465e854a4d9a753cc87-1f096ece43c67754f95a', + playwright_test_title: 'test open new tab', + playwright_test_status: 'passed', + playwright_test_duration: '44274', }, - ] + children: JSON.stringify(item.data), + })) : [], }, resolve: { diff --git a/apps/report/src/components/PlaywrightCaseSelector.tsx b/apps/report/src/components/PlaywrightCaseSelector.tsx index 2e8f30903..608bc16b6 100644 --- a/apps/report/src/components/PlaywrightCaseSelector.tsx +++ b/apps/report/src/components/PlaywrightCaseSelector.tsx @@ -1,11 +1,21 @@ import { DownOutlined, SearchOutlined } from '@ant-design/icons'; import type { GroupedActionDump } from '@midscene/core'; import { iconForStatus, timeCostStrElement } from '@midscene/visualizer'; -import { Dropdown, Input } from 'antd'; +import { Dropdown, Input, Select, Space } from 'antd'; import type React from 'react'; import { useMemo, useState } from 'react'; import type { ExecutionDumpWithPlaywrightAttributes } from '../types'; +// define all possible test statuses +const TEST_STATUS_OPTIONS = [ + { value: 'all', label: 'All' }, + { value: 'passed', label: 'Passed' }, + { value: 'failed', label: 'Failed' }, + { value: 'skipped', label: 'Skipped' }, + { value: 'timedOut', label: 'Timed Out' }, + { value: 'interrupted', label: 'Interrupted' }, +]; + interface PlaywrightCaseSelectorProps { dumps?: ExecutionDumpWithPlaywrightAttributes[]; selected?: GroupedActionDump | null; @@ -20,6 +30,7 @@ export function PlaywrightCaseSelector({ if (!dumps || dumps.length <= 1) return null; const [searchText, setSearchText] = useState(''); + const [statusFilter, setStatusFilter] = useState('all'); const [dropdownVisible, setDropdownVisible] = useState(false); const nameForDump = (dump: GroupedActionDump) => @@ -49,11 +60,24 @@ export function PlaywrightCaseSelector({ }; const filteredDumps = useMemo(() => { - if (!searchText) return dumps || []; - return (dumps || []).filter((dump) => - nameForDump(dump).toLowerCase().includes(searchText.toLowerCase()), - ); - }, [dumps, searchText]); + let result = dumps || []; + + // apply text filter + if (searchText) { + result = result.filter((dump) => + nameForDump(dump).toLowerCase().includes(searchText.toLowerCase()), + ); + } + + // apply status filter + if (statusFilter !== 'all') { + result = result.filter( + (dump) => dump.attributes?.playwright_test_status === statusFilter, + ); + } + + return result; + }, [dumps, searchText, statusFilter]); const items = filteredDumps.map((dump, index) => { return { @@ -85,17 +109,30 @@ export function PlaywrightCaseSelector({ setSearchText(e.target.value); }; + const handleStatusChange = (value: string) => { + setStatusFilter(value); + }; + const dropdownRender = (menu: React.ReactNode) => (
- } - allowClear - autoFocus - /> + + } + allowClear + autoFocus + style={{ flex: 1 }} + /> +