From b3d0cd86b5d4b82e7d3919312443a207a4fe836b Mon Sep 17 00:00:00 2001 From: Jorge Padilla Date: Tue, 18 Jul 2023 01:41:03 +0900 Subject: [PATCH] feat(frontend): add transaction automate page (#2939) --- web/src/components/Router/Router.tsx | 14 +-- .../RunActionsMenu/RunActionsMenu.tsx | 2 - .../components/RunCard/TransactionRunCard.tsx | 2 +- .../RunDetailAutomate.styled.ts | 13 +- .../RunDetailAutomate/RunDetailAutomate.tsx | 44 ++++++- .../RunDetailAutomateDefinition.tsx | 24 ++-- .../RunDetailAutomateMethods.styled.ts | 98 --------------- .../RunDetailAutomateMethods.tsx | 47 +++---- .../methods/CLICommand/CliCommand.tsx | 25 +++- .../methods/CLICommand/Controls.tsx | 25 ++-- .../methods/DeepLink/DeepLink.tsx | 13 +- .../RunDetailLayout/RunDetailLayout.tsx | 2 +- .../TransactionHeader.styled.ts | 35 ++++++ .../TransactionHeader/TransactionHeader.tsx | 63 +++++++--- .../__tests__/TransactionHeader.test.tsx | 11 -- .../TransactionRunActionsMenu.tsx | 19 +-- .../TransactionRunLayout.styled.ts | 86 ------------- .../TransactionRunLayout.tsx | 40 ++---- web/src/constants/Common.constants.ts | 1 + .../pages/TransactionRunAutomate/Content.tsx | 54 ++++++++ .../TransactionRunAutomate.styled.ts | 24 ++++ .../TransactionRunAutomate.tsx | 11 ++ .../index.ts | 2 +- .../TransactionRunDetail/Content.styled.tsx | 115 ------------------ .../pages/TransactionRunDetail/Content.tsx | 12 -- .../TransactionRunDetail.tsx | 19 --- .../pages/TransactionRunOverview/Content.tsx | 28 +++++ .../TransactionRunOverview.styled.ts | 24 ++++ .../TransactionRunOverview.tsx | 11 ++ web/src/pages/TransactionRunOverview/index.ts | 2 + .../TransactionRun.provider.tsx | 6 +- web/src/services/CliCommand.service.ts | 17 +-- 32 files changed, 396 insertions(+), 493 deletions(-) delete mode 100644 web/src/components/TransactionHeader/__tests__/TransactionHeader.test.tsx delete mode 100644 web/src/components/TransactionRunLayout/TransactionRunLayout.styled.ts create mode 100644 web/src/pages/TransactionRunAutomate/Content.tsx create mode 100644 web/src/pages/TransactionRunAutomate/TransactionRunAutomate.styled.ts create mode 100644 web/src/pages/TransactionRunAutomate/TransactionRunAutomate.tsx rename web/src/pages/{TransactionRunDetail => TransactionRunAutomate}/index.ts (50%) delete mode 100644 web/src/pages/TransactionRunDetail/Content.styled.tsx delete mode 100644 web/src/pages/TransactionRunDetail/Content.tsx delete mode 100644 web/src/pages/TransactionRunDetail/TransactionRunDetail.tsx create mode 100644 web/src/pages/TransactionRunOverview/Content.tsx create mode 100644 web/src/pages/TransactionRunOverview/TransactionRunOverview.styled.ts create mode 100644 web/src/pages/TransactionRunOverview/TransactionRunOverview.tsx create mode 100644 web/src/pages/TransactionRunOverview/index.ts diff --git a/web/src/components/Router/Router.tsx b/web/src/components/Router/Router.tsx index dc0e3db8c5..e24a8ae6a9 100644 --- a/web/src/components/Router/Router.tsx +++ b/web/src/components/Router/Router.tsx @@ -8,7 +8,8 @@ import RunDetail from 'pages/RunDetail'; import Settings from 'pages/Settings'; import Test from 'pages/Test'; import Transaction from 'pages/Transaction'; -import TransactionRunDetail from 'pages/TransactionRunDetail'; +import TransactionRunOverview from 'pages/TransactionRunOverview'; +import TransactionRunAutomate from 'pages/TransactionRunAutomate'; import AutomatedTestRun from 'pages/AutomatedTestRun'; import Env from 'utils/Env'; @@ -24,15 +25,14 @@ const Router = () => ( } /> } /> - - }> - } /> - - + } /> + } /> } /> } /> - } /> + } /> + } /> + } /> } /> diff --git a/web/src/components/RunActionsMenu/RunActionsMenu.tsx b/web/src/components/RunActionsMenu/RunActionsMenu.tsx index ed6f573ff6..21f77fed57 100644 --- a/web/src/components/RunActionsMenu/RunActionsMenu.tsx +++ b/web/src/components/RunActionsMenu/RunActionsMenu.tsx @@ -17,9 +17,7 @@ interface IProps { const RunActionsMenu = ({resultId, testId, transactionId, transactionRunId, isRunView = false}: IProps) => { const {onJUnit} = useFileViewerModal(); - const navigate = useNavigate(); - const onDelete = useDeleteResourceRun({id: testId, isRunView, type: ResourceType.Test}); return ( diff --git a/web/src/components/RunCard/TransactionRunCard.tsx b/web/src/components/RunCard/TransactionRunCard.tsx index 092b75185f..c2f4aed899 100644 --- a/web/src/components/RunCard/TransactionRunCard.tsx +++ b/web/src/components/RunCard/TransactionRunCard.tsx @@ -72,7 +72,7 @@ const TransactionRunCard = ({ )}
- +
diff --git a/web/src/components/RunDetailAutomate/RunDetailAutomate.styled.ts b/web/src/components/RunDetailAutomate/RunDetailAutomate.styled.ts index ba4593a586..e5b4532d62 100644 --- a/web/src/components/RunDetailAutomate/RunDetailAutomate.styled.ts +++ b/web/src/components/RunDetailAutomate/RunDetailAutomate.styled.ts @@ -8,21 +8,18 @@ export const Container = styled.div` export const Section = styled.div` flex: 1; -`; - -export const SectionLeft = styled(Section)` background-color: ${({theme}) => theme.color.white}; overflow-y: scroll; - z-index: 1; flex-basis: 50%; max-width: 50vw; `; +export const SectionLeft = styled(Section)` + z-index: 1; +`; + export const SectionRight = styled(Section)` - background-color: ${({theme}) => theme.color.white}; border-left: 1px solid rgba(3, 24, 73, 0.1); - overflow-y: scroll; z-index: 2; - flex-basis: 50%; - max-width: 50vw; + padding: 24px; `; diff --git a/web/src/components/RunDetailAutomate/RunDetailAutomate.tsx b/web/src/components/RunDetailAutomate/RunDetailAutomate.tsx index cbc8505a1e..f72ef864d9 100644 --- a/web/src/components/RunDetailAutomate/RunDetailAutomate.tsx +++ b/web/src/components/RunDetailAutomate/RunDetailAutomate.tsx @@ -1,10 +1,15 @@ import {snakeCase} from 'lodash'; -import Test from 'models/Test.model'; import {useState} from 'react'; +import RunDetailAutomateDefinition from 'components/RunDetailAutomateDefinition'; +import RunDetailAutomateMethods from 'components/RunDetailAutomateMethods'; +import CliCommand from 'components/RunDetailAutomateMethods/methods/CLICommand'; +import DeepLink from 'components/RunDetailAutomateMethods/methods/DeepLink'; +import {CLI_RUNNING_TESTS_URL} from 'constants/Common.constants'; +import Test from 'models/Test.model'; import TestRun from 'models/TestRun.model'; +import {useEnvironment} from 'providers/Environment/Environment.provider'; +import {ResourceType} from 'types/Resource.type'; import * as S from './RunDetailAutomate.styled'; -import RunDetailAutomateDefinition from '../RunDetailAutomateDefinition'; -import RunDetailAutomateMethods from '../RunDetailAutomateMethods/RunDetailAutomateMethods'; interface IProps { test: Test; @@ -13,14 +18,43 @@ interface IProps { const RunDetailAutomate = ({test, run}: IProps) => { const [fileName, setFileName] = useState(`${snakeCase(test.name)}.yaml`); + const {selectedEnvironment: {id: environmentId} = {}} = useEnvironment(); return ( - + - + + ), + }, + { + id: 'deeplink', + label: 'Deep Link', + children: , + }, + ]} + /> ); diff --git a/web/src/components/RunDetailAutomateDefinition/RunDetailAutomateDefinition.tsx b/web/src/components/RunDetailAutomateDefinition/RunDetailAutomateDefinition.tsx index 9f4ffc236c..3a6c5c942c 100644 --- a/web/src/components/RunDetailAutomateDefinition/RunDetailAutomateDefinition.tsx +++ b/web/src/components/RunDetailAutomateDefinition/RunDetailAutomateDefinition.tsx @@ -1,21 +1,23 @@ -import {useCallback, useEffect} from 'react'; import {DownloadOutlined} from '@ant-design/icons'; import {Button} from 'antd'; -import {downloadFile} from 'utils/Common'; +import {capitalize} from 'lodash'; +import {useCallback, useEffect} from 'react'; +import {FramedCodeBlock} from 'components/CodeBlock'; +import InputOverlay from 'components/InputOverlay/InputOverlay'; import useDefinitionFile from 'hooks/useDefinitionFile'; import {ResourceType} from 'types/Resource.type'; -import Test from 'models/Test.model'; +import {downloadFile} from 'utils/Common'; import * as S from './RunDetailAutomateDefinition.styled'; -import {FramedCodeBlock} from '../CodeBlock'; -import InputOverlay from '../InputOverlay/InputOverlay'; interface IProps { - test: Test; - onFileNameChange(value: string): void; + id: string; + version: number; + resourceType: ResourceType; fileName: string; + onFileNameChange(value: string): void; } -const RunDetailAutomateDefinition = ({test: {id, version}, onFileNameChange, fileName}: IProps) => { +const RunDetailAutomateDefinition = ({id, version, resourceType, fileName, onFileNameChange}: IProps) => { const {definition, loadDefinition} = useDefinitionFile(); const onDownload = useCallback(() => { @@ -23,12 +25,12 @@ const RunDetailAutomateDefinition = ({test: {id, version}, onFileNameChange, fil }, [definition, fileName]); useEffect(() => { - loadDefinition(ResourceType.Test, id, version); - }, [id, loadDefinition, version]); + loadDefinition(resourceType, id, version); + }, [id, loadDefinition, resourceType, version]); return ( - Test Definition + {capitalize(resourceType)} Definition diff --git a/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.styled.ts b/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.styled.ts index 8318ea9617..df84c75b05 100644 --- a/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.styled.ts +++ b/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.styled.ts @@ -1,12 +1,10 @@ import {Typography} from 'antd'; import styled from 'styled-components'; -import noResultsIcon from 'assets/SpanAssertionsEmptyState.svg'; export const Container = styled.div` display: flex; flex-direction: column; height: 100%; - padding: 24px; `; export const TitleContainer = styled.div` @@ -44,99 +42,3 @@ export const TabsContainer = styled.div` padding: 0; } `; - -export const StatusText = styled(Typography.Text)` - && { - font-size: ${({theme}) => theme.size.md}; - } -`; - -export const LoadingResponseBody = styled.div` - margin-top: 24px; - display: flex; - flex-direction: column; - justify-items: center; - gap: 16px; - padding: 0.4em 0.6em; -`; - -export const TextHolder = styled.div<{$width?: number}>` - @keyframes skeleton-loading { - 0% { - background-color: hsl(200, 20%, 80%); - } - 100% { - background-color: hsl(200, 20%, 95%); - } - } - - animation: skeleton-loading 1s linear infinite alternate; - height: 16px; - border-radius: 2px; - width: ${({$width = 100}) => $width}%; -`; - -export const TextContainer = styled.div` - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -`; - -export const Text = styled(Typography.Text)` - font-size: ${({theme}) => theme.size.sm}; -`; - -export const HeadersList = styled.div` - display: flex; - flex-direction: column; - gap: 4px; - padding: 16px 0; -`; - -export const Actions = styled.div` - display: flex; - justify-content: flex-end; - align-items: center; - margin-top: 16px; - gap: 10px; -`; - -export const ResponseEnvironmentContainer = styled.div` - padding: 16px 0; -`; - -export const EmptyContainer = styled.div` - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin-top: 100px; - text-align: center; -`; - -export const EmptyIcon = styled.img.attrs({ - src: noResultsIcon, -})` - height: auto; - margin-bottom: 16px; - width: 90px; -`; - -export const EmptyText = styled(Typography.Text)` - color: ${({theme}) => theme.color.textSecondary}; -`; - -export const EmptyTitle = styled(Typography.Title).attrs({level: 3})``; - -export const ResponseBodyContainer = styled.div` - display: flex; - width: 100%; -`; - -export const ResponseBodyContent = styled.div` - flex: 1; -`; - -export const ResponseBodyActions = styled.div` - margin: 16px 0 0 4px; -`; diff --git a/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.tsx b/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.tsx index 08fd5d0666..a1a4a430bb 100644 --- a/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.tsx +++ b/web/src/components/RunDetailAutomateMethods/RunDetailAutomateMethods.tsx @@ -1,48 +1,41 @@ import {Tabs} from 'antd'; import {useSearchParams} from 'react-router-dom'; -import Test from 'models/Test.model'; -import TestRun from 'models/TestRun.model'; -import {useEnvironment} from 'providers/Environment/Environment.provider'; -import CliCommand from './methods/CLICommand'; +import {ResourceType} from 'types/Resource.type'; import * as S from './RunDetailAutomateMethods.styled'; -import DeepLink from './methods/DeepLink/DeepLink'; -const TabsKeys = { - CLI: 'cli', - DeepLink: 'deeplink', -}; +interface IProps { + resourceType: ResourceType; + methods?: IMethodProps[]; +} -export interface IMethodProps { - environmentId?: string; - test: Test; - run: TestRun; - fileName?: string; +interface IMethodProps { + id: string; + label: string; + children: React.ReactNode; } -const RunDetailAutomateMethods = ({test, run, fileName}: IMethodProps) => { +const RunDetailAutomateMethods = ({resourceType, methods = []}: IProps) => { const [query, updateQuery] = useSearchParams(); - const {selectedEnvironment: {id: environmentId} = {}} = useEnvironment(); return ( - Running Techniques Methods to automate the running of this test + Running Techniques + Methods to automate the running of this {resourceType} { - updateQuery([['tab', newTab]]); + onChange={activeKey => { + updateQuery([['tab', activeKey]]); }} > - - - - - - + {methods.map(({id, label, children}) => ( + + {children} + + ))} diff --git a/web/src/components/RunDetailAutomateMethods/methods/CLICommand/CliCommand.tsx b/web/src/components/RunDetailAutomateMethods/methods/CLICommand/CliCommand.tsx index cb42a4817d..d990bf60ab 100644 --- a/web/src/components/RunDetailAutomateMethods/methods/CLICommand/CliCommand.tsx +++ b/web/src/components/RunDetailAutomateMethods/methods/CLICommand/CliCommand.tsx @@ -1,19 +1,26 @@ -import {FramedCodeBlock} from 'components/CodeBlock'; import {ReadOutlined} from '@ant-design/icons'; -import {CLI_RUNNING_TESTS_URL} from 'constants/Common.constants'; +import {FramedCodeBlock} from 'components/CodeBlock'; +import {ResourceType} from 'types/Resource.type'; import * as S from './CliCommand.styled'; import Controls from './Controls'; import useCliCommand from './hooks/useCliCommand'; -import {IMethodProps} from '../../RunDetailAutomateMethods'; -const CLiCommand = ({test, environmentId, fileName = ''}: IMethodProps) => { +interface IProps { + id: string; + environmentId?: string; + fileName?: string; + resourceType: ResourceType; + docsUrl?: string; +} + +const CLiCommand = ({id, environmentId, fileName = '', resourceType, docsUrl}: IProps) => { const {command, onGetCommand} = useCliCommand(); return ( CLI Configuration - + @@ -24,7 +31,13 @@ const CLiCommand = ({test, environmentId, fileName = ''}: IMethodProps) => { minHeight="100px" maxHeight="100px" /> - + ); }; diff --git a/web/src/components/RunDetailAutomateMethods/methods/CLICommand/Controls.tsx b/web/src/components/RunDetailAutomateMethods/methods/CLICommand/Controls.tsx index 8aaf3d0c3a..1aa89a1d96 100644 --- a/web/src/components/RunDetailAutomateMethods/methods/CLICommand/Controls.tsx +++ b/web/src/components/RunDetailAutomateMethods/methods/CLICommand/Controls.tsx @@ -1,14 +1,15 @@ import {Form, Radio, Typography} from 'antd'; import {toUpper} from 'lodash'; import {useEffect, useMemo} from 'react'; -import Test from 'models/Test.model'; import {CliCommandFormat, CliCommandOption, TCliCommandConfig} from 'services/CliCommand.service'; +import {ResourceType} from 'types/Resource.type'; import * as S from './CliCommand.styled'; import SwitchControl from './SwitchControl'; import {defaultOptions} from './hooks/useCliCommand'; interface IOptionsMetadataParams { isEnvironmentSelected: boolean; + resourceType: ResourceType; } interface IOptionsMetadata { label: string; @@ -18,10 +19,11 @@ interface IOptionsMetadata { function getOptionsMetadata({ isEnvironmentSelected, + resourceType, }: IOptionsMetadataParams): Record { return { - [CliCommandOption.UseId]: {label: 'Use test ID instead of file'}, - [CliCommandOption.SkipResultWait]: {label: 'Skip waiting for test to complete'}, + [CliCommandOption.UseId]: {label: `Use ${resourceType} ID instead of file`}, + [CliCommandOption.SkipResultWait]: {label: `Skip waiting for ${resourceType} to complete`}, [CliCommandOption.UseHostname]: {label: 'Specify Tracetest server hostname'}, [CliCommandOption.UseCurrentEnvironment]: { label: 'Use selected environment', @@ -35,26 +37,31 @@ function getOptionsMetadata({ interface IProps { onChange(cmdConfig: TCliCommandConfig): void; - test: Test; + id: string; environmentId?: string; fileName: string; + resourceType: ResourceType; } -const Controls = ({onChange, test, environmentId, fileName}: IProps) => { +const Controls = ({onChange, id, environmentId, fileName, resourceType}: IProps) => { const [form] = Form.useForm(); const options = Form.useWatch('options', form); const format = Form.useWatch('format', form); - const optionsMetadata = useMemo(() => getOptionsMetadata({isEnvironmentSelected: !!environmentId}), [environmentId]); + const optionsMetadata = useMemo( + () => getOptionsMetadata({isEnvironmentSelected: !!environmentId, resourceType}), + [environmentId, resourceType] + ); useEffect(() => { onChange({ options: options ?? defaultOptions, format: format ?? CliCommandFormat.Pretty, - test, + id, environmentId, fileName, + resourceType, }); - }, [environmentId, fileName, format, onChange, options, test]); + }, [environmentId, fileName, format, onChange, options, id, resourceType]); return ( @@ -70,7 +77,7 @@ const Controls = ({onChange, test, environmentId, fileName}: IProps) => { {Object.entries(optionsMetadata).map(([name, data]) => ( - + ))} diff --git a/web/src/components/RunDetailAutomateMethods/methods/DeepLink/DeepLink.tsx b/web/src/components/RunDetailAutomateMethods/methods/DeepLink/DeepLink.tsx index 5e4eec40bb..17ffaf2a8d 100644 --- a/web/src/components/RunDetailAutomateMethods/methods/DeepLink/DeepLink.tsx +++ b/web/src/components/RunDetailAutomateMethods/methods/DeepLink/DeepLink.tsx @@ -1,11 +1,18 @@ import {Typography} from 'antd'; import {FramedCodeBlock} from 'components/CodeBlock'; -import * as S from './DeepLink.styled'; +import Test from 'models/Test.model'; +import TestRun from 'models/TestRun.model'; import Controls from './Controls'; -import {IMethodProps} from '../../RunDetailAutomateMethods'; +import * as S from './DeepLink.styled'; import useDeepLink from './hooks/useDeepLink'; -const DeepLink = ({test, environmentId, run: {environment}}: IMethodProps) => { +interface IProps { + test: Test; + run: TestRun; + environmentId?: string; +} + +const DeepLink = ({test, environmentId, run: {environment}}: IProps) => { const {deepLink, onGetDeepLink} = useDeepLink(); return ( diff --git a/web/src/components/RunDetailLayout/RunDetailLayout.tsx b/web/src/components/RunDetailLayout/RunDetailLayout.tsx index 35b7f76119..163d4b1f44 100644 --- a/web/src/components/RunDetailLayout/RunDetailLayout.tsx +++ b/web/src/components/RunDetailLayout/RunDetailLayout.tsx @@ -1,6 +1,7 @@ import {Tabs, TabsProps} from 'antd'; import {useEffect, useMemo, useState} from 'react'; import {useParams} from 'react-router-dom'; +import RunDetailAutomate from 'components/RunDetailAutomate'; import RunDetailTest from 'components/RunDetailTest'; import RunDetailTrace from 'components/RunDetailTrace'; import RunDetailTrigger from 'components/RunDetailTrigger'; @@ -16,7 +17,6 @@ import {ConfigMode} from 'types/DataStore.types'; import HeaderLeft from './HeaderLeft'; import HeaderRight from './HeaderRight'; import * as S from './RunDetailLayout.styled'; -import RunDetailAutomate from '../RunDetailAutomate/RunDetailAutomate'; interface IProps { test: Test; diff --git a/web/src/components/TransactionHeader/TransactionHeader.styled.ts b/web/src/components/TransactionHeader/TransactionHeader.styled.ts index dde3f25b8d..4e0d0cd943 100644 --- a/web/src/components/TransactionHeader/TransactionHeader.styled.ts +++ b/web/src/components/TransactionHeader/TransactionHeader.styled.ts @@ -1,5 +1,6 @@ import {LeftOutlined} from '@ant-design/icons'; import {Typography} from 'antd'; +import {Link as RRLink} from 'react-router-dom'; import styled from 'styled-components'; export const BackIcon = styled(LeftOutlined)` @@ -20,9 +21,14 @@ export const Container = styled.div` export const Section = styled.div` align-items: center; display: flex; + flex: 1; gap: 14px; `; +export const SectionRight = styled(Section)` + justify-content: flex-end; +`; + export const Text = styled(Typography.Text).attrs({ type: 'secondary', })` @@ -51,3 +57,32 @@ export const StateText = styled(Typography.Text)` color: ${({theme}) => theme.color.textSecondary}; } `; + +export const LinksContainer = styled.div` + a:first-child { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; + } + + a:last-child { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + } +`; + +export const Link = styled(RRLink)<{$isActive?: boolean}>` + && { + background-color: ${({theme, $isActive}) => ($isActive ? theme.color.primary : theme.color.white)}; + color: ${({theme, $isActive}) => ($isActive ? theme.color.white : theme.color.primary)}; + font-weight: 600; + padding: 7px 16px; + + border: 1px solid rgba(3, 24, 73, 0.1); + + &:hover, + &:visited, + &:focused { + color: ${({theme, $isActive}) => $isActive && theme.color.white}; + } + } +`; diff --git a/web/src/components/TransactionHeader/TransactionHeader.tsx b/web/src/components/TransactionHeader/TransactionHeader.tsx index 22fa0a7bda..58e449faa2 100644 --- a/web/src/components/TransactionHeader/TransactionHeader.tsx +++ b/web/src/components/TransactionHeader/TransactionHeader.tsx @@ -1,30 +1,42 @@ import {Button} from 'antd'; -import {useNavigate} from 'react-router-dom'; -import {useTransaction} from 'providers/Transaction/Transaction.provider'; +import {useLocation, useNavigate} from 'react-router-dom'; +import {TransactionRunStatusIcon} from 'components/RunStatusIcon'; +import TestState from 'components/TestState'; +import TransactionRunActionsMenu from 'components/TransactionRunActionsMenu'; import {TestState as TestStateEnum} from 'constants/TestRun.constants'; -import Transaction from 'models/Transaction.model'; -import TransactionRun from 'models/TransactionRun.model'; -import TestState from '../TestState'; +import {useTransaction} from 'providers/Transaction/Transaction.provider'; +import {useTransactionRun} from 'providers/TransactionRun/TransactionRun.provider'; import * as S from './TransactionHeader.styled'; -import TransactionRunActionsMenu from '../TransactionRunActionsMenu'; -import {TransactionRunStatusIcon} from '../RunStatusIcon'; -interface IProps { - transaction: Transaction; - transactionRun: TransactionRun; +const transactionLastPathRegex = /\/transaction\/[\w-]+\/run\/[\d-]+\/([\w-]+)/; + +function getLastPath(pathname: string): string { + const match = pathname.match(transactionLastPathRegex); + if (match === null) { + return ''; + } + + return match.length > 1 ? match[1] : ''; } -const TransactionHeader = ({ - transaction: {id: transactionId, name, version, description}, - transactionRun: {state, id: runId, allStepsRequiredGatesPassed}, -}: IProps) => { - const {onRun} = useTransaction(); +const LINKS = [ + {id: 'overview', label: 'Overview'}, + {id: 'automate', label: 'Automate'}, +]; + +const TransactionHeader = () => { + const {transaction, onRun} = useTransaction(); + const {transactionRun} = useTransactionRun(); const navigate = useNavigate(); + const {pathname} = useLocation(); + const {id: transactionId, name, version, description} = transaction; + const {state, id: runId, allStepsRequiredGatesPassed} = transactionRun; + const lastPath = getLastPath(pathname); return ( - navigate(-1)} data-cy="transaction-header-back-button"> + navigate('/')} data-cy="transaction-header-back-button">
@@ -34,7 +46,20 @@ const TransactionHeader = ({ {description}
- + + + {LINKS.map(({id, label}) => ( + + {label} + + ))} + + + {state && state !== TestStateEnum.FINISHED && ( Status: @@ -49,8 +74,8 @@ const TransactionHeader = ({ )} - - + +
); }; diff --git a/web/src/components/TransactionHeader/__tests__/TransactionHeader.test.tsx b/web/src/components/TransactionHeader/__tests__/TransactionHeader.test.tsx deleted file mode 100644 index ebec8b4ce7..0000000000 --- a/web/src/components/TransactionHeader/__tests__/TransactionHeader.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import {render} from 'test-utils'; -import TransactionRunModel from '../../../models/TransactionRun.model'; -import TransactionMock from '../../../models/__mocks__/Transaction.mock'; -import TransactionHeader from '../TransactionHeader'; - -test('TransactionHeader', () => { - const {getByTestId} = render( - - ); - expect(getByTestId('transaction-details-name')).toBeInTheDocument(); -}); diff --git a/web/src/components/TransactionRunActionsMenu/TransactionRunActionsMenu.tsx b/web/src/components/TransactionRunActionsMenu/TransactionRunActionsMenu.tsx index 8d98c765a0..219d23d310 100644 --- a/web/src/components/TransactionRunActionsMenu/TransactionRunActionsMenu.tsx +++ b/web/src/components/TransactionRunActionsMenu/TransactionRunActionsMenu.tsx @@ -3,18 +3,15 @@ import {useNavigate} from 'react-router-dom'; import useDeleteResourceRun from 'hooks/useDeleteResourceRun'; import {ResourceType} from 'types/Resource.type'; -import {useFileViewerModal} from '../FileViewerModal/FileViewerModal.provider'; import * as S from './TransactionRunActionsMenu.styled'; interface IProps { runId: string; transactionId: string; isRunView?: boolean; - transactionVersion: number; } -const TransactionRunActionsMenu = ({runId, transactionId, isRunView = false, transactionVersion}: IProps) => { - const {onDefinition} = useFileViewerModal(); +const TransactionRunActionsMenu = ({runId, transactionId, isRunView = false}: IProps) => { const navigate = useNavigate(); const onDelete = useDeleteResourceRun({id: transactionId, isRunView, type: ResourceType.Transaction}); @@ -23,30 +20,24 @@ const TransactionRunActionsMenu = ({runId, transactionId, isRunView = false, tra - onDefinition(ResourceType.Transaction, transactionId, transactionVersion)} - > - Transaction Definition + navigate(`/transaction/${transactionId}/run/${runId}/automate`)}> + Automate { domEvent.stopPropagation(); navigate(`/transaction/${transactionId}/run/${runId}`); }} - key="edit" > Edit { domEvent.stopPropagation(); onDelete(runId); }} - key="delete" > Delete diff --git a/web/src/components/TransactionRunLayout/TransactionRunLayout.styled.ts b/web/src/components/TransactionRunLayout/TransactionRunLayout.styled.ts deleted file mode 100644 index 262830675e..0000000000 --- a/web/src/components/TransactionRunLayout/TransactionRunLayout.styled.ts +++ /dev/null @@ -1,86 +0,0 @@ -import {Typography} from 'antd'; -import emptyStateIcon from 'assets/SpanAssertionsEmptyState.svg'; -import styled from 'styled-components'; - -export const TestDetailsHeader = styled.div` - display: flex; - width: 100%; - justify-content: space-between; - margin: 32px 0 24px; -`; - -export const Wrapper = styled.div<{detail?: boolean}>` - display: flex; - flex-grow: 1; - flex-direction: column; - background: ${({theme}) => theme.color.white}; -`; - -export const EmptyStateIcon = styled.img.attrs({ - src: emptyStateIcon, -})``; - -export const EmptyStateContainer = styled.div` - align-items: center; - display: flex; - flex-direction: column; - gap: 14px; - justify-content: center; - margin: 100px 0; -`; - -export const Container = styled.div` - display: flex; - height: 100%; - width: 100%; -`; - -export const Text = styled(Typography.Title).attrs({level: 3})<{opacity?: number}>` - && { - font-size: 12px; - color: ${({opacity}) => `rgba(3, 24, 73, ${opacity || 1})`}; - margin: 0 !important; - } -`; - -export const TagContainer = styled.div` - display: flex; - white-space: nowrap; - overflow: auto; -`; - -export const Title = styled(Typography.Title).attrs({level: 3})` - && { - margin: 0; - } -`; - -export const Stack = styled.div` - display: flex; - justify-content: space-between; - flex-direction: column; -`; - -export const Info = styled.div` - flex: 1; -`; - -export const Section = styled.div` - flex: 1; -`; - -export const SectionLeft = styled.div` - background-color: ${({theme}) => theme.color.white}; - overflow-y: auto; - z-index: 1; - flex-basis: 50%; -`; - -export const SectionRight = styled.div` - background-color: ${({theme}) => theme.color.white}; - border-left: 1px solid rgba(3, 24, 73, 0.1); - overflow-y: auto; - z-index: 2; - padding: 24px; - flex-basis: 50%; -`; diff --git a/web/src/components/TransactionRunLayout/TransactionRunLayout.tsx b/web/src/components/TransactionRunLayout/TransactionRunLayout.tsx index adffab78c7..9be41eb134 100644 --- a/web/src/components/TransactionRunLayout/TransactionRunLayout.tsx +++ b/web/src/components/TransactionRunLayout/TransactionRunLayout.tsx @@ -1,37 +1,23 @@ -import {useMemo} from 'react'; +import {useParams} from 'react-router-dom'; +import Layout from 'components/Layout'; import TransactionHeader from 'components/TransactionHeader'; -import useDocumentTitle from 'hooks/useDocumentTitle'; -import TransactionService from 'services/Transaction.service'; -import Transaction from 'models/Transaction.model'; -import TransactionRun from 'models/TransactionRun.model'; -import * as S from './TransactionRunLayout.styled'; -import EditTransaction from '../EditTransaction'; -import TransactionRunResult from '../TransactionRunResult/TransactionRunResult'; +import TransactionRunProvider from 'providers/TransactionRun/TransactionRun.provider'; interface IProps { - transaction: Transaction; - transactionRun: TransactionRun; + children: React.ReactNode; } -const TransactionRunDetailLayout = ({transaction, transactionRun}: IProps) => { - useDocumentTitle(`${transaction.name} - ${transactionRun.state}`); - const draftTransaction = useMemo(() => TransactionService.getInitialValues(transaction), [transaction]); +const TransactionRunLayout = ({children}: IProps) => { + const {transactionId = '', runId = ''} = useParams(); return ( - <> - - - - - - - - - - - - + + + + {children} + + ); }; -export default TransactionRunDetailLayout; +export default TransactionRunLayout; diff --git a/web/src/constants/Common.constants.ts b/web/src/constants/Common.constants.ts index 873b42a801..61a48131a0 100644 --- a/web/src/constants/Common.constants.ts +++ b/web/src/constants/Common.constants.ts @@ -9,6 +9,7 @@ export const GITHUB_ISSUES_URL = 'https://github.com/kubeshop/tracetest/issues/n export const DISCORD_URL = 'https://discord.gg/6zupCZFQbe'; export const OCTOLIINT_ISSUE_URL = 'https://github.com/kubeshop/tracetest/issues/2615'; export const CLI_RUNNING_TESTS_URL = 'https://docs.tracetest.io/cli/running-tests'; +export const CLI_RUNNING_TRANSACTIONS_URL = 'https://docs.tracetest.io/cli/running-transactions'; export const INGESTOR_ENDPOINT_URL = 'https://docs.tracetest.io/configuration/ingestor-endpoint'; diff --git a/web/src/pages/TransactionRunAutomate/Content.tsx b/web/src/pages/TransactionRunAutomate/Content.tsx new file mode 100644 index 0000000000..25e78ad81c --- /dev/null +++ b/web/src/pages/TransactionRunAutomate/Content.tsx @@ -0,0 +1,54 @@ +import {snakeCase} from 'lodash'; +import {useState} from 'react'; +import RunDetailAutomateDefinition from 'components/RunDetailAutomateDefinition'; +import RunDetailAutomateMethods from 'components/RunDetailAutomateMethods'; +import CliCommand from 'components/RunDetailAutomateMethods/methods/CLICommand'; +import {CLI_RUNNING_TRANSACTIONS_URL} from 'constants/Common.constants'; +import useDocumentTitle from 'hooks/useDocumentTitle'; +import {useEnvironment} from 'providers/Environment/Environment.provider'; +import {useTransaction} from 'providers/Transaction/Transaction.provider'; +import {ResourceType} from 'types/Resource.type'; +import * as S from './TransactionRunAutomate.styled'; + +const Content = () => { + const {transaction} = useTransaction(); + useDocumentTitle(`${transaction.name} - Automate`); + const [fileName, setFileName] = useState(`${snakeCase(transaction.name)}.yaml`); + const {selectedEnvironment: {id: environmentId} = {}} = useEnvironment(); + + return ( + + + + + + + ), + }, + ]} + /> + + + ); +}; + +export default Content; diff --git a/web/src/pages/TransactionRunAutomate/TransactionRunAutomate.styled.ts b/web/src/pages/TransactionRunAutomate/TransactionRunAutomate.styled.ts new file mode 100644 index 0000000000..4934a08a5d --- /dev/null +++ b/web/src/pages/TransactionRunAutomate/TransactionRunAutomate.styled.ts @@ -0,0 +1,24 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + background: ${({theme}) => theme.color.white}; + display: flex; + height: 100%; + width: 100%; +`; + +export const Section = styled.div` + background-color: ${({theme}) => theme.color.white}; + flex-basis: 50%; + overflow-y: auto; +`; + +export const SectionLeft = styled(Section)` + z-index: 1; +`; + +export const SectionRight = styled(Section)` + border-left: 1px solid rgba(3, 24, 73, 0.1); + padding: 24px; + z-index: 2; +`; diff --git a/web/src/pages/TransactionRunAutomate/TransactionRunAutomate.tsx b/web/src/pages/TransactionRunAutomate/TransactionRunAutomate.tsx new file mode 100644 index 0000000000..7f1efd14aa --- /dev/null +++ b/web/src/pages/TransactionRunAutomate/TransactionRunAutomate.tsx @@ -0,0 +1,11 @@ +import TransactionRunLayout from 'components/TransactionRunLayout'; +import withAnalytics from 'components/WithAnalytics/WithAnalytics'; +import Content from './Content'; + +const TransactionRunAutomate = () => ( + + + +); + +export default withAnalytics(TransactionRunAutomate, 'transaction-details-automate'); diff --git a/web/src/pages/TransactionRunDetail/index.ts b/web/src/pages/TransactionRunAutomate/index.ts similarity index 50% rename from web/src/pages/TransactionRunDetail/index.ts rename to web/src/pages/TransactionRunAutomate/index.ts index 64b930e16b..79155a46cc 100644 --- a/web/src/pages/TransactionRunDetail/index.ts +++ b/web/src/pages/TransactionRunAutomate/index.ts @@ -1,2 +1,2 @@ // eslint-disable-next-line no-restricted-exports -export {default} from './TransactionRunDetail'; +export {default} from './TransactionRunAutomate'; diff --git a/web/src/pages/TransactionRunDetail/Content.styled.tsx b/web/src/pages/TransactionRunDetail/Content.styled.tsx deleted file mode 100644 index 7bdea4aa36..0000000000 --- a/web/src/pages/TransactionRunDetail/Content.styled.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import {CheckCircleFilled, CloseCircleFilled} from '@ant-design/icons'; -import {Typography} from 'antd'; -import emptyStateIcon from 'assets/SpanAssertionsEmptyState.svg'; -import styled from 'styled-components'; - -export const TestDetailsHeader = styled.div` - display: flex; - width: 100%; - justify-content: space-between; - margin: 32px 0 24px; -`; - -export const Wrapper = styled.div<{detail?: boolean}>` - padding: 0 24px; - display: flex; - flex-grow: 1; - flex-direction: column; - background: ${({theme}) => theme.color.white}; -`; - -export const EmptyStateIcon = styled.img.attrs({ - src: emptyStateIcon, -})``; - -export const EmptyStateContainer = styled.div` - align-items: center; - display: flex; - flex-direction: column; - gap: 14px; - justify-content: center; - margin: 100px 0; -`; - -export const Container = styled.div` - display: flex; - height: 100%; - width: 100%; -`; - -export const Containerr = styled.div` - align-items: center; - border: ${({theme}) => `1px solid ${theme.color.borderLight}`}; - border-radius: 2px; - background: ${({theme}) => theme.color.background}; - display: flex; - gap: 16px; - padding: 7px 16px; - margin-bottom: 8px; - height: 58px; -`; - -export const Text = styled(Typography.Title).attrs({level: 3})<{opacity?: number}>` - && { - font-size: 12px; - color: ${({opacity}) => `rgba(3, 24, 73, ${opacity || 1})`}; - margin: 0 !important; - } -`; - -export const TagContainer = styled.div` - display: flex; - white-space: nowrap; - overflow: auto; -`; - -export const Title = styled(Typography.Title).attrs({level: 3})` - && { - margin: 0; - } -`; - -export const IconSuccess = styled(CheckCircleFilled)` - color: ${({theme}) => theme.color.success}; -`; - -export const IconFail = styled(CloseCircleFilled)` - color: ${({theme}) => theme.color.error}; -`; - -export const Infoo = styled.div` - display: flex; - justify-content: space-between; - width: 100%; - height: 100%; -`; -export const Stack = styled.div` - display: flex; - justify-content: space-between; - flex-direction: column; -`; - -export const Info = styled.div` - flex: 1; -`; - -export const Section = styled.div` - flex: 1; -`; - -export const SectionLeft = styled.div` - background-color: ${({theme}) => theme.color.white}; - overflow-y: auto; - z-index: 1; - padding: 24px; - flex-basis: 50%; -`; - -export const SectionRight = styled.div` - background-color: ${({theme}) => theme.color.white}; - border-left: 1px solid rgba(3, 24, 73, 0.1); - overflow-y: auto; - z-index: 2; - padding: 24px; - flex-basis: 50%; -`; diff --git a/web/src/pages/TransactionRunDetail/Content.tsx b/web/src/pages/TransactionRunDetail/Content.tsx deleted file mode 100644 index 2b7e6bad80..0000000000 --- a/web/src/pages/TransactionRunDetail/Content.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import TransactionRunLayout from 'components/TransactionRunLayout'; -import {useTransaction} from 'providers/Transaction/Transaction.provider'; -import {useTransactionRun} from 'providers/TransactionRun/TransactionRun.provider'; - -const Content = () => { - const {transaction} = useTransaction(); - const {transactionRun} = useTransactionRun(); - - return transaction ? : null; -}; - -export default Content; diff --git a/web/src/pages/TransactionRunDetail/TransactionRunDetail.tsx b/web/src/pages/TransactionRunDetail/TransactionRunDetail.tsx deleted file mode 100644 index 062633aab3..0000000000 --- a/web/src/pages/TransactionRunDetail/TransactionRunDetail.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import {useParams} from 'react-router-dom'; -import Layout from 'components/Layout'; -import withAnalytics from 'components/WithAnalytics/WithAnalytics'; -import TransactionRunProvider from 'providers/TransactionRun/TransactionRun.provider'; -import TransactionContent from './Content'; - -const TransactionRunDetail = () => { - const {transactionId = '', runId = ''} = useParams(); - - return ( - - - - - - ); -}; - -export default withAnalytics(TransactionRunDetail, 'transaction-details'); diff --git a/web/src/pages/TransactionRunOverview/Content.tsx b/web/src/pages/TransactionRunOverview/Content.tsx new file mode 100644 index 0000000000..9f6af8ab21 --- /dev/null +++ b/web/src/pages/TransactionRunOverview/Content.tsx @@ -0,0 +1,28 @@ +import {useMemo} from 'react'; +import EditTransaction from 'components/EditTransaction'; +import TransactionRunResult from 'components/TransactionRunResult'; +import useDocumentTitle from 'hooks/useDocumentTitle'; +import {useTransaction} from 'providers/Transaction/Transaction.provider'; +import {useTransactionRun} from 'providers/TransactionRun/TransactionRun.provider'; +import TransactionService from 'services/Transaction.service'; +import * as S from './TransactionRunOverview.styled'; + +const Content = () => { + const {transaction} = useTransaction(); + const {transactionRun} = useTransactionRun(); + useDocumentTitle(`${transaction.name} - ${transactionRun.state}`); + const draftTransaction = useMemo(() => TransactionService.getInitialValues(transaction), [transaction]); + + return ( + + + + + + + + + ); +}; + +export default Content; diff --git a/web/src/pages/TransactionRunOverview/TransactionRunOverview.styled.ts b/web/src/pages/TransactionRunOverview/TransactionRunOverview.styled.ts new file mode 100644 index 0000000000..4934a08a5d --- /dev/null +++ b/web/src/pages/TransactionRunOverview/TransactionRunOverview.styled.ts @@ -0,0 +1,24 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + background: ${({theme}) => theme.color.white}; + display: flex; + height: 100%; + width: 100%; +`; + +export const Section = styled.div` + background-color: ${({theme}) => theme.color.white}; + flex-basis: 50%; + overflow-y: auto; +`; + +export const SectionLeft = styled(Section)` + z-index: 1; +`; + +export const SectionRight = styled(Section)` + border-left: 1px solid rgba(3, 24, 73, 0.1); + padding: 24px; + z-index: 2; +`; diff --git a/web/src/pages/TransactionRunOverview/TransactionRunOverview.tsx b/web/src/pages/TransactionRunOverview/TransactionRunOverview.tsx new file mode 100644 index 0000000000..c4435fcc5b --- /dev/null +++ b/web/src/pages/TransactionRunOverview/TransactionRunOverview.tsx @@ -0,0 +1,11 @@ +import TransactionRunLayout from 'components/TransactionRunLayout'; +import withAnalytics from 'components/WithAnalytics/WithAnalytics'; +import Content from './Content'; + +const TransactionRunOverview = () => ( + + + +); + +export default withAnalytics(TransactionRunOverview, 'transaction-details-overview'); diff --git a/web/src/pages/TransactionRunOverview/index.ts b/web/src/pages/TransactionRunOverview/index.ts new file mode 100644 index 0000000000..15285a7d3d --- /dev/null +++ b/web/src/pages/TransactionRunOverview/index.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-restricted-exports +export {default} from './TransactionRunOverview'; diff --git a/web/src/providers/TransactionRun/TransactionRun.provider.tsx b/web/src/providers/TransactionRun/TransactionRun.provider.tsx index 13a26e4a85..f0c21b4981 100644 --- a/web/src/providers/TransactionRun/TransactionRun.provider.tsx +++ b/web/src/providers/TransactionRun/TransactionRun.provider.tsx @@ -4,11 +4,11 @@ import TransactionRun from 'models/TransactionRun.model'; import TransactionProvider from '../Transaction/Transaction.provider'; interface IContext { - transactionRun?: TransactionRun; + transactionRun: TransactionRun; } export const Context = createContext({ - transactionRun: undefined, + transactionRun: {} as TransactionRun, }); interface IProps { @@ -21,7 +21,7 @@ export const useTransactionRun = () => useContext(Context); const TransactionRunProvider = ({children, transactionId, runId}: IProps) => { const {data: transactionRun} = useGetTransactionRunByIdQuery({transactionId, runId}); - const value = useMemo(() => ({transactionRun}), [transactionRun]); + const value = useMemo(() => ({transactionRun: transactionRun!}), [transactionRun]); return transactionRun ? ( diff --git a/web/src/services/CliCommand.service.ts b/web/src/services/CliCommand.service.ts index 00a48e943c..a7fc7193a2 100644 --- a/web/src/services/CliCommand.service.ts +++ b/web/src/services/CliCommand.service.ts @@ -1,4 +1,4 @@ -import Test from 'models/Test.model'; +import {ResourceType} from 'types/Resource.type'; import {getServerBaseUrl} from 'utils/Common'; export enum CliCommandOption { @@ -19,15 +19,16 @@ export type TCliCommandEnabledOptions = Record; export type TCliCommandConfig = { options: TCliCommandEnabledOptions; - format: CliCommandFormat; + id: string; environmentId?: string; - test: Test; fileName: string; + format: CliCommandFormat; + resourceType: ResourceType; }; type TApplyProps = { command: string; - test: Test; + id: string; environmentId?: string; enabled: boolean; fileName: string; @@ -36,7 +37,7 @@ type TApplyOption = (props: TApplyProps) => string; const CliCommandService = () => ({ applyOptions: { - [CliCommandOption.UseId]: ({enabled, command, test: {id}, fileName}) => + [CliCommandOption.UseId]: ({enabled, command, id, fileName}) => `${command} ${enabled ? `--id ${id}` : `--file ${fileName}`}`, [CliCommandOption.SkipResultWait]: ({command, enabled}) => (enabled ? `${command} --skip-result-wait` : command), [CliCommandOption.UseHostname]: ({command, enabled}) => { @@ -53,11 +54,11 @@ const CliCommandService = () => ({ : 'tracetest' } ${command}`, } as Record, - getCommand({options, format, test, environmentId, fileName}: TCliCommandConfig) { + getCommand({options, format, id, environmentId, fileName, resourceType}: TCliCommandConfig) { const command = Object.entries(options).reduce( (acc, [option, enabled]) => - this.applyOptions[option as CliCommandOption]({command: acc, enabled, test, environmentId, fileName}), - 'run test' + this.applyOptions[option as CliCommandOption]({command: acc, enabled, id, environmentId, fileName}), + `run ${resourceType}` ); return `${command} --output ${format}`;