Skip to content

Commit

Permalink
feature(frontend): adding test spec snippets (#2366)
Browse files Browse the repository at this point in the history
* feature(frontend): adding test spec snippets

* feature(frontend): adding test spec snippets

* feature(frontend): updating based on feedback
  • Loading branch information
xoscar committed Apr 11, 2023
1 parent b4f9814 commit e0f422b
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 31 deletions.
92 changes: 92 additions & 0 deletions web/src/components/TestResults/AddTestSpecButton.tsx
@@ -0,0 +1,92 @@
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {CaretDownOutlined} from '@ant-design/icons';
import {Dropdown, Menu} from 'antd';
import SpanService from 'services/Span.service';
import Span from 'models/Span.model';
import {TEST_SPEC_SNIPPETS, TSnippet} from 'constants/TestSpecs.constants';
import * as S from './TestResults.styled';
import {useTestSpecForm} from '../TestSpecForm/TestSpecForm.provider';

interface IProps {
selectedSpan: Span;
visibleByDefault?: boolean;
}

const AddTestSpecButton = ({selectedSpan, visibleByDefault = false}: IProps) => {
const {open} = useTestSpecForm();
const caretRef = useRef<HTMLElement>(null);
const handleEmptyTestSpec = useCallback(() => {
const selector = SpanService.getSelectorInformation(selectedSpan);

open({
isEditing: false,
selector,
defaultValues: {
selector,
},
});
}, [open, selectedSpan]);

const onSnippetClick = useCallback(
(snippet: TSnippet) => {
open({
isEditing: false,
selector: snippet.selector,
defaultValues: snippet,
});
},
[open]
);

useEffect(() => {
if (visibleByDefault && caretRef.current) {
caretRef.current?.click();
}
}, [visibleByDefault]);

const menu = useMemo(
() => (
<Menu
items={[
{
label: 'Try these snippets for quick testing:',
key: 'test',
type: 'group',
children: TEST_SPEC_SNIPPETS.map(snippet => ({
label: snippet.name,
key: snippet.name,
onClick: () => onSnippetClick(snippet),
})),
},
{type: 'divider'},
{
label: 'Empty Test Spec',
key: 'empty-test-spec',
onClick: handleEmptyTestSpec,
},
]}
/>
),
[handleEmptyTestSpec, onSnippetClick]
);

return (
<Dropdown.Button
overlay={menu}
trigger={['click']}
placement="bottomRight"
onClick={handleEmptyTestSpec}
type="primary"
buttonsRender={([leftButton]) => [
React.cloneElement(leftButton as React.ReactElement<any, string>, {'data-cy': 'add-test-spec-button'}),
<S.CaretDropdownButton ref={caretRef} type="primary" data-cy="create-button">
<CaretDownOutlined />
</S.CaretDropdownButton>,
]}
>
Add Test Spec
</Dropdown.Button>
);
};

export default AddTestSpecButton;
21 changes: 3 additions & 18 deletions web/src/components/TestResults/Header.tsx
@@ -1,9 +1,7 @@
import {StepsID} from 'components/GuidedTour/testRunSteps';
import {useTestSpecForm} from 'components/TestSpecForm/TestSpecForm.provider';
import SpanService from 'services/Span.service';
import {singularOrPlural} from 'utils/Common';
import Span from 'models/Span.model';
import * as S from './TestResults.styled';
import AddTestSpecButton from './AddTestSpecButton';

interface IProps {
selectedSpan: Span;
Expand All @@ -12,18 +10,7 @@ interface IProps {
}

const Header = ({selectedSpan, totalFailedSpecs, totalPassedSpecs}: IProps) => {
const {open} = useTestSpecForm();

const handleAddTestSpecOnClick = () => {
const selector = SpanService.getSelectorInformation(selectedSpan!);
open({
isEditing: false,
selector,
defaultValues: {
selector,
},
});
};
const hasSpecs = !!(totalFailedSpecs || totalPassedSpecs);

return (
<S.HeaderContainer>
Expand All @@ -45,9 +32,7 @@ const Header = ({selectedSpan, totalFailedSpecs, totalPassedSpecs}: IProps) => {
</div>
</S.Row>

<S.PrimaryButton data-tour={StepsID.TestSpecs} data-cy="add-test-spec-button" onClick={handleAddTestSpecOnClick}>
Add Test Spec
</S.PrimaryButton>
<AddTestSpecButton selectedSpan={selectedSpan} visibleByDefault={!hasSpecs} />
</S.HeaderContainer>
);
};
Expand Down
11 changes: 7 additions & 4 deletions web/src/components/TestResults/TestResults.styled.ts
Expand Up @@ -40,12 +40,15 @@ export const LoadingContainer = styled.div`
text-align: center;
`;

export const PrimaryButton = styled(Button).attrs({
type: 'primary',
})``;

export const Row = styled.div`
align-items: center;
display: flex;
gap: 8px;
`;

export const CaretDropdownButton = styled(Button)`
font-weight: 600;
opacity: 0.7;
width: 32px;
padding: 0px;
`;
23 changes: 14 additions & 9 deletions web/src/components/TestResults/TestResults.tsx
Expand Up @@ -36,22 +36,27 @@ const TestResults = ({onDelete, onEdit, onRevert}: IProps) => {

return (
<S.Container>
<Header selectedSpan={selectedSpan!} totalFailedSpecs={totalFailedSpecs} totalPassedSpecs={totalPassedSpecs} />

{isLoading && (
<S.LoadingContainer>
<LoadingSpinner />
</S.LoadingContainer>
)}

{!isLoading && (
<TestSpecs
assertionResults={assertionResults}
onDelete={onDelete}
onEdit={onEdit}
onOpen={handleOpen}
onRevert={onRevert}
/>
<>
<Header
selectedSpan={selectedSpan!}
totalFailedSpecs={totalFailedSpecs}
totalPassedSpecs={totalPassedSpecs}
/>
<TestSpecs
assertionResults={assertionResults}
onDelete={onDelete}
onEdit={onEdit}
onOpen={handleOpen}
onRevert={onRevert}
/>
</>
)}
</S.Container>
);
Expand Down
84 changes: 84 additions & 0 deletions web/src/constants/TestSpecs.constants.ts
@@ -0,0 +1,84 @@
import {IValues} from 'components/TestSpecForm/TestSpecForm';

export type TSnippet = Required<IValues>;

export const HTTP_SPANS_STATUS_CODE: TSnippet = {
name: 'All HTTP Spans: Status code is 200',
selector: 'span[tracetest.span.type="http"]',
assertions: [
{
left: 'attr:http.status_code',
comparator: '=',
right: '200',
},
],
};

export const TRIGGER_SPAN_RESPONSE_TIME: TSnippet = {
name: 'Trigger Span: Response time is less than 200ms',
selector: 'span[tracetest.span.type="general" name="Tracetest trigger"]',
assertions: [
{
left: 'attr:tracetest.span.duration',
comparator: '<',
right: '200ms',
},
],
};

export const DB_SPANS_RESPONSE_TIME: TSnippet = {
name: 'All Database Spans: Processing time is less than 100ms',
selector: 'span[tracetest.span.type="database"]',
assertions: [
{
left: 'attr:tracetest.span.duration',
comparator: '<',
right: '100ms',
},
],
};

export const TRIGGER_SPAN_RESPONSE_BODY_CONTAINS: TSnippet = {
name: 'Trigger Span: Response body contains "this string"',
selector: 'span[tracetest.span.type="general" name="Tracetest trigger"]',
assertions: [
{
left: 'attr:tracetest.response.body',
comparator: 'contains',
right: '"this string"',
},
],
};

export const GRPC_SPANS_STATUS_CODE: TSnippet = {
name: 'All gRPC Spans: Status is Ok',
selector: 'span[tracetest.span.type="rpc" rpc.system="grpc"]',
assertions: [
{
left: 'attr:grpc.status_code',
comparator: '=',
right: '0',
},
],
};

export const DB_SPANS_QUALITY_DB_STATEMENT_PRESENT: TSnippet = {
name: 'All Database Spans: db.statement should always be defined (QUALITY)',
selector: 'span[tracetest.span.type="database"]',
assertions: [
{
left: 'attr:db.system',
comparator: '!=',
right: '""',
},
],
};

export const TEST_SPEC_SNIPPETS: TSnippet[] = [
HTTP_SPANS_STATUS_CODE,
GRPC_SPANS_STATUS_CODE,
TRIGGER_SPAN_RESPONSE_TIME,
TRIGGER_SPAN_RESPONSE_BODY_CONTAINS,
DB_SPANS_RESPONSE_TIME,
DB_SPANS_QUALITY_DB_STATEMENT_PRESENT,
];

0 comments on commit e0f422b

Please sign in to comment.