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

fix(frontend): fix analyzer results in trace DAG #2674

Merged
merged 3 commits into from
Jun 7, 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
3 changes: 3 additions & 0 deletions web/cypress/e2e/TestRunDetail/Outputs.spec.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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;
}
`;
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to consider cleaning up nodes to not have to be connected to all of this state and such, maybe something to think latter on

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree with that. I have been thinking about that for a while. We need to parse everything (span related) and inject it in the data object.

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.