Skip to content

Commit

Permalink
feat(frontend): stop a test run (#2295)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgeepc authored Apr 13, 2023
1 parent ba47e42 commit 1cd98ab
Show file tree
Hide file tree
Showing 19 changed files with 190 additions and 82 deletions.
44 changes: 22 additions & 22 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -391,25 +391,25 @@ jobs:
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}

# cleanup:
# name: Cleanup test infra
# runs-on: ubuntu-latest
# needs: [trace-testing, e2e]
# if: always()
# steps:
# - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7
# with:
# service_account_key: ${{ secrets.GKE_SA_KEY }}
# project_id: ${{ secrets.GKE_PROJECT }}

# - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e
# with:
# cluster_name: ${{ secrets.GKE_CLUSTER }}
# location: ${{ secrets.GKE_ZONE }}
# credentials: ${{ secrets.GKE_SA_KEY }}

# - name: Uninstall tracetest
# run: |
# helm delete tracetest-pr-${{ github.event.pull_request.number }} \
# --namespace tracetest-pr-${{ github.event.pull_request.number }}
# kubectl delete ns tracetest-pr-${{ github.event.pull_request.number }}
cleanup:
name: Cleanup test infra
runs-on: ubuntu-latest
needs: [trace-testing, e2e]
if: always()
steps:
- uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7
with:
service_account_key: ${{ secrets.GKE_SA_KEY }}
project_id: ${{ secrets.GKE_PROJECT }}

- uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e
with:
cluster_name: ${{ secrets.GKE_CLUSTER }}
location: ${{ secrets.GKE_ZONE }}
credentials: ${{ secrets.GKE_SA_KEY }}

- name: Uninstall tracetest
run: |
helm delete tracetest-pr-${{ github.event.pull_request.number }} \
--namespace tracetest-pr-${{ github.event.pull_request.number }}
kubectl delete ns tracetest-pr-${{ github.event.pull_request.number }}
6 changes: 5 additions & 1 deletion web/src/components/RunCard/RunCard.styled.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {CheckCircleFilled, MinusCircleFilled, MoreOutlined} from '@ant-design/icons';
import {CheckCircleFilled, InfoCircleFilled, MinusCircleFilled, MoreOutlined} from '@ant-design/icons';
import {Typography} from 'antd';
import styled from 'styled-components';

Expand Down Expand Up @@ -49,6 +49,10 @@ export const IconFail = styled(MinusCircleFilled)`
color: ${({theme}) => theme.color.error};
`;

export const IconInfo = styled(InfoCircleFilled)`
color: ${({theme}) => theme.color.textLight};
`;

export const Info = styled.div`
flex: 1;
`;
Expand Down
5 changes: 4 additions & 1 deletion web/src/components/RunCard/TestRunCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Tooltip} from 'antd';
import {Link} from 'react-router-dom';
import RunActionsMenu from 'components/RunActionsMenu';
import TestState from 'components/TestState';
import TestRun, {isRunStateFailed, isRunStateFinished} from 'models/TestRun.model';
import TestRun, {isRunStateFailed, isRunStateFinished, isRunStateStopped} from 'models/TestRun.model';
import Date from 'utils/Date';
import * as S from './RunCard.styled';

