Skip to content

Commit

Permalink
fix(frontend): fix analyzer results in trace DAG (#2674)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgeepc committed Jun 7, 2023
1 parent 4f94356 commit a25c379
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 146 deletions.
3 changes: 3 additions & 0 deletions web/cypress/e2e/TestRunDetail/Outputs.spec.ts
Expand Up @@ -7,6 +7,7 @@ describe('Outputs', () => {
// Open output flow from the Trace view (attributes)
cy.selectRunDetailMode(2);
cy.get('[data-cy=trace-node-database]', {timeout: 25000}).first().click({force: true});
cy.get('[data-cy=toggle-drawer]', {timeout: 25000}).click({force: true});
cy.get('[data-cy=attributes-search-container] input').type('db.name');
cy.get('[data-cy=attribute-row-db-name] .ant-dropdown-trigger').click();
cy.contains('Create output').click();
Expand Down Expand Up @@ -45,6 +46,7 @@ describe('Outputs', () => {
// Open output flow from the Trace view (attributes)
cy.selectRunDetailMode(2);
cy.get('[data-cy=trace-node-database]', {timeout: 25000}).first().click({force: true});
cy.get('[data-cy=toggle-drawer]', {timeout: 25000}).click({force: true});
cy.get('[data-cy=attributes-search-container] input').type('db.name');
cy.get('[data-cy=attribute-row-db-name] .ant-dropdown-trigger').click();
cy.contains('Create output').click();
Expand Down Expand Up @@ -87,6 +89,7 @@ describe('Outputs', () => {
// Open output flow from the Trace view (attributes)
cy.selectRunDetailMode(2);
cy.get('[data-cy=trace-node-database]', {timeout: 25000}).first().click({force: true});
cy.get('[data-cy=toggle-drawer]', {timeout: 25000}).click({force: true});
cy.get('[data-cy=attributes-search-container] input').type('db.name');
cy.get('[data-cy=attribute-row-db-name] .ant-dropdown-trigger').click();
cy.contains('Create output').click();
Expand Down
2 changes: 2 additions & 0 deletions web/cypress/e2e/TestRunDetail/TestRunDetail.spec.ts
Expand Up @@ -7,6 +7,7 @@ describe('Test Run Detail Views', () => {
it('Trace view -> show the attribute list for a specific span', () => {
cy.selectRunDetailMode(2);
cy.get('[data-cy=trace-node-http]').click();
cy.get('[data-cy=toggle-drawer]').click();

cy.get('[data-cy=attribute-list]').should('be.visible');
cy.get('[data-cy=attribute-row-http-method]').should('be.visible');
Expand All @@ -15,6 +16,7 @@ describe('Test Run Detail Views', () => {
it('Trace view -> attribute list', () => {
cy.selectRunDetailMode(2);
cy.get('[data-cy=trace-node-http]').click();
cy.get('[data-cy=toggle-drawer]').click();

cy.get('[data-cy=attribute-list]').should('be.visible');
});
Expand Down
1 change: 1 addition & 0 deletions web/src/components/Drawer/Drawer.tsx
Expand Up @@ -66,6 +66,7 @@ const Drawer = ({leftPanel, rightPanel}: IProps) => {
<ReflexSplitter>
<S.ButtonContainer>
<Button
data-cy="toggle-drawer"
data-tour={StepsID.SpanDetails}
icon={isOpen ? <DoubleLeftOutlined /> : <DoubleRightOutlined />}
onClick={event => {
Expand Down
2 changes: 2 additions & 0 deletions web/src/components/RunDetailTrace/RunDetailTrace.styled.ts
Expand Up @@ -9,6 +9,8 @@ export const Container = styled.div`

export const SearchContainer = styled.div`
padding: 24px 24px 0;
position: relative;
z-index: 9;
`;

export const Section = styled.div`
Expand Down
8 changes: 2 additions & 6 deletions web/src/components/RunDetailTrace/Visualization.tsx
Expand Up @@ -11,7 +11,6 @@ import TraceDiagramAnalyticsService from 'services/Analytics/TraceDiagramAnalyti
import TestRunService from 'services/TestRun.service';
import {TTestRunState} from 'types/TestRun.types';
import Span from 'models/Span.model';
import {useDrawer} from '../Drawer/Drawer';
import DAG from '../Visualization/components/DAG';
import Timeline from '../Visualization/components/Timeline';
import {VisualizationType} from './RunDetailTrace';
Expand All @@ -30,7 +29,6 @@ const Visualization = ({runEvents, runState, spans, type}: IProps) => {
const nodes = useAppSelector(TraceSelectors.selectNodes);
const selectedSpan = useAppSelector(TraceSelectors.selectSelectedSpan);
const isMatchedMode = Boolean(matchedSpans.length);
const {openDrawer} = useDrawer();

useEffect(() => {
dispatch(initNodes({spans}));
Expand All @@ -48,18 +46,16 @@ const Visualization = ({runEvents, runState, spans, type}: IProps) => {
(event, {id}: Node) => {
TraceDiagramAnalyticsService.onClickSpan(id);
dispatch(selectSpan({spanId: id}));
openDrawer();
},
[dispatch, openDrawer]
[dispatch]
);

const onNodeClickTimeline = useCallback(
(spanId: string) => {
TraceAnalyticsService.onTimelineSpanClick(spanId);
dispatch(selectSpan({spanId}));
openDrawer();
},
[dispatch, openDrawer]
[dispatch]
);

const onNavigateToSpan = useCallback(
Expand Down
@@ -0,0 +1,62 @@
import {ExclamationCircleFilled} from '@ant-design/icons';
import {Typography} from 'antd';
import styled, {css} from 'styled-components';

export const Body = styled.div`
margin-top: 5px;
position: relative;
`;

export const Connector = styled.div`
background-color: ${({theme}) => theme.color.error};
height: 2px;
left: -26px;
position: absolute;
top: 49px;
width: 26px;
z-index: 9;
`;

export const Container = styled.div`
background-color: ${({theme}) => theme.color.white};
border: ${({theme}) => `2px solid ${theme.color.error}`};
border-radius: 10px;
font-size: ${({theme}) => theme.size.xs};
height: 100px;
padding: 10px;
position: absolute;
right: -210px;
top: -32px;
width: 200px;
`;

export const Content = styled.div`
height: 100%;
overflow-y: scroll;
::-webkit-scrollbar {
display: none;
}
`;

export const ErrorIcon = styled(ExclamationCircleFilled)<{$isAbsolute?: boolean}>`
color: ${({theme}) => theme.color.error};
${({$isAbsolute}) =>
$isAbsolute &&
css`
position: absolute;
right: 10px;
top: 5px;
`}
`;

export const Text = styled(Typography.Text)`
color: inherit;
`;

export const Title = styled(Typography.Title)`
&& {
margin-bottom: 0;
}
`;
@@ -0,0 +1,44 @@
import {Space} from 'antd';
import {useRef} from 'react';

import useOnClickOutside from 'hooks/useOnClickOutside';
import {TLintBySpanContent} from 'services/Span.service';
import * as S from './AnalyzerResults.styled';

interface IProps {
lintErrors: TLintBySpanContent[];
onClose(): void;
}

const AnalyzerResults = ({lintErrors, onClose}: IProps) => {
const ref = useRef(null);
useOnClickOutside(ref, onClose);

return (
<S.Container className="nowheel nodrag" ref={ref}>
<S.Connector />
<S.Content>
<Space>
<S.ErrorIcon />
<S.Title level={4}>Analyzer errors</S.Title>
</Space>
<S.Body>
{lintErrors.map(lintError => (
<div key={lintError.ruleName}>
<S.Text strong>{lintError.ruleName}</S.Text>

{lintError.errors.map((error, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index}>
<S.Text type="secondary">- {error}</S.Text>
</div>
))}
</div>
))}
</S.Body>
</S.Content>
</S.Container>
);
};

export default AnalyzerResults;
49 changes: 0 additions & 49 deletions web/src/components/Visualization/components/DAG/SpanNode.styled.ts
@@ -1,4 +1,3 @@
import {CloseOutlined, ExclamationCircleFilled} from '@ant-design/icons';
import {Badge, Typography} from 'antd';
import styled, {css} from 'styled-components';

Expand Down Expand Up @@ -116,54 +115,6 @@ export const ItemText = styled(Typography.Text)`
margin-left: 5px;
`;

export const LintBody = styled.div`
margin-top: 5px;
`;

export const LintContainer = styled.div`
background-color: ${({theme}) => theme.color.white};
border: ${({theme}) => `2px solid ${theme.color.error}`};
border-radius: 10px;
font-size: ${({theme}) => theme.size.xs};
height: 100px;
overflow-y: scroll;
padding: 10px;
position: absolute;
right: -210px;
top: -50px;
width: 200px;
`;

export const LintCloseIcon = styled(CloseOutlined)`
color: ${({theme}) => theme.color.border};
cursor: pointer;
position: absolute;
right: 10px;
top: 10px;
`;

export const LintErrorIcon = styled(ExclamationCircleFilled)<{$isAbsolute?: boolean}>`
color: ${({theme}) => theme.color.error};
${({$isAbsolute}) =>
$isAbsolute &&
css`
position: absolute;
right: 10px;
top: 5px;
`}
`;

export const LintText = styled(Typography.Text)`
color: inherit;
`;

export const LintTitle = styled(Typography.Title)`
&& {
margin-bottom: 0;
}
`;

export const TopLine = styled.div<{$type: SemanticGroupNames}>`
background-color: ${({$type}) => SemanticGroupNamesToColor[$type]};
height: 7px;
Expand Down
55 changes: 22 additions & 33 deletions web/src/components/Visualization/components/DAG/SpanNode.tsx
@@ -1,27 +1,31 @@
import {ClockCircleOutlined, SettingOutlined, ToolOutlined} from '@ant-design/icons';
import {Space} from 'antd';
import {useMemo, useState} from 'react';
import {useMemo} from 'react';
import {Handle, NodeProps, Position} from 'react-flow-renderer';

import {SemanticGroupNamesToText} from 'constants/SemanticGroupNames.constants';
import {SpanKindToText} from 'constants/Span.constants';
import {useAppSelector} from 'redux/hooks';
import SpanService from 'services/Span.service';
import TestSpecsSelectors from 'selectors/TestSpecs.selectors';
import {INodeDataSpan} from 'types/DAG.types';
import AssertionResultChecks from 'components/AssertionResultChecks/AssertionResultChecks';
import {selectOutputsBySpanId} from 'redux/testOutputs/selectors';
import CurrentSpanSelector from 'components/CurrentSpanSelector';
import TestOutputMark from 'components/TestOutputMark';
import {useTestSpecForm} from 'components/TestSpecForm/TestSpecForm.provider';
import CurrentSpanSelector from 'components/CurrentSpanSelector';
import {SemanticGroupNamesToText} from 'constants/SemanticGroupNames.constants';
import {SpanKindToText} from 'constants/Span.constants';
import {useSpan} from 'providers/Span/Span.provider';
import {useTestOutput} from 'providers/TestOutput/TestOutput.provider';
import {useTestRun} from 'providers/TestRun/TestRun.provider';
import {useAppDispatch, useAppSelector} from 'redux/hooks';
import {selectAnalyzerResults} from 'redux/slices/Trace.slice';
import {selectOutputsBySpanId} from 'redux/testOutputs/selectors';
import TestSpecsSelectors from 'selectors/TestSpecs.selectors';
import TraceSelectors from 'selectors/Trace.selectors';
import SpanService from 'services/Span.service';
import {INodeDataSpan} from 'types/DAG.types';
import AnalyzerResults from './AnalyzerResults';
import * as SA from './AnalyzerResults.styled';
import * as S from './SpanNode.styled';

interface IProps extends NodeProps<INodeDataSpan> {}

const SpanNode = ({data, id, selected}: IProps) => {
const dispatch = useAppDispatch();
const assertions = useAppSelector(state => TestSpecsSelectors.selectAssertionResultsBySpan(state, data?.id || ''));
const outputs = useAppSelector(state => selectOutputsBySpanId(state, data?.id || ''));
const {failed, passed} = useMemo(() => SpanService.getAssertionResultSummary(assertions), [assertions]);
Expand All @@ -36,7 +40,11 @@ const SpanNode = ({data, id, selected}: IProps) => {
const showSelectAsCurrent =
!data.isMatched && !!matchedSpans.length && (isTestSpecFormOpen || isTestOutputFormOpen) && selected;
const className = `${data.isMatched ? 'matched' : ''} ${showSelectAsCurrent ? 'selectedAsCurrent' : ''}`;
const [isOpenLintErrors, setIsOpenLintErrors] = useState(false);
const selectedAnalyzerResults = useAppSelector(TraceSelectors.selectSelectedAnalyzerResults);

const handleSelectAnalyzerResults = (spanId: string = '') => {
dispatch(selectAnalyzerResults({spanId}));
};

return (
<>
Expand All @@ -50,35 +58,16 @@ const SpanNode = ({data, id, selected}: IProps) => {

<S.TopLine $type={data.type} />

{isOpenLintErrors && (
<S.LintContainer className="nowheel nodrag">
<S.LintCloseIcon onClick={() => setIsOpenLintErrors(false)} />
<Space>
<S.LintErrorIcon />
<S.LintTitle level={4}>Analyzer errors</S.LintTitle>
</Space>
<S.LintBody>
{lintErrors.map(lintError => (
<div>
<S.LintText strong>{lintError.ruleName}</S.LintText>

{lintError.errors.map(error => (
<div>
<S.LintText type="secondary">- {error}</S.LintText>
</div>
))}
</div>
))}
</S.LintBody>
</S.LintContainer>
{selectedAnalyzerResults === data.id && (
<AnalyzerResults lintErrors={lintErrors} onClose={() => handleSelectAnalyzerResults()} />
)}

<S.Header>
<S.BadgeContainer>
<S.BadgeType count={SemanticGroupNamesToText[data.type]} $hasMargin $type={data.type} />
</S.BadgeContainer>
<S.HeaderText>{data.name}</S.HeaderText>
{!!lintErrors.length && <S.LintErrorIcon $isAbsolute onClick={() => setIsOpenLintErrors(prev => !prev)} />}
{!!lintErrors.length && <SA.ErrorIcon $isAbsolute onClick={() => handleSelectAnalyzerResults(data.id)} />}
</S.Header>

<S.Body>
Expand Down
8 changes: 0 additions & 8 deletions web/src/hooks/__tests__/useElementSize.test.tsx

This file was deleted.

0 comments on commit a25c379

Please sign in to comment.