Skip to content

Commit

Permalink
Feat env tag (#565)
Browse files Browse the repository at this point in the history
* feat: create plan tags

* feat: runningStatus caseTags

* chore: modify test service url

* fix: operationTypes null

* fix: operationTypes null

* chore: restore serviceUrl

---------

Co-authored-by: onePone <[email protected]>
  • Loading branch information
1pone and Xremn authored Dec 15, 2023
1 parent b016ff6 commit 9b35923
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 35 deletions.
2 changes: 1 addition & 1 deletion packages/arex/src/components/InterfaceSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const InterfaceSelect = (props: SelectProps & { appId: string; open?: boolean })
() =>
Object.entries(
data.reduce<Record<string, { label: string; value?: string | null }[]>>((options, item) => {
item.operationTypes.forEach((operation) => {
item.operationTypes?.forEach((operation) => {
if (options[operation]) {
options[operation].push({
label: item.operationName,
Expand Down
95 changes: 95 additions & 0 deletions packages/arex/src/components/TagSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { PlusOutlined } from '@ant-design/icons';
import { css, useTranslation } from '@arextest/arex-core';
import { useAutoAnimate } from '@formkit/auto-animate/react';
import { Button, Cascader, Space, Tag } from 'antd';
import React, { FC, useMemo, useState } from 'react';

import { CaseTags } from '@/services/ScheduleService';

export interface TagSelectProps {
tags?: Record<string, string[]>;
value?: CaseTags;
onChange?: (value: CaseTags) => void;
}

interface Option {
value: string | number;
label: string;
children?: Option[];
}
const TagSelect: FC<TagSelectProps> = (props) => {
const { tags, value, onChange } = props;
const { t } = useTranslation('common');

const [wrapperRef] = useAutoAnimate();
const [addTagModalVisible, setAddTagModalVisible] = useState(false);

const options = useMemo<Option[]>(
() =>
Object.entries(tags || {}).map(([key, value]) => ({
label: key,
value: key,
children: value.map((v) => ({ label: v, value: v })),
})),
[tags],
);

return (
<Space ref={wrapperRef}>
{Object.entries(value || {}).map(([tagKey, tagValue], index) => (
<Tag
closable
key={`${tagKey}-${tagValue}`}
onClose={() => {
const newValue = { ...value };
delete newValue[tagKey];
onChange?.(newValue);
}}
>{`${tagKey}:${tagValue}`}</Tag>
))}

{addTagModalVisible ? (
<div
key='add-tag-input'
css={css`
ul.ant-cascader-menu {
height: auto !important;
}
`}
>
<Cascader
allowClear
value={[]}
size='small'
options={options}
style={{ width: '64px' }}
open={addTagModalVisible}
getPopupContainer={(triggerNode) => triggerNode.parentElement}
onBlur={() => {
setAddTagModalVisible(false);
}}
onChange={(value) => {
setAddTagModalVisible(false);
onChange?.({ ...props.value, [value[0]]: value[1] as string });
}}
/>
</div>
) : (
<Button
type='dashed'
size='small'
key='add-tag-input'
icon={<PlusOutlined />}
style={{ width: '64px', fontSize: '10px' }}
onClick={() => {
setAddTagModalVisible(true);
}}
>
{t('add')}
</Button>
)}
</Space>
);
};

export default TagSelect;
1 change: 1 addition & 0 deletions packages/arex/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export { default as MenuSelect } from './MenuSelect';
export { default as NextInterfaceButton } from './PlanItemNavigation';
export { default as SingleCollapse } from './SingleCollapse';
export { default as StatusTag } from './StatusTag';
export { default as TagSelect } from './TagSelect';
export { default as UserMenu } from './UserMenu';
export { default as WorkspacesMenu } from './WorkspacesMenu';
1 change: 1 addition & 0 deletions packages/arex/src/i18n/locales/cn/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"paths": "回放路径",
"pathsTooltip": "选择需要回放的路径。如果不指定,默认情况下,所有路径下的用例都将被回放。",
"pathsPlaceholder": "可选,默认选择所有路径",
"caseTags": "标签",
"state": "状态",
"all": "全部",
"passed": "成功",
Expand Down
1 change: 1 addition & 0 deletions packages/arex/src/i18n/locales/en/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"pathsTooltip": "Select the paths for which cases will be replayed. If not specified, all paths will be replayed by default.",
"pathsPlaceholder": "Optional, all paths are selected by default",
"replayReportName": " Report Name",
"caseTags": "Tags",
"state": "State",
"all": "All",
"passed": "Passed",
Expand Down
2 changes: 1 addition & 1 deletion packages/arex/src/panes/AppSetting/CompareConfig/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const CompareConfig: FC<CompareConfigProps> = (props) => {
Object.entries(
operationList.reduce<Record<string, { label: string; value?: string | null }[]>>(
(options, item) => {
item.operationTypes.forEach((operation) => {
item.operationTypes?.forEach((operation) => {
if (options[operation]) {
options[operation].push({
label: item.operationName,
Expand Down
1 change: 0 additions & 1 deletion packages/arex/src/panes/AppSetting/Other/AppBasicSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const AppBasicSetup: FC<AppBasicSetupProps> = (props) => {
ready: !!appId,
defaultParams: [appId as string],
onSuccess(res) {
console.log(res);
form.setFieldsValue({
appName: res.appName,
owners: res.owners ?? undefined, // set null to undefined
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DeleteOutlined } from '@ant-design/icons';
import { HelpTooltip, useTranslation } from '@arextest/arex-core';
import { useRequest } from 'ahooks';
import { Badge, Button, Popconfirm, Table, Typography } from 'antd';
import { Badge, Button, Popconfirm, Table, Tag, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import dayjs from 'dayjs';
import React, { FC } from 'react';
Expand Down Expand Up @@ -88,6 +88,19 @@ const RunningStatus: FC<RunningStatusProps> = (props) => {
</Typography.Text>
),
},
{
title: t('replay.caseTags'),
dataIndex: 'tags',
align: 'center',
render: (_, record) =>
_
? Object.entries(record.tags || {}).map(([key, value], index) => (
<Tag key={index}>
{key}:{value}
</Tag>
))
: '-',
},
{
title: t('replay.action', { ns: 'components' }),
align: 'center',
Expand Down
33 changes: 18 additions & 15 deletions packages/arex/src/panes/Replay/AppTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,33 @@ import {
AutoComplete,
Badge,
Button,
Checkbox,
Collapse,
DatePicker,
Divider,
Form,
Input,
Menu,
Modal,
Popover,
Select,
Skeleton,
Space,
theme,
Typography,
} from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import React, { createElement, FC, ReactNode, useCallback, useMemo, useRef, useState } from 'react';

import { InterfaceSelect } from '@/components';
import { InterfaceSelect, TagSelect } from '@/components';
import { EMAIL_KEY, PanesType, TARGET_HOST_AUTOCOMPLETE_KEY } from '@/constant';
import { useNavPane } from '@/hooks';
import CompareNoise from '@/panes/Replay/CompareNoise';
import RecordedCaseList, { RecordedCaseListRef } from '@/panes/Replay/RecordedCaseList';
import { ApplicationService, ScheduleService } from '@/services';
import { MessageMap } from '@/services/ScheduleService';
import { ScheduleService } from '@/services';
import { CaseTags, MessageMap } from '@/services/ScheduleService';

type AppTitleProps = {
appId: string;
appName?: string;
readOnly?: boolean;
recordCount?: number;
tags?: Record<string, string[]>;
onRefresh?: () => void;
onQueryRecordCount?: () => void;
};
Expand All @@ -65,6 +61,7 @@ type CreatePlanForm = {
targetEnv: string;
caseSourceRange: [Dayjs, Dayjs];
operationList?: string[];
caseTags?: CaseTags;
};

const TitleWrapper = styled(
Expand Down Expand Up @@ -161,6 +158,7 @@ const AppTitle: FC<AppTitleProps> = ({
appName,
readOnly,
recordCount = 0,
tags,
onRefresh,
onQueryRecordCount,
}) => {
Expand All @@ -176,7 +174,7 @@ const AppTitle: FC<AppTitleProps> = ({
const [form] = Form.useForm<CreatePlanForm>();
const targetEnv = Form.useWatch('targetEnv', form);

const [open, setOpen] = useState(false);
const [openPathDropdown, setOpenPathDropdown] = useState(false);

const [targetHostSource, setTargetHostSource] = useLocalStorageState<{
[appId: string]: string[];
Expand Down Expand Up @@ -220,7 +218,7 @@ const AppTitle: FC<AppTitleProps> = ({
});
},
onFinally() {
setOpen(false);
setOpenPathDropdown(false);
form.resetFields();
},
});
Expand All @@ -242,6 +240,7 @@ const AppTitle: FC<AppTitleProps> = ({
})),
operator: email as string,
replayPlanType: Number(Boolean(values.operationList?.length)),
caseTags: values.caseTags,
});

// update targetHostSource
Expand Down Expand Up @@ -294,7 +293,7 @@ const AppTitle: FC<AppTitleProps> = ({
),
value: item,
})) || [],
[appId, targetHostSource, open],
[appId, targetHostSource, openPathDropdown],
);

const handleClickTitle = useCallback(() => caseListRef.current?.open(), [caseListRef]);
Expand All @@ -312,7 +311,7 @@ const AppTitle: FC<AppTitleProps> = ({
}, [appId]);

const handleCloseModal = useCallback(() => {
setOpen(false);
setOpenPathDropdown(false);
form.resetFields();
}, [form]);

Expand Down Expand Up @@ -340,7 +339,7 @@ const AppTitle: FC<AppTitleProps> = ({
type='primary'
disabled={readOnly}
icon={<PlayCircleOutlined />}
onClick={() => setOpen(true)}
onClick={() => setOpenPathDropdown(true)}
>
{t('replay.startButton')}
</Button>
Expand All @@ -349,7 +348,7 @@ const AppTitle: FC<AppTitleProps> = ({

<Modal
title={`${t('replay.startButton')} - ${appId}`}
open={open}
open={openPathDropdown}
onOk={handleStartReplay}
onCancel={handleCloseModal}
styles={{
Expand Down Expand Up @@ -456,11 +455,15 @@ const AppTitle: FC<AppTitleProps> = ({
>
<InterfaceSelect
appId={appId}
open={open}
open={openPathDropdown}
placeholder={t('replay.pathsPlaceholder')}
/>
</Form.Item>

<Form.Item label={t('replay.caseTags')} name='caseTags'>
<TagSelect tags={tags} />
</Form.Item>

<Form.Item label={'Webhook'}>
<Typography.Text copyable>{webhook}</Typography.Text>
</Form.Item>
Expand Down
21 changes: 13 additions & 8 deletions packages/arex/src/panes/Replay/PlanItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
Row,
Space,
Statistic,
Tag,
theme,
Tooltip,
Typography,
Expand Down Expand Up @@ -511,14 +512,18 @@ const PlanItem: FC<ReplayPlanItemProps> = (props) => {
<Label>{t('replay.executor')}</Label>
{selectedPlan.creator}
</div>
<div>
<Label>{t('replay.recordVersion')}</Label>
{selectedPlan.caseRecordVersion || '-'}
&nbsp;
<span>
{t('replay.replayVersion')}: {selectedPlan.coreVersion || '-'}
</span>
</div>
{selectedPlan.caseTags && (
<div style={{ marginTop: '2px' }}>
<Label>{t('replay.caseTags')}</Label>
<Space>
{Object.entries(selectedPlan.caseTags || {}).map(([key, value]) => (
<Tag key={key}>
{key}:{value}
</Tag>
))}
</Space>
</div>
)}
</Col>

<Col span={12}>
Expand Down
1 change: 1 addition & 0 deletions packages/arex/src/panes/Replay/Replay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const ReplayPage: ArexPaneFC = (props) => {
appName={appInfo?.appName}
readOnly={!hasOwner}
recordCount={recordCount}
tags={appInfo?.tags}
onRefresh={handleRefreshDep}
onQueryRecordCount={queryRecordCount}
/>
Expand Down
9 changes: 2 additions & 7 deletions packages/arex/src/panes/ReplayCase/ReplayCase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import {
CollapseTable,
DiffMatch,
getJsonValueByPath,
getLocalStorage,
i18n,
I18nextLng,
jsonIndexPathFilter,
Label,
PaneDrawer,
Expand All @@ -26,22 +23,20 @@ import dayjs from 'dayjs';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { NextInterfaceButton } from '@/components';
import { APP_ID_KEY, EMAIL_KEY, PanesType } from '@/constant';
import { APP_ID_KEY, PanesType } from '@/constant';
import CompareConfig from '@/panes/AppSetting/CompareConfig';
import { ComparisonService, ReportService, ScheduleService } from '@/services';
import { DependencyParams, ExpirationType } from '@/services/ComparisonService';
import { InfoItem, PlanItemStatistic, ReplayCaseType } from '@/services/ReportService';
import { MessageMap } from '@/services/ScheduleService';
import { useMenusPanes } from '@/store';
import { decodePaneKey } from '@/store/useMenusPanes';

import Case, { CaseProps } from './Case';
import SaveCase, { SaveCaseRef } from './SaveCase';

const ReplayCasePage: ArexPaneFC<{ filter?: number } | undefined> = (props) => {
const { message, notification } = App.useApp();
const { message } = App.useApp();
const { activePane } = useMenusPanes();
const email = getLocalStorage<string>(EMAIL_KEY);
const { t } = useTranslation(['components']);

const [wrapperRef] = useAutoAnimate();
Expand Down
2 changes: 2 additions & 0 deletions packages/arex/src/services/ApplicationService/getAppList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ export type ApplicationDataType = {
owner: string;
owners?: string[];
organizationName: string;
organizationId: string;
recordedCaseCount: number;
visibilityLevel: AppVisibilityLevel;
tags: Record<string, string[]>;
};

export type RegressionListRes = Array<{
Expand Down
Loading

0 comments on commit 9b35923

Please sign in to comment.