Skip to content

Commit

Permalink
Refactor/batch send (#714)
Browse files Browse the repository at this point in the history
* refactor: extracted send request from batch run result block

* chore: ts

* refactor: batch run

* refactor: extracted send request from batch run result block
  • Loading branch information
QizhengMo authored Sep 26, 2024
1 parent e3e92e9 commit 7c1a4fa
Show file tree
Hide file tree
Showing 13 changed files with 312 additions and 114 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

RUN npm i -g pnpm@latest-9
# RUN pnpm config set electron_mirror "https://npm.taobao.org/mirrors/electron/"
# RUN pnpm config set electron_mirror "https://npmmirror.com/mirrors/electron/"

FROM pnpm-base AS base
COPY . /app
Expand Down
2 changes: 1 addition & 1 deletion packages/arex-core/src/utils/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function tryParseJsonString<T>(
try {
return parser.parse(jsonString || '{}') as T;
} catch (e) {
console.error(e);
// console.error(e);
errorTip && window.message.warning(errorTip);
return jsonString as T;
}
Expand Down
3 changes: 1 addition & 2 deletions packages/arex-request/src/helpers/utils/sendRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ export async function sendRequest(
// }
},
item: function (err: any, cursor: any, item: any, visualizer: any) {
console.log('item');
resolve({
response: res,
testResult: assertionsBox,
Expand All @@ -149,7 +148,7 @@ export async function sendRequest(
if (err) {
reject(err);
}
console.log('response', cursor, response, request, item, cookies, history);
// console.log('response', cursor, response, request, item, cookies, history);
res = {
type: 'success', // TODO check response status
headers: response?.headers.members,
Expand Down
11 changes: 10 additions & 1 deletion packages/arex/src/i18n/locales/cn/page.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@
"requestSuccess": "请求成功",
"selectCaseTip": "请选择测试用例",
"statusBlockStructure": "状态块组成",
"testStatus": "测试状态"
"testStatus": "测试状态",

"flatten": "平铺",
"groupByInterface": "接口分组",
"groupByStatus": "状态分组",

"sendNormalTestAbnormal": "测试失败",
"sendAbnormalTestNormal": "HTTP异常",
"sendNormalTestNormal": "正常",
"sendAbnormalTestAbnormal": "HTTP异常/测试失败"
},
"folderPage": {
"tests": "测试"
Expand Down
11 changes: 10 additions & 1 deletion packages/arex/src/i18n/locales/en/page.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@
"requestSuccess": "Request Success",
"selectCaseTip": "Select cases for batch execution",
"statusBlockStructure": "Status Block Structure",
"testStatus": "Test Status"
"testStatus": "Test Status",

"flatten": "Flatten",
"groupByInterface": "Group by Interface",
"groupByStatus": "Group by Status",

"sendNormalTestAbnormal": "Test Failed",
"sendAbnormalTestNormal": "HTTP Failed",
"sendNormalTestNormal": "Passed",
"sendAbnormalTestAbnormal": "HTTP/Test Failed"
},
"folderPage": {
"tests": "Tests"
Expand Down
73 changes: 45 additions & 28 deletions packages/arex/src/panes/BatchRun/BatchRun.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import {
SpaceBetweenWrapper,
useTranslation,
} from '@arextest/arex-core';
import { ArexEnvironment, ArexResponse, EnvironmentSelect } from '@arextest/arex-request';
import {
ArexEnvironment,
ArexResponse,
EnvironmentSelect,
sendRequest,
} from '@arextest/arex-request';
import { ArexRESTRequest } from '@arextest/arex-request/src';
import { useLocalStorageState, useRequest } from 'ahooks';
import { App, Button, Divider, Flex, Slider, TreeSelect, Typography } from 'antd';
Expand All @@ -23,6 +28,11 @@ import { EnvironmentService, FileSystemService } from '@/services';
import { useCollections } from '@/store';
import { decodePaneKey } from '@/store/useMenusPanes';

export type RunResult = {
request: ArexRESTRequest;
response?: ArexResponse;
};
type FsNode = { infoId: string; nodeType: CollectionNodeType };
const BatchRun: ArexPaneFC = (props) => {
const { paneKey } = props;
const { t } = useTranslation('page');
Expand All @@ -31,15 +41,15 @@ const BatchRun: ArexPaneFC = (props) => {
const { collectionsTreeData } = useCollections();

const [activeEnvironment, setActiveEnvironment] = useState<ArexEnvironment>();
const [selectNodes, setSelectNodes] = useState<
{ infoId: string; nodeType: CollectionNodeType }[]
>(id ? [{ infoId: id, nodeType: CollectionNodeType.folder }] : []);
const [selectNodes, setSelectNodes] = useState<FsNode[]>(
id ? [{ infoId: id, nodeType: CollectionNodeType.folder }] : [],
);
const selectNodesInfoId = useMemo(() => selectNodes.map((node) => node.infoId), [selectNodes]);

const [processing, setProcessing] = useState(false);

const [casesResults, setCasesResults] = useImmer<ArexRESTRequest[]>([]);
const [runResult, setRunResult] = useState<{
const [casesResults, setCasesResults] = useImmer<RunResult[]>([]);
const [currentResult, setCurrentResult] = useState<{
request: ArexRESTRequest;
response?: ArexResponse;
}>();
Expand Down Expand Up @@ -69,33 +79,40 @@ const BatchRun: ArexPaneFC = (props) => {
});

const batchGetInterfaceCaseCallback = useCallback(
async (res: ArexRESTRequest[], _timestamp?: number) => {
async function processPromiseArray(promiseArray: typeof res, qps: number) {
for (let i = 0; i < promiseArray.length; i++) {
async (requests: ArexRESTRequest[], _timestamp?: number) => {
setProcessing(false);
async function processPromiseArray(qps: number) {
for (let i = 0; i < requests.length; i++) {
if (timestampRef.current !== _timestamp) {
console.log('timestamp changed, stop batch run');
setProcessing(false);
// console.log('timestamp changed, stop batch run');
break;
}

const batch = promiseArray.slice(i, i + 1);

setCasesResults((result) => {
result.push(...batch);
const request = requests[i];
setCasesResults((results) => {
const idx = results.length;
const result: RunResult = { request };
results.push(result);
if (!request.endpoint || !request.endpoint.trim()) {
return;
}
sendRequest(request, activeEnvironment).then((response) => {
setCasesResults((results) => {
results[idx] = { ...result, response };
});
});
});

if (i + 1 < promiseArray.length) {
if (i + 1 < requests.length) {
await new Promise((resolve) => setTimeout(resolve, 1000 / qps));
}
}
}

setProcessing(true);
processPromiseArray(res, qps || 10).then(() => {
setProcessing(false);
console.log('batch run finished');
});
setRunResult(undefined);
processPromiseArray(qps || 10);

// reset result to empty
setCurrentResult(undefined);
},
[timestamp],
);
Expand All @@ -107,6 +124,7 @@ const BatchRun: ArexPaneFC = (props) => {
} = useRequest(FileSystemService.batchGetInterfaceCase, {
manual: true,
onBefore: () => {
setProcessing(true);
setCasesResults([]);
},
onSuccess: (res, [params, _timestamp]) => {
Expand Down Expand Up @@ -151,7 +169,6 @@ const BatchRun: ArexPaneFC = (props) => {
showCheckedStrategy={TreeSelect.SHOW_PARENT}
onChange={(id, labelList, extra) => {
casesResults.length && setCasesResults([]); // reset cases results
runResult && setRunResult(undefined);
try {
setSelectNodes(
extra.allCheckedNodes.map((item) => ({
Expand Down Expand Up @@ -187,9 +204,9 @@ const BatchRun: ArexPaneFC = (props) => {
<RequestTestStatusMap
key={selectNodes.length} // Add key to force re-render
data={casesResults}
selectedKey={runResult?.request.id}
selectedKey={currentResult?.request.id}
environment={activeEnvironment}
onClick={setRunResult}
onClick={setCurrentResult}
/>

<EmptyWrapper
Expand All @@ -199,11 +216,11 @@ const BatchRun: ArexPaneFC = (props) => {
overflow: auto;
`}
>
{runResult && (
{currentResult && (
<BatchRunResultItem
environment={activeEnvironment}
request={runResult.request}
response={runResult.response}
request={currentResult.request}
response={currentResult.response}
/>
)}
</EmptyWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Card } from 'antd';
import * as React from 'react';
import { ReactElement } from 'react';

import { CollectionNodeType } from '@/constant';
import { RunResult } from '@/panes/BatchRun/BatchRun';
import { GroupProps } from '@/panes/BatchRun/BatchRunResultGroup/common';

function groupByInterface(blockMap: Map<RunResult, ReactElement>) {
const interfaceMap = new Map<string, RunResult[]>();
for (const runResult of blockMap.keys()) {
const request = runResult.request;
const parent = request.parentPath[request.parentPath.length - 1];
if (parent.nodeType !== CollectionNodeType.interface) {
interfaceMap.set(request.id, [runResult]);
}
}
for (const runResult of blockMap.keys()) {
const parent = runResult.request.parentPath[runResult.request.parentPath.length - 1];
if (parent.nodeType === CollectionNodeType.interface) {
interfaceMap.get(parent.id)?.push(runResult);
}
}
return interfaceMap;
}

export function ByInterface(props: GroupProps) {
const { blockMap } = props;
// console.log(blockMap);
const interfaceMap = groupByInterface(blockMap);
// console.log(interfaceMap.values());
return (
<div style={{ maxHeight: 350, overflowY: 'scroll' }}>
{Array.from(interfaceMap.values()).map((casesOfInterface) => {
const interfaceItem = casesOfInterface[0];
return (
<Card
style={{ marginBottom: 8 }}
size='small'
key={interfaceItem.request.id}
title={interfaceItem.request.name}
>
<div style={{ display: 'flex', flexFlow: 'row wrap' }}>
{casesOfInterface.map((runResult) => {
return blockMap.get(runResult);
})}
</div>
</Card>
);
})}
</div>
);
}
76 changes: 76 additions & 0 deletions packages/arex/src/panes/BatchRun/BatchRunResultGroup/ByStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useTranslation } from '@arextest/arex-core';
import { Card } from 'antd';
import * as React from 'react';
import { ReactElement, ReactNode } from 'react';

import { RunResult } from '@/panes/BatchRun/BatchRun';
import { GroupProps } from '@/panes/BatchRun/BatchRunResultGroup/common';

const SendAbnormal = 0b1;
const SendNormal = 0b1 << 1;
const TestNormal = 0b1 << 2;
const TestAbnormal = 0b1 << 3;

const SendAbnormalTestAbnormal = SendAbnormal | TestAbnormal;
const SendAbnormalTestNormal = SendAbnormal | TestNormal;
const SendNormalTestAbnormal = SendNormal | TestAbnormal;
const SendNormalTestNormal = SendNormal | TestNormal;

function buildStatusMap(blockMap: Map<RunResult, React.ReactElement>) {
const statusMap = new Map<number, React.ReactElement[]>();
for (const key of blockMap.keys()) {
const { response } = key;
const statusCode = response?.response?.statusCode ?? 0;
const send = statusCode >= 200 && statusCode < 300 ? SendNormal : SendAbnormal;
const test =
// no case or all passed
!response?.testResult?.length || response?.testResult?.every((t) => t.passed)
? TestNormal
: TestAbnormal;

const status = send | test;
if (status === SendAbnormalTestAbnormal) {
console.log('SendAbnormalTestAbnormal', key);
}

if (!statusMap.has(status)) {
statusMap.set(status, []);
}
statusMap.get(status)!.push(blockMap.get(key)!);
}
return statusMap;
}

const GroupCard = (props: { title: string; children?: ReactElement[] }) => {
return (
<Card
size='small'
title={props.title}
style={{ display: props.children?.length ? '' : 'none', marginBottom: 8 }}
>
<div style={{ display: 'flex', flexFlow: 'row wrap' }}>{props.children}</div>
</Card>
);
};

export function ByStatus(props: GroupProps) {
const { t } = useTranslation('page');
const { blockMap } = props;
const statusMap = buildStatusMap(blockMap);
return (
<>
<GroupCard title={t('batchRunPage.sendNormalTestAbnormal')}>
{statusMap.get(SendNormalTestAbnormal)}
</GroupCard>
<GroupCard title={t('batchRunPage.sendAbnormalTestNormal')}>
{statusMap.get(SendAbnormalTestNormal)}
</GroupCard>
<GroupCard title={t('batchRunPage.sendAbnormalTestAbnormal')}>
{statusMap.get(SendAbnormalTestAbnormal)}
</GroupCard>
<GroupCard title={t('batchRunPage.sendNormalTestNormal')}>
{statusMap.get(SendNormalTestNormal)}
</GroupCard>
</>
);
}
14 changes: 14 additions & 0 deletions packages/arex/src/panes/BatchRun/BatchRunResultGroup/Flat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as React from 'react';

import { GroupProps } from '@/panes/BatchRun/BatchRunResultGroup/common';

export function Flat(props: GroupProps) {
const { blockMap } = props;
return (
<>
<div style={{ display: 'flex', flexFlow: 'row wrap' }}>
{...Array.from(blockMap.values())}
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ReactElement } from 'react';

import { RunResult } from '@/panes/BatchRun/BatchRun';

export type GroupProps = {
blockMap: Map<RunResult, ReactElement>;
selectedKey?: string;
};
Loading

0 comments on commit 7c1a4fa

Please sign in to comment.