diff --git a/web/src/components/RunDetailLayout/RunDetailLayout.tsx b/web/src/components/RunDetailLayout/RunDetailLayout.tsx index 7bba0e246b..20d9fd124d 100644 --- a/web/src/components/RunDetailLayout/RunDetailLayout.tsx +++ b/web/src/components/RunDetailLayout/RunDetailLayout.tsx @@ -72,10 +72,10 @@ const RunDetailLayout = ({test: {id, name, trigger, version = 1}, test}: IProps) - + - + diff --git a/web/src/components/RunDetailTest/RunDetailTest.tsx b/web/src/components/RunDetailTest/RunDetailTest.tsx index 9e85d8b240..4f09c8fa5f 100644 --- a/web/src/components/RunDetailTest/RunDetailTest.tsx +++ b/web/src/components/RunDetailTest/RunDetailTest.tsx @@ -13,6 +13,9 @@ import TestSpecDetail from 'components/TestSpecDetail'; import TestSpecForm from 'components/TestSpecForm'; import {useTestSpecForm} from 'components/TestSpecForm/TestSpecForm.provider'; import Switch from 'components/Visualization/components/Switch'; +import {TAssertionResultEntry} from 'models/AssertionResults.model'; +import TestRun from 'models/TestRun.model'; +import TestRunEvent from 'models/TestRunEvent.model'; import {useGuidedTour} from 'providers/GuidedTour/GuidedTour.provider'; import {useSpan} from 'providers/Span/Span.provider'; import {useTestOutput} from 'providers/TestOutput/TestOutput.provider'; @@ -20,8 +23,6 @@ import {useTestSpecs} from 'providers/TestSpecs/TestSpecs.provider'; import AssertionAnalyticsService from 'services/Analytics/AssertionAnalytics.service'; import TestRunAnalytics from 'services/Analytics/TestRunAnalytics.service'; import AssertionService from 'services/Assertion.service'; -import TestRun from 'models/TestRun.model'; -import {TAssertionResultEntry} from 'models/AssertionResults.model'; import * as S from './RunDetailTest.styled'; import Visualization from './Visualization'; @@ -32,10 +33,11 @@ const TABS = { interface IProps { run: TestRun; + runEvents: TestRunEvent[]; testId: string; } -const RunDetailTest = ({run, testId}: IProps) => { +const RunDetailTest = ({run, runEvents, testId}: IProps) => { const [query, updateQuery] = useSearchParams(); const {selectedSpan, onSetFocusedSpan, onSelectSpan} = useSpan(); const {remove, revert, selectedTestSpec, setSelectedSpec, setSelectorSuggestions, setPrevSelector, specs} = @@ -125,7 +127,12 @@ const RunDetailTest = ({run, testId}: IProps) => { /> - + diff --git a/web/src/components/RunDetailTest/Visualization.tsx b/web/src/components/RunDetailTest/Visualization.tsx index 882e05d47e..944ab81065 100644 --- a/web/src/components/RunDetailTest/Visualization.tsx +++ b/web/src/components/RunDetailTest/Visualization.tsx @@ -2,11 +2,14 @@ import {useCallback, useEffect} from 'react'; import {Node, NodeChange} from 'react-flow-renderer'; import {VisualizationType} from 'components/RunDetailTrace/RunDetailTrace'; -import SkeletonDiagram from 'components/SkeletonDiagram'; +import RunEvents from 'components/RunEvents'; import {useTestSpecForm} from 'components/TestSpecForm/TestSpecForm.provider'; import DAG from 'components/Visualization/components/DAG'; import Timeline from 'components/Visualization/components/Timeline'; import {TestState} from 'constants/TestRun.constants'; +import {TestRunStage} from 'constants/TestRunEvents.constants'; +import Span from 'models/Span.model'; +import TestRunEvent from 'models/TestRunEvent.model'; import {useSpan} from 'providers/Span/Span.provider'; import {useAppDispatch, useAppSelector} from 'redux/hooks'; import {initNodes, onNodesChange as onNodesChangeAction} from 'redux/slices/DAG.slice'; @@ -14,15 +17,15 @@ import DAGSelectors from 'selectors/DAG.selectors'; import TraceAnalyticsService from 'services/Analytics/TestRunAnalytics.service'; import TraceDiagramAnalyticsService from 'services/Analytics/TraceDiagramAnalytics.service'; import {TTestRunState} from 'types/TestRun.types'; -import Span from 'models/Span.model'; export interface IProps { + runEvents: TestRunEvent[]; runState: TTestRunState; spans: Span[]; type: VisualizationType; } -const Visualization = ({runState, spans, type}: IProps) => { +const Visualization = ({runEvents, runState, spans, type}: IProps) => { const dispatch = useAppDispatch(); const edges = useAppSelector(DAGSelectors.selectEdges); const nodes = useAppSelector(DAGSelectors.selectNodes); @@ -67,7 +70,7 @@ const Visualization = ({runState, spans, type}: IProps) => { ); if (runState !== TestState.FINISHED) { - return ; + return ; } return type === VisualizationType.Dag ? ( diff --git a/web/src/components/RunDetailTrace/RunDetailTrace.tsx b/web/src/components/RunDetailTrace/RunDetailTrace.tsx index da582681e2..af9ae3e777 100644 --- a/web/src/components/RunDetailTrace/RunDetailTrace.tsx +++ b/web/src/components/RunDetailTrace/RunDetailTrace.tsx @@ -5,10 +5,11 @@ import Drawer from 'components/Drawer'; import SpanDetail from 'components/SpanDetail'; import Switch from 'components/Visualization/components/Switch'; import {TestState} from 'constants/TestRun.constants'; +import TestRun from 'models/TestRun.model'; +import TestRunEvent from 'models/TestRunEvent.model'; import SpanSelectors from 'selectors/Span.selectors'; import TraceSelectors from 'selectors/Trace.selectors'; import TraceAnalyticsService from 'services/Analytics/TestRunAnalytics.service'; -import TestRun from 'models/TestRun.model'; import * as S from './RunDetailTrace.styled'; import Search from './Search'; import Visualization from './Visualization'; @@ -16,6 +17,7 @@ import SetupAlert from '../SetupAlert'; interface IProps { run: TestRun; + runEvents: TestRunEvent[]; testId: string; } @@ -24,7 +26,7 @@ export enum VisualizationType { Timeline, } -const RunDetailTrace = ({run, testId}: IProps) => { +const RunDetailTrace = ({run, runEvents, testId}: IProps) => { const selectedSpan = useAppSelector(TraceSelectors.selectSelectedSpan); const searchText = useAppSelector(TraceSelectors.selectSearchText); const span = useAppSelector(state => SpanSelectors.selectSpanById(state, selectedSpan, testId, run.id)); @@ -58,7 +60,12 @@ const RunDetailTrace = ({run, testId}: IProps) => { /> )} - + } diff --git a/web/src/components/RunDetailTrace/Visualization.tsx b/web/src/components/RunDetailTrace/Visualization.tsx index f23b3d17d2..4ee70fc127 100644 --- a/web/src/components/RunDetailTrace/Visualization.tsx +++ b/web/src/components/RunDetailTrace/Visualization.tsx @@ -1,5 +1,7 @@ -import SkeletonDiagram from 'components/SkeletonDiagram'; +import RunEvents from 'components/RunEvents'; import {TestState} from 'constants/TestRun.constants'; +import {TestRunStage} from 'constants/TestRunEvents.constants'; +import TestRunEvent from 'models/TestRunEvent.model'; import {useCallback, useEffect} from 'react'; import {Node, NodeChange} from 'react-flow-renderer'; import {useAppDispatch, useAppSelector} from 'redux/hooks'; @@ -15,12 +17,13 @@ import Timeline from '../Visualization/components/Timeline'; import {VisualizationType} from './RunDetailTrace'; interface IProps { + runEvents: TestRunEvent[]; runState: TTestRunState; spans: Span[]; type: VisualizationType; } -const Visualization = ({runState, spans, type}: IProps) => { +const Visualization = ({runEvents, runState, spans, type}: IProps) => { const dispatch = useAppDispatch(); const edges = useAppSelector(TraceSelectors.selectEdges); const matchedSpans = useAppSelector(TraceSelectors.selectMatchedSpans); @@ -67,7 +70,7 @@ const Visualization = ({runState, spans, type}: IProps) => { ); if (runState !== TestState.FINISHED) { - return ; + return ; } return type === VisualizationType.Dag ? ( diff --git a/web/src/components/RunDetailTrigger/RunDetailTrigger.tsx b/web/src/components/RunDetailTrigger/RunDetailTrigger.tsx index ca22728961..853d60a664 100644 --- a/web/src/components/RunDetailTrigger/RunDetailTrigger.tsx +++ b/web/src/components/RunDetailTrigger/RunDetailTrigger.tsx @@ -3,9 +3,10 @@ import RunDetailTriggerResponseFactory from 'components/RunDetailTriggerResponse import RunEvents from 'components/RunEvents'; import {TriggerTypes} from 'constants/Test.constants'; import {TestState} from 'constants/TestRun.constants'; +import {TestRunStage} from 'constants/TestRunEvents.constants'; import Test from 'models/Test.model'; import TestRun from 'models/TestRun.model'; -import TestRunEvent, {TestRunStage} from 'models/TestRunEvent.model'; +import TestRunEvent from 'models/TestRunEvent.model'; import * as S from './RunDetailTrigger.styled'; interface IProps { @@ -25,7 +26,7 @@ const RunDetailTrigger = ({test, run: {state, triggerResult, triggerTime}, runEv {shouldDisplayError ? ( - + ) : ( ( +const RunEvent = ({event}: IPropsEvent) => ( {event.title} diff --git a/web/src/components/RunEvents/RunEventDataStore.tsx b/web/src/components/RunEvents/RunEventDataStore.tsx new file mode 100644 index 0000000000..6efa815b29 --- /dev/null +++ b/web/src/components/RunEvents/RunEventDataStore.tsx @@ -0,0 +1,24 @@ +import {Typography} from 'antd'; +import TestConnectionNotification from 'components/TestConnectionNotification/TestConnectionNotification'; +import {IPropsEvent} from './RunEvent'; +import * as S from './RunEvents.styled'; + +const RunEventDataStore = ({event}: IPropsEvent) => ( + + + {event.title} + {event.description} + {!!event.dataStoreConnection && ( + + + {event.dataStoreConnection?.allPassed + ? 'Data store configuration is valid.' + : 'Data store configuration is not valid.'} + + + + )} + +); + +export default RunEventDataStore; diff --git a/web/src/components/RunEvents/RunEventPolling.tsx b/web/src/components/RunEvents/RunEventPolling.tsx new file mode 100644 index 0000000000..a320e2378e --- /dev/null +++ b/web/src/components/RunEvents/RunEventPolling.tsx @@ -0,0 +1,38 @@ +import {Typography} from 'antd'; +import {IPropsEvent} from './RunEvent'; +import * as S from './RunEvents.styled'; + +const RunEventPolling = ({event}: IPropsEvent) => ( + + + {event.title} + {event.description} + {!!event.polling && ( + + + +
+ Number of spans collected: + {event.polling.periodic.numberSpans} +
+
+ + +
+ Iteration number: + {event.polling.periodic.numberIterations} +
+
+ + +
+ Reason why the next iteration will be executed: + {event.polling.reasonNextIteration} +
+
+
+ )} +
+); + +export default RunEventPolling; diff --git a/web/src/components/RunEvents/RunEvents.styled.ts b/web/src/components/RunEvents/RunEvents.styled.ts index ab645bb772..64a1f9bfeb 100644 --- a/web/src/components/RunEvents/RunEvents.styled.ts +++ b/web/src/components/RunEvents/RunEvents.styled.ts @@ -1,8 +1,7 @@ -import {CloseCircleFilled} from '@ant-design/icons'; +import {CloseCircleFilled, InfoCircleFilled, LoadingOutlined} from '@ant-design/icons'; import {Typography} from 'antd'; -import styled, {DefaultTheme} from 'styled-components'; - -import {LogLevel} from 'models/TestRunEvent.model'; +import styled, {css, DefaultTheme} from 'styled-components'; +import {LogLevel} from 'constants/TestRunEvents.constants'; function getLogLevelColor(logLevel: LogLevel, theme: DefaultTheme): string { if (logLevel === LogLevel.Error) return theme.color.error; @@ -11,12 +10,29 @@ function getLogLevelColor(logLevel: LogLevel, theme: DefaultTheme): string { return theme.color.textLight; } -export const Container = styled.div` +export const Column = styled.div` + align-items: start; + display: flex; + gap: 6px; + margin-bottom: 8px; +`; + +export const Container = styled.div<{$hasScroll?: boolean}>` align-items: center; display: flex; flex-direction: column; - justify-content: center; - padding: 70px 120px; + padding: 70px 20%; + + ${({$hasScroll}) => + $hasScroll && + css` + height: 100%; + overflow-y: auto; + `} +`; + +export const Content = styled.div` + margin: 16px; `; export const Dot = styled.div<{$logLevel?: LogLevel}>` @@ -42,6 +58,11 @@ export const EventContainer = styled.div` position: relative; `; +export const InfoIcon = styled(InfoCircleFilled)` + color: ${({theme}) => theme.color.textLight}; + margin-top: 3px; +`; + export const Link = styled(Typography.Link)` font-weight: bold; `; @@ -50,6 +71,12 @@ export const ListContainer = styled.div` padding: 24px 0; `; +export const LoadingIcon = styled(LoadingOutlined)` + color: ${({theme}) => theme.color.primary}; + font-size: 32px; + margin-bottom: 26px; +`; + export const Paragraph = styled(Typography.Paragraph)` text-align: center; `; diff --git a/web/src/components/RunEvents/RunEvents.tsx b/web/src/components/RunEvents/RunEvents.tsx index bd01fa6285..a6b455796b 100644 --- a/web/src/components/RunEvents/RunEvents.tsx +++ b/web/src/components/RunEvents/RunEvents.tsx @@ -1,9 +1,13 @@ -import TestRunEvent, {TestRunStage} from 'models/TestRunEvent.model'; +import {TestRunStage} from 'constants/TestRunEvents.constants'; +import TestRunEvent from 'models/TestRunEvent.model'; import TestRunService from 'services/TestRun.service'; +import {TTestRunState} from 'types/TestRun.types'; +import RunEventsTrace from './RunEventsTrace'; import RunEventsTrigger from './RunEventsTrigger'; export interface IPropsComponent { events: TestRunEvent[]; + state: TTestRunState; } interface IProps extends IPropsComponent { @@ -12,15 +16,15 @@ interface IProps extends IPropsComponent { const ComponentMap: Record React.ReactElement> = { [TestRunStage.Trigger]: RunEventsTrigger, - [TestRunStage.Trace]: RunEventsTrigger, - [TestRunStage.Test]: RunEventsTrigger, + [TestRunStage.Trace]: RunEventsTrace, + [TestRunStage.Test]: RunEventsTrace, }; -const RunEvents = ({events, stage}: IProps) => { +const RunEvents = ({events, stage, state}: IProps) => { const Component = ComponentMap[stage]; const filteredEvents = TestRunService.getTestRunEventsByStage(events, stage); - return ; + return ; }; export default RunEvents; diff --git a/web/src/components/RunEvents/RunEventsTrace.tsx b/web/src/components/RunEvents/RunEventsTrace.tsx new file mode 100644 index 0000000000..3c971f203c --- /dev/null +++ b/web/src/components/RunEvents/RunEventsTrace.tsx @@ -0,0 +1,62 @@ +import {Typography} from 'antd'; + +import {TRACE_DOCUMENTATION_URL} from 'constants/Common.constants'; +import {TestState} from 'constants/TestRun.constants'; +import {TraceEventType} from 'constants/TestRunEvents.constants'; +import TestRunService from 'services/TestRun.service'; +import RunEvent, {IPropsEvent} from './RunEvent'; +import RunEventDataStore from './RunEventDataStore'; +import RunEventPolling from './RunEventPolling'; +import {IPropsComponent} from './RunEvents'; +import * as S from './RunEvents.styled'; + +const ComponentMap: Record React.ReactElement> = { + [TraceEventType.DATA_STORE_CONNECTION_INFO]: RunEventDataStore, + [TraceEventType.POLLING_ITERATION_INFO]: RunEventPolling, +}; + +const RunEventsTrace = ({events, state}: IPropsComponent) => { + const filteredEvents = TestRunService.getTestRunEventsWithLastPolling(events); + + const loadingHeader = ( + <> + + + We are working to gather the trace associated with this test run + + + Want to know more about traces? Head to the official{' '} + + Open Telemetry Documentation + + + + ); + + const failedHeader = ( + <> + + + Trace Fetch Failed + + + We prepared the breakdown of diagnostic steps and tips to help identify the source of the issue: + + + ); + + return ( + + {state === TestState.FAILED ? failedHeader : loadingHeader} + + + {filteredEvents.map(event => { + const Component = ComponentMap[event.type as TraceEventType] ?? RunEvent; + return ; + })} + + + ); +}; + +export default RunEventsTrace; diff --git a/web/src/components/RunEvents/RunEventsTrigger.tsx b/web/src/components/RunEvents/RunEventsTrigger.tsx index 257aabcaf6..c92d90aa66 100644 --- a/web/src/components/RunEvents/RunEventsTrigger.tsx +++ b/web/src/components/RunEvents/RunEventsTrigger.tsx @@ -8,7 +8,7 @@ import * as S from './RunEvents.styled'; const RunEventsTrigger = ({events}: IPropsComponent) => ( - Test Run Failed + Test Trigger Failed We prepared the breakdown of diagnostic steps and tips to help identify the source of the issue: @@ -19,17 +19,15 @@ const RunEventsTrigger = ({events}: IPropsComponent) => ( ))} - - - - Create an issue - {' '} - or contact us via{' '} - - Discord - - . We will check it out and will help you rectify the issue. - + + + Create an issue + {' '} + or contact us via{' '} + + Discord + + . We will check it out and will help you rectify the issue. ); diff --git a/web/src/components/SkeletonDiagram/SkeletonDiagram.styled.ts b/web/src/components/SkeletonDiagram/SkeletonDiagram.styled.ts deleted file mode 100644 index 42fe0fe479..0000000000 --- a/web/src/components/SkeletonDiagram/SkeletonDiagram.styled.ts +++ /dev/null @@ -1,78 +0,0 @@ -import styled from 'styled-components'; - -export const Container = styled.div` - position: relative; - height: 100%; - - > .react-flow { - height: calc(100% - 76px); - } - - .react-flow__attribution { - visibility: hidden; - } -`; - -export const SkeletonDiagramMessage = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - margin-top: 24px; -`; - -export const SkeletonNode = styled.div` - background-color: ${({theme}) => theme.color.white}; - border: ${({theme}) => `1px solid ${theme.color.border}`}; - border-radius: 10px; - min-width: fit-content; - display: flex; - width: 150px; - max-width: 150px; - height: 75px; - justify-content: center; - align-items: center; - overflow: hidden; -`; - -export const SkeletonNotch = styled.div` - background-color: ${({theme}) => theme.color.background}; - position: absolute; - top: 0; - margin-top: 1px; - padding: 3px 6px; - border-radius: 10px 10px 0 0; - width: 99%; - font-weight: 700; - height: 40px; - padding-top: 10px; -`; - -export const TextContainer = styled.div` - padding: 6px; - padding-top: 30px; - display: flex; - flex-direction: column; - gap: 4px; - justify-content: center; - width: 150px; - max-width: 150px; - height: 38px; - box-sizing: content-box; -`; - -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: 8px; - border-radius: 10px; - width: ${({$width = 100}) => $width}%; -`; diff --git a/web/src/components/SkeletonDiagram/SkeletonDiagram.tsx b/web/src/components/SkeletonDiagram/SkeletonDiagram.tsx deleted file mode 100644 index 311474f1d1..0000000000 --- a/web/src/components/SkeletonDiagram/SkeletonDiagram.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, {useMemo} from 'react'; -import {Typography} from 'antd'; -import ReactFlow from 'react-flow-renderer'; - -import {TRACE_DOCUMENTATION_URL} from 'constants/Common.constants'; -import {skeletonNodesDatum} from 'constants/DAG.constants'; -import DAGService from 'services/DAG.service'; -import * as S from './SkeletonDiagram.styled'; -import SkeletonNode from './SkeletonNode'; - -/** Important to define the nodeTypes outside the component to prevent re-renderings */ -const nodeTypes = {skeleton: SkeletonNode}; - -const SkeletonDiagram = () => { - const {edges, nodes} = useMemo(() => DAGService.getEdgesAndNodes(skeletonNodesDatum), []); - - return ( - - - - We are working on your trace - - - Want to know more about traces? Head to the official{' '} - - Open Telemetry Documentation - - - - - - - ); -}; - -export default SkeletonDiagram; diff --git a/web/src/components/SkeletonDiagram/SkeletonNode.tsx b/web/src/components/SkeletonDiagram/SkeletonNode.tsx deleted file mode 100644 index 6e095c7fd3..0000000000 --- a/web/src/components/SkeletonDiagram/SkeletonNode.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import {Handle, NodeProps, Position} from 'react-flow-renderer'; -import * as S from './SkeletonDiagram.styled'; - -const SkeletonNode: React.FC> = ({id}) => { - return ( - - - - - - - - - - - - ); -}; - -export default SkeletonNode; diff --git a/web/src/components/SkeletonDiagram/__tests__/SkeletonDiagram.test.tsx b/web/src/components/SkeletonDiagram/__tests__/SkeletonDiagram.test.tsx deleted file mode 100644 index f5b504face..0000000000 --- a/web/src/components/SkeletonDiagram/__tests__/SkeletonDiagram.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import {render} from 'test-utils'; -import SkeletonDiagram from '../SkeletonDiagram'; - -describe('SkeletonDiagram', () => { - it('should render correctly', () => { - const {getByTestId} = render(); - - expect(getByTestId('skeleton-diagram')).toBeInTheDocument(); - }); -}); diff --git a/web/src/components/SkeletonDiagram/index.ts b/web/src/components/SkeletonDiagram/index.ts deleted file mode 100644 index 859b015e09..0000000000 --- a/web/src/components/SkeletonDiagram/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line no-restricted-exports -export {default} from './SkeletonDiagram'; diff --git a/web/src/constants/TestRunEvents.constants.ts b/web/src/constants/TestRunEvents.constants.ts new file mode 100644 index 0000000000..e76ed6c594 --- /dev/null +++ b/web/src/constants/TestRunEvents.constants.ts @@ -0,0 +1,27 @@ +export enum TestRunStage { + Trigger = 'trigger', + Trace = 'trace', + Test = 'test', +} + +export enum PollingInfoType { + Periodic = 'periodic', +} + +export enum OutputInfoLogLevel { + Warning = 'warning', + Error = 'error', +} + +export enum LogLevel { + Error = 'Error', + Info = 'Info', + Start = 'Start', + Success = 'Success', + Warning = 'Warning', +} + +export enum TraceEventType { + DATA_STORE_CONNECTION_INFO = 'DATA_STORE_CONNECTION_INFO', + POLLING_ITERATION_INFO = 'POLLING_ITERATION_INFO', +} diff --git a/web/src/models/TestRunEvent.model.ts b/web/src/models/TestRunEvent.model.ts index 454b714f91..699e948dcd 100644 --- a/web/src/models/TestRunEvent.model.ts +++ b/web/src/models/TestRunEvent.model.ts @@ -1,29 +1,7 @@ +import {LogLevel, OutputInfoLogLevel, PollingInfoType, TestRunStage} from 'constants/TestRunEvents.constants'; import {Model, TTestEventsSchemas} from 'types/Common.types'; import ConnectionResult from './ConnectionResult.model'; -export enum TestRunStage { - Trigger = 'trigger', - Trace = 'trace', - Test = 'test', -} - -enum PollingInfoType { - Periodic = 'periodic', -} - -enum OutputInfoLogLevel { - Warning = 'warning', - Error = 'error', -} - -export enum LogLevel { - Error = 'Error', - Info = 'Info', - Start = 'Start', - Success = 'Success', - Warning = 'Warning', -} - export type TRawTestRunEvent = TTestEventsSchemas['TestRunEvent']; export type TRawPollingInfo = TTestEventsSchemas['PollingInfo']; export type TRawOutputInfo = TTestEventsSchemas['OutputInfo']; diff --git a/web/src/redux/apis/endpoints/TestRun.endpoints.ts b/web/src/redux/apis/endpoints/TestRun.endpoints.ts index 3a2696ff29..75e37bbf4c 100644 --- a/web/src/redux/apis/endpoints/TestRun.endpoints.ts +++ b/web/src/redux/apis/endpoints/TestRun.endpoints.ts @@ -10,8 +10,7 @@ import RunError from 'models/RunError.model'; import {TEnvironmentValue} from 'models/Environment.model'; import {TRawTestSpecs} from 'models/TestSpecs.model'; import Test from 'models/Test.model'; -import TestRunEvent from 'models/TestRunEvent.model'; -import TestRunEventMock from 'models/__mocks__/TestRunEvent.mock'; +import TestRunEvent, {TRawTestRunEvent} from 'models/TestRunEvent.model'; function getTotalCountFromHeaders(meta: any) { return Number(meta?.response?.headers.get('x-total-count') || 0); @@ -102,11 +101,7 @@ const TestRunEndpoint = (builder: TTestApiEndpointBuilder) => ({ getRunEvents: builder.query({ query: ({runId, testId}) => `/tests/${testId}/run/${runId}/events`, providesTags: [{type: TracetestApiTags.TEST_RUN, id: 'EVENTS'}], - // transformResponse: (rawTestRunEvent: TRawTestRunEvent[]) => rawTestRunEvent.map(event => TestRunEvent(event)), - transformResponse: () => { - const rawTestRunEvent = new Array(10).fill(null).map(() => TestRunEventMock.raw()); - return rawTestRunEvent.map(event => TestRunEvent(event)); - }, + transformResponse: (rawTestRunEvent: TRawTestRunEvent[]) => rawTestRunEvent.map(event => TestRunEvent(event)), /* async onCacheEntryAdded(arg, {cacheDataLoaded, cacheEntryRemoved, updateCachedData}) { const listener: IListenerFunction = data => { updateCachedData(() => TestRun(data.event)); diff --git a/web/src/services/TestRun.service.ts b/web/src/services/TestRun.service.ts index 4c2cccec62..0e5ab5fa92 100644 --- a/web/src/services/TestRun.service.ts +++ b/web/src/services/TestRun.service.ts @@ -1,9 +1,22 @@ -import TestRunEvent, {TestRunStage} from 'models/TestRunEvent.model'; +import {TestRunStage, TraceEventType} from 'constants/TestRunEvents.constants'; +import {filter, findLastIndex} from 'lodash'; +import TestRunEvent from 'models/TestRunEvent.model'; const TestRunService = () => ({ getTestRunEventsByStage(events: TestRunEvent[], stage: TestRunStage) { return events.filter(event => event.stage === stage); }, + + getTestRunEventsWithLastPolling(events: TestRunEvent[]) { + const lastPollingIndex = findLastIndex(events, event => event.type === TraceEventType.POLLING_ITERATION_INFO); + if (lastPollingIndex === -1) return events; + + const eventsWithoutPolling = filter(events, event => event.type !== TraceEventType.POLLING_ITERATION_INFO); + const newIndex = lastPollingIndex - (events.length - eventsWithoutPolling.length) + 1; + eventsWithoutPolling.splice(newIndex, 0, events[lastPollingIndex]); + + return eventsWithoutPolling; + }, }); export default TestRunService();