Expand All @@ -16,6 +16,9 @@ function getIcon(state: TestRun['state'], failedAssertions: number) {
if (!isRunStateFinished(state)) {
return null;
}
if (isRunStateStopped(state)) {
return <S.IconInfo />;
}
if (isRunStateFailed(state) || failedAssertions > 0) {
return <S.IconFail />;
}
Expand Down
18 changes: 16 additions & 2 deletions web/src/components/RunDetailLayout/HeaderRight.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Button} from 'antd';
import {CloseCircleOutlined} from '@ant-design/icons';
import {Button, Tooltip} from 'antd';
import RunActionsMenu from 'components/RunActionsMenu';
import TestActions from 'components/TestActions';
import TestState from 'components/TestState';
Expand All @@ -19,7 +20,7 @@ const HeaderRight = ({testId, testVersion}: IProps) => {
const {isDraftMode: isTestSpecsDraftMode} = useTestSpecs();
const {isDraftMode: isTestOutputsDraftMode} = useTestOutput();
const isDraftMode = isTestSpecsDraftMode || isTestOutputsDraftMode;
const {run} = useTestRun();
const {isLoadingStop, run, stopRun} = useTestRun();
const {onRun} = useTest();
const state = run.state;

Expand All @@ -30,6 +31,19 @@ const HeaderRight = ({testId, testVersion}: IProps) => {
<S.StateContainer data-cy="test-run-result-status">
<S.StateText>Test status:</S.StateText>
<TestState testState={state} />
{state === TestStateEnum.AWAITING_TRACE && (
<S.StopContainer>
<Tooltip title="Stop test execution">
<Button
disabled={isLoadingStop}
icon={<CloseCircleOutlined />}
onClick={stopRun}
shape="circle"
type="link"
/>
</Tooltip>
</S.StopContainer>
)}
</S.StateContainer>
)}
{!isDraftMode && state && isRunStateFinished(state) && (
Expand Down
4 changes: 4 additions & 0 deletions web/src/components/RunDetailLayout/RunDetailLayout.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ export const StateContainer = styled.div`
justify-self: flex-end;
`;

export const StopContainer = styled.div`
margin-left: 12px;
`;

export const StateText = styled(Typography.Text)`
&& {
margin-right: 8px;
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/RunDetailTest/Visualization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ 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';
Expand All @@ -16,6 +15,7 @@ import {initNodes, onNodesChange as onNodesChangeAction} from 'redux/slices/DAG.
import DAGSelectors from 'selectors/DAG.selectors';
import TraceAnalyticsService from 'services/Analytics/TestRunAnalytics.service';
import TraceDiagramAnalyticsService from 'services/Analytics/TraceDiagramAnalytics.service';
import TestRunService from 'services/TestRun.service';
import {TTestRunState} from 'types/TestRun.types';

export interface IProps {
Expand Down Expand Up @@ -69,7 +69,7 @@ const Visualization = ({runEvents, runState, spans, type}: IProps) => {
[onSelectSpan, onSetFocusedSpan]
);

if (runState !== TestState.FINISHED) {
if (TestRunService.shouldDisplayTraceEvents(runState, spans.length)) {
return <RunEvents events={runEvents} stage={TestRunStage.Trace} state={runState} />;
}

Expand Down
4 changes: 2 additions & 2 deletions web/src/components/RunDetailTrace/Visualization.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
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';
Expand All @@ -9,6 +8,7 @@ import {changeNodes, initNodes, selectSpan} from 'redux/slices/Trace.slice';
import TraceSelectors from 'selectors/Trace.selectors';
import TraceAnalyticsService from 'services/Analytics/TestRunAnalytics.service';
import TraceDiagramAnalyticsService from 'services/Analytics/TraceDiagramAnalytics.service';
import TestRunService from 'services/TestRun.service';
import {TTestRunState} from 'types/TestRun.types';
import Span from 'models/Span.model';
import {useDrawer} from '../Drawer/Drawer';
Expand Down Expand Up @@ -69,7 +69,7 @@ const Visualization = ({runEvents, runState, spans, type}: IProps) => {
[dispatch]
);

if (runState !== TestState.FINISHED) {
if (TestRunService.shouldDisplayTraceEvents(runState, spans.length)) {
return <RunEvents events={runEvents} stage={TestRunStage.Trace} state={runState} />;
}

Expand Down
10 changes: 9 additions & 1 deletion web/src/components/RunEvents/RunEvents.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,17 @@ export const EventContainer = styled.div`
position: relative;
`;

export const InfoIcon = styled(InfoCircleFilled)`
export const InfoIcon = styled(InfoCircleFilled)<{$isLarge?: boolean}>`
color: ${({theme}) => theme.color.textLight};
margin-top: 3px;
${({$isLarge}) =>
$isLarge &&
css`
font-size: 32px;
margin-bottom: 26px;
margin-top: 0;
`}
`;

export const Link = styled(Typography.Link)`
Expand Down
60 changes: 14 additions & 46 deletions web/src/components/RunEvents/RunEventsTrace.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
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 {isRunStateFailed} from 'models/TestRun.model';
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';
import FailedTraceHeader from './TraceHeader/FailedTraceHeader';
import FailedTriggerHeader from './TraceHeader/FailedTriggerHeader';
import LoadingHeader from './TraceHeader/LoadingHeader';
import StoppedHeader from './TraceHeader/StoppedHeader';

type TestStateType = TestState.TRIGGER_FAILED | TestState.TRACE_FAILED | TestState.STOPPED;

const HeaderComponentMap: Record<TestStateType, () => React.ReactElement> = {
[TestState.TRIGGER_FAILED]: FailedTriggerHeader,
[TestState.TRACE_FAILED]: FailedTraceHeader,
[TestState.STOPPED]: StoppedHeader,
};

type TraceEventTypeWithoutFetching = Exclude<
TraceEventType,
Expand All @@ -23,51 +31,11 @@ const ComponentMap: Record<TraceEventTypeWithoutFetching, (props: IPropsEvent) =

const RunEventsTrace = ({events, state}: IPropsComponent) => {
const filteredEvents = TestRunService.getTestRunTraceEvents(events);

const loadingHeader = (
<>
<S.LoadingIcon />
<Typography.Title level={3} type="secondary">
We are working to gather the trace associated with this test run
</Typography.Title>
<S.Paragraph type="secondary">
Want to know more about traces? Head to the official{' '}
<S.Link href={TRACE_DOCUMENTATION_URL} target="_blank">
Open Telemetry Documentation
</S.Link>
</S.Paragraph>
</>
);

const failedTriggerHeader = (
<>
<S.ErrorIcon />
<Typography.Title level={2} type="secondary">
Test Trigger Failed
</Typography.Title>
<S.Paragraph type="secondary">
The test failed in the Trigger stage, review the Trigger tab to see the breakdown of diagnostic steps.
</S.Paragraph>
</>
);

const failedTraceHeader = (
<>
<S.ErrorIcon />
<Typography.Title level={2} type="secondary">
Trace Fetch Failed
</Typography.Title>
<S.Paragraph type="secondary">
We prepared the breakdown of diagnostic steps and tips to help identify the source of the issue:
</S.Paragraph>
</>
);
const HeaderComponent = HeaderComponentMap[state as TestStateType] ?? LoadingHeader;

return (
<S.Container $hasScroll>
{state === TestState.TRIGGER_FAILED && failedTriggerHeader}
{state === TestState.TRACE_FAILED && failedTraceHeader}
{!isRunStateFailed(state) && loadingHeader}
<HeaderComponent />

<S.ListContainer>
{filteredEvents.map(event => {
Expand Down
16 changes: 16 additions & 0 deletions web/src/components/RunEvents/TraceHeader/FailedTraceHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Typography} from 'antd';
import * as S from '../RunEvents.styled';

const FailedTraceHeader = () => (
<>
<S.ErrorIcon />
<Typography.Title level={2} type="secondary">
Trace Fetch Failed
</Typography.Title>
<S.Paragraph type="secondary">
We prepared the breakdown of diagnostic steps and tips to help identify the source of the issue:
</S.Paragraph>
</>
);

export default FailedTraceHeader;
16 changes: 16 additions & 0 deletions web/src/components/RunEvents/TraceHeader/FailedTriggerHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Typography} from 'antd';
import * as S from '../RunEvents.styled';

const FailedTriggerHeader = () => (
<>
<S.ErrorIcon />
<Typography.Title level={2} type="secondary">
Test Trigger Failed
</Typography.Title>
<S.Paragraph type="secondary">
The test failed in the Trigger stage, review the Trigger tab to see the breakdown of diagnostic steps.
</S.Paragraph>
</>
);

export default FailedTriggerHeader;
20 changes: 20 additions & 0 deletions web/src/components/RunEvents/TraceHeader/LoadingHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {Typography} from 'antd';
import {TRACE_DOCUMENTATION_URL} from 'constants/Common.constants';
import * as S from '../RunEvents.styled';

const LoadingHeader = () => (
<>
<S.LoadingIcon />
<Typography.Title level={3} type="secondary">
We are working to gather the trace associated with this test run
</Typography.Title>
<S.Paragraph type="secondary">
Want to know more about traces? Head to the official{' '}
<S.Link href={TRACE_DOCUMENTATION_URL} target="_blank">
Open Telemetry Documentation
</S.Link>
</S.Paragraph>
</>
);

export default LoadingHeader;
14 changes: 14 additions & 0 deletions web/src/components/RunEvents/TraceHeader/StoppedHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {Typography} from 'antd';
import * as S from '../RunEvents.styled';

const StoppedHeader = () => (
<>
<S.InfoIcon $isLarge />
<Typography.Title level={2} type="secondary">
Test Run Stopped
</Typography.Title>
<S.Paragraph type="secondary">The test run was stopped by the user and no trace was detected.</S.Paragraph>
</>
);

export default StoppedHeader;
2 changes: 1 addition & 1 deletion web/src/constants/TestRun.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const TestStateMap: Record<
label: 'Failed',
},
[TestState.STOPPED]: {
status: 'error',
status: 'default',
label: 'Stopped by user',
},
[TestState.TRIGGER_FAILED]: {
Expand Down
16 changes: 15 additions & 1 deletion web/src/models/TestRun.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,28 @@ const getTestResultCount = (

export function isRunStateFinished(state: TTestRunState) {
return (
[TestState.FINISHED, TestState.TRIGGER_FAILED, TestState.TRACE_FAILED, TestState.ASSERTION_FAILED] as string[]
[
TestState.FINISHED,
TestState.STOPPED,
TestState.TRIGGER_FAILED,
TestState.TRACE_FAILED,
TestState.ASSERTION_FAILED,
] as string[]
).includes(state);
}

export function isRunStateFailed(state: TTestRunState) {
return ([TestState.TRIGGER_FAILED, TestState.TRACE_FAILED, TestState.ASSERTION_FAILED] as string[]).includes(state);
}

export function isRunStateSucceeded(state: TTestRunState) {
return state === TestState.FINISHED;
}

export function isRunStateStopped(state: TTestRunState) {
return state === TestState.STOPPED;
}

const TestRun = ({
id = '',
traceId = '',
Expand Down
Loading

0 comments on commit 1cd98ab

Please sign in to comment.