Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(frontend): add error handling for trace section #2282

Merged
merged 1 commit into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions web/src/components/RunDetailLayout/RunDetailLayout.tsx
Expand Up @@ -72,10 +72,10 @@ const RunDetailLayout = ({test: {id, name, trigger, version = 1}, test}: IProps)
<RunDetailTrigger test={test} run={run} runEvents={runEvents} isError={isError} />
</Tabs.TabPane>
<Tabs.TabPane tab={renderTab('Trace', id, run.id, mode)} key={RunDetailModes.TRACE}>
<RunDetailTrace run={run} testId={id} />
<RunDetailTrace run={run} runEvents={runEvents} testId={id} />
</Tabs.TabPane>
<Tabs.TabPane tab={renderTab('Test', id, run.id, mode)} key={RunDetailModes.TEST}>
<RunDetailTest run={run} testId={id} />
<RunDetailTest run={run} runEvents={runEvents} testId={id} />
</Tabs.TabPane>
</Tabs>
</S.Container>
Expand Down
15 changes: 11 additions & 4 deletions web/src/components/RunDetailTest/RunDetailTest.tsx
Expand Up @@ -13,15 +13,16 @@ 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';
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';

Expand All @@ -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} =
Expand Down Expand Up @@ -125,7 +127,12 @@ const RunDetailTest = ({run, testId}: IProps) => {
/>
</S.SwitchContainer>

<Visualization runState={run.state} spans={run?.trace?.spans ?? []} type={visualizationType} />
<Visualization
runEvents={runEvents}
runState={run.state}
spans={run?.trace?.spans ?? []}
type={visualizationType}
/>
</S.SectionLeft>

<S.SectionRight $shouldScroll={!selectedTestSpec}>
Expand Down
11 changes: 7 additions & 4 deletions web/src/components/RunDetailTest/Visualization.tsx
Expand Up @@ -2,27 +2,30 @@ 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';
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);
Expand Down Expand Up @@ -67,7 +70,7 @@ const Visualization = ({runState, spans, type}: IProps) => {
);

if (runState !== TestState.FINISHED) {
return <SkeletonDiagram />;
return <RunEvents events={runEvents} stage={TestRunStage.Trace} state={runState} />;
}

return type === VisualizationType.Dag ? (
Expand Down
13 changes: 10 additions & 3 deletions web/src/components/RunDetailTrace/RunDetailTrace.tsx
Expand Up @@ -5,17 +5,19 @@ 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';
import SetupAlert from '../SetupAlert';

interface IProps {
run: TestRun;
runEvents: TestRunEvent[];
testId: string;
}

Expand All @@ -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));
Expand Down Expand Up @@ -58,7 +60,12 @@ const RunDetailTrace = ({run, testId}: IProps) => {
/>
)}
</S.SwitchContainer>
<Visualization runState={run.state} spans={run?.trace?.spans ?? []} type={visualizationType} />
<Visualization
runEvents={runEvents}
runState={run.state}
spans={run?.trace?.spans ?? []}
type={visualizationType}
/>
</S.VisualizationContainer>
</S.Section>
}
Expand Down
9 changes: 6 additions & 3 deletions 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';
Expand All @@ -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);
Expand Down Expand Up @@ -67,7 +70,7 @@ const Visualization = ({runState, spans, type}: IProps) => {
);

if (runState !== TestState.FINISHED) {
return <SkeletonDiagram />;
return <RunEvents events={runEvents} stage={TestRunStage.Trace} state={runState} />;
}

return type === VisualizationType.Dag ? (
Expand Down
5 changes: 3 additions & 2 deletions web/src/components/RunDetailTrigger/RunDetailTrigger.tsx
Expand Up @@ -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 {
Expand All @@ -25,7 +26,7 @@ const RunDetailTrigger = ({test, run: {state, triggerResult, triggerTime}, runEv
</S.SectionLeft>
<S.SectionRight>
{shouldDisplayError ? (
<RunEvents events={runEvents} stage={TestRunStage.Trigger} />
<RunEvents events={runEvents} stage={TestRunStage.Trigger} state={state} />
) : (
<RunDetailTriggerResponseFactory
state={state}
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/RunEvents/RunEvent.tsx
Expand Up @@ -2,11 +2,11 @@ import {Typography} from 'antd';
import TestRunEvent from 'models/TestRunEvent.model';
import * as S from './RunEvents.styled';

interface IProps {
export interface IPropsEvent {
event: TestRunEvent;
}

const RunEvent = ({event}: IProps) => (
const RunEvent = ({event}: IPropsEvent) => (
<S.EventContainer>
<S.Dot $logLevel={event.logLevel} />
<Typography.Title level={3}>{event.title}</Typography.Title>
Expand Down
24 changes: 24 additions & 0 deletions 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) => (
<S.EventContainer>
<S.Dot $logLevel={event.logLevel} />
<Typography.Title level={3}>{event.title}</Typography.Title>
<Typography.Text>{event.description}</Typography.Text>
{!!event.dataStoreConnection && (
<S.Content>
<Typography.Title level={3}>
{event.dataStoreConnection?.allPassed
? 'Data store configuration is valid.'
: 'Data store configuration is not valid.'}
</Typography.Title>
<TestConnectionNotification result={event.dataStoreConnection} />
</S.Content>
)}
</S.EventContainer>
);

export default RunEventDataStore;
38 changes: 38 additions & 0 deletions 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) => (
<S.EventContainer>
<S.Dot $logLevel={event.logLevel} />
<Typography.Title level={3}>{event.title}</Typography.Title>
<Typography.Text>{event.description}</Typography.Text>
{!!event.polling && (
<S.Content>
<S.Column>
<S.InfoIcon />
<div>
<Typography.Title level={3}>Number of spans collected:</Typography.Title>
<Typography.Text>{event.polling.periodic.numberSpans}</Typography.Text>
</div>
</S.Column>
<S.Column>
<S.InfoIcon />
<div>
<Typography.Title level={3}>Iteration number:</Typography.Title>
<Typography.Text>{event.polling.periodic.numberIterations}</Typography.Text>
</div>
</S.Column>
<S.Column>
<S.InfoIcon />
<div>
<Typography.Title level={3}>Reason why the next iteration will be executed:</Typography.Title>
<Typography.Text>{event.polling.reasonNextIteration}</Typography.Text>
</div>
</S.Column>
</S.Content>
)}
</S.EventContainer>
);

export default RunEventPolling;
41 changes: 34 additions & 7 deletions 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;
Expand All @@ -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}>`
Expand All @@ -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;
`;
Expand All @@ -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;
`;
14 changes: 9 additions & 5 deletions 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 {
Expand All @@ -12,15 +16,15 @@ interface IProps extends IPropsComponent {

const ComponentMap: Record<TestRunStage, (props: IPropsComponent) => 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 <Component events={filteredEvents} />;
return <Component events={filteredEvents} state={state} />;
};

export default RunEvents;