Skip to content

Commit d52aabd

Browse files
AlexGalichenkoOleksandr_Halichenko
andauthored
Request reporting (#51)
* implemented request/response displaying * updated main menu implementation --------- Co-authored-by: Oleksandr_Halichenko <[email protected]>
1 parent aac2fbb commit d52aabd

File tree

8 files changed

+187
-44
lines changed

8 files changed

+187
-44
lines changed

CHANGELOG.MD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.18.0
2+
- added capability to display http request and response
3+
- updated main menu implementation
4+
15
# 0.17.0
26
- implemented runtime streaming
37

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@qavajs/html-formatter",
3-
"version": "0.17.0",
3+
"version": "0.18.0",
44
"main": "formatter/formatter.js",
55
"authors": [
66
"Alexandr Galichenko",

src/App.module.scss

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ footer {
3737
margin-right: 15px;
3838
}
3939

40-
.caption {
41-
//color: aliceblue;
42-
}
43-
4440
.header {
4541
z-index: 100;
4642
}
@@ -101,3 +97,16 @@ footer {
10197
.stepText {
10298
margin-right: 10px;
10399
}
100+
101+
.responseTextArea {
102+
width: 100%;
103+
}
104+
105+
.responseModal {
106+
overflow-y: scroll;
107+
}
108+
109+
.responseUrlInput {
110+
width: 600px;
111+
margin-left: 25px;
112+
}

src/AppHeader.tsx

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,40 @@
11
import React from 'react';
2-
import { MainMenu, FlexSpacer, LinkButton } from '@epam/uui';
3-
import css from './App.module.scss';
2+
import { MainMenu, FlexSpacer, MainMenuButton } from '@epam/uui';
43
import { useUuiContext } from '@epam/uui-core';
54
import { MetadataModal } from './components/MetadataModal';
5+
import { AdaptiveItemProps } from '@epam/uui-components';
66

77
export const AppHeader = () => {
88
const svc = useUuiContext();
99

10+
const getMenuItems = (): AdaptiveItemProps<{ caption?: string; onClose?: () => void }>[] => {
11+
return [
12+
{
13+
id: 'main',
14+
priority: 1,
15+
render: (p) => <MainMenuButton key={p.id} href="/" caption="@qavajs/html-formatter" />,
16+
caption: '@qavajs/html-formatter'
17+
},
18+
{ id: 'flexSpacer', priority: 2, render: (p) => <FlexSpacer key={p.id} /> },
19+
{
20+
id: 'Metadata',
21+
priority: 3,
22+
render: (p) =>
23+
<MainMenuButton
24+
key={p.id}
25+
onClick={() => svc.uuiModals.show((props) => <MetadataModal {...props} metadata={window.metadata} />)} caption="Metadata" />,
26+
caption: 'Metadata'
27+
},
28+
{
29+
id: 'Failed',
30+
priority: 4,
31+
render: (p) => <MainMenuButton key={p.id} href="#/failed-scenarios" caption="Failed Scenarios" />,
32+
caption: 'Failed Scenarios'
33+
}
34+
];
35+
};
36+
1037
return (
11-
<MainMenu cx={css.header}>
12-
<LinkButton
13-
caption='@qavajs/html-formatter'
14-
href='#/'
15-
size='48'
16-
cx={css.title}
17-
captionCX={css.caption}
18-
/>
19-
<FlexSpacer/>
20-
{window.metadata.length > 0 && <LinkButton
21-
onClick={() => svc.uuiModals.show((props) => <MetadataModal { ...props } metadata={window.metadata}/>)}
22-
caption='Metadata'
23-
size='48'
24-
cx={css.failedTitle}
25-
captionCX={css.caption}
26-
/>}
27-
<LinkButton
28-
caption='Failed Scenarios'
29-
href='#/failed-scenarios'
30-
size='48'
31-
cx={css.failedTitle}
32-
captionCX={css.caption}
33-
/>
34-
</MainMenu>
35-
)
36-
}
38+
<MainMenu items={getMenuItems()} />
39+
);
40+
};

src/components/ResponseModal.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {
2+
ModalBlocker,
3+
ModalFooter,
4+
ModalHeader,
5+
ModalWindow,
6+
FlexRow,
7+
Panel,
8+
Badge,
9+
Text, TextInput, TextArea, FlexCell, TabButton
10+
} from '@epam/loveship';
11+
12+
import css from '../App.module.scss';
13+
import { useState } from 'react';
14+
15+
export const ResponseModal = (modalProps: any) => {
16+
const apiData = JSON.parse(modalProps.response.body);
17+
const [requestBodyType, onRequestBodyTypeChange] = useState('Base64');
18+
const [responseBodyType, onResponseBodyTypeChange] = useState('Base64');
19+
const [requestBody, updateRequestBody] = useState(apiData.request.body);
20+
const [responseBody, updateResponseBody] = useState(apiData.response.body);
21+
const requestHeaders = Object.entries(apiData.request.headers).map(([key, value]) => `${key}: ${value}`).join('\n');
22+
const responseHeaders = Object.entries(apiData.response.headers).map(([key, value]) => `${key}: ${value}`).join('\n');
23+
const statusText = apiData.request.method + ': ' + apiData.response.status;
24+
return <>
25+
<ModalBlocker blockerShadow="dark" {...modalProps}>
26+
<ModalWindow width={900}>
27+
<Panel cx={css.responseModal}>
28+
<ModalHeader title="Response" onClose={() => modalProps.success('close')} />
29+
<FlexRow padding="24">
30+
<FlexRow columnGap="18" key="02">
31+
<TextInput cx={css.responseUrlInput} value={apiData.request.url} onValueChange={() => {}} />
32+
<Badge size="36" color="success" fill="outline" caption={statusText} />
33+
{/*<Badge size="36" color="success" fill="outline" caption={apiData.response.status} />*/}
34+
</FlexRow>
35+
</FlexRow>
36+
<FlexRow padding="24" vPadding="18">
37+
<FlexCell width="100%">
38+
<FlexRow padding="24" vPadding="18">
39+
<Text>Request body:</Text>
40+
<TabButton caption="Base64" isLinkActive={ requestBodyType === 'Base64' } onClick={ () => { onRequestBodyTypeChange('Base64'); updateRequestBody(apiData.request.body) } } size="36" />
41+
<TabButton caption="Text" isLinkActive={ requestBodyType === 'Text' } onClick={ () => { onRequestBodyTypeChange('Text'); updateRequestBody(atob(apiData.request.body).toString()) } } size="36" />
42+
</FlexRow>
43+
<FlexRow padding="24" vPadding="18">
44+
<TextArea cx={css.responseTextArea} rows={8} value={requestBody}
45+
onValueChange={() => {}} />
46+
</FlexRow>
47+
<FlexRow padding="24" vPadding="18">
48+
<Text>Request headers:</Text>
49+
</FlexRow>
50+
<FlexRow padding="24" vPadding="18">
51+
<TextArea cx={css.responseTextArea} rows={8} value={requestHeaders}
52+
onValueChange={() => {}} />
53+
</FlexRow>
54+
</FlexCell>
55+
<FlexCell width="100%">
56+
<FlexRow padding="24" vPadding="18">
57+
<Text>Response body:</Text>
58+
<FlexRow>
59+
<TabButton caption="Base64" isLinkActive={ responseBodyType === 'Base64' } onClick={ () => { onResponseBodyTypeChange('Base64'); updateResponseBody(apiData.response.body) } } size="36" />
60+
<TabButton caption="Text" isLinkActive={ responseBodyType === 'Text' } onClick={ () => { onResponseBodyTypeChange('Text'); updateResponseBody(atob(apiData.response.body).toString()) } } size="36" />
61+
</FlexRow>
62+
</FlexRow>
63+
<FlexRow padding="24" vPadding="18">
64+
<TextArea cx={css.responseTextArea} rows={8} value={responseBody}
65+
onValueChange={() => {}} />
66+
</FlexRow>
67+
<FlexRow padding="24" vPadding="18">
68+
<Text>Response headers:</Text>
69+
</FlexRow>
70+
<FlexRow padding="24" vPadding="18">
71+
<TextArea cx={css.responseTextArea} rows={8} value={responseHeaders}
72+
onValueChange={() => {}} />
73+
</FlexRow>
74+
</FlexCell>
75+
</FlexRow>
76+
<ModalFooter />
77+
</Panel>
78+
79+
</ModalWindow>
80+
</ModalBlocker>
81+
</>;
82+
};

src/components/Step.tsx

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
import {Text, IconContainer, IconButton, LinkButton, FlexRow} from '@epam/loveship';
1+
import { Text, IconContainer, IconButton, LinkButton, FlexRow } from '@epam/loveship';
22
import { ErrorModal } from './ErrorModal';
3+
import { AttachmentModal } from './AttachmentModal';
4+
import { ResponseModal } from './ResponseModal';
5+
import { LogsModal } from './LogsModal';
6+
import { TimeLabel } from './TimeLabel';
7+
import { FlexSpacer } from '@epam/uui';
8+
39
import { ReactComponent as PassedIcon } from '@epam/assets/icons/common/notification-done-24.svg';
410
import { ReactComponent as FailedIcon } from '@epam/assets/icons/common/navigation-close-24.svg';
511
import { ReactComponent as SkippedIcon } from '@epam/assets/icons/common/navigation-chevron-right_right-24.svg';
@@ -9,15 +15,15 @@ import { ReactComponent as PendingIcon } from '@epam/assets/icons/common/navigat
915
import { ReactComponent as ErrorIcon } from '@epam/assets/icons/common/notification-info-fill-24.svg';
1016
import { ReactComponent as AttachmentIcon } from '@epam/assets/icons/common/file-attachment-24.svg';
1117
import { ReactComponent as LogsIcon } from '@epam/assets/icons/common/content-code-24.svg';
18+
import { ReactComponent as ResponseIcon } from '@epam/assets/icons/common/content-code_braces-24.svg';
1219

1320
import css from '../App.module.scss';
1421
import { useUuiContext } from '@epam/uui-core';
15-
import { AttachmentModal } from './AttachmentModal';
1622
import { supportedMimeTypes } from '../utils/supportedMimeTypes';
1723
import { openInNewTab } from '../utils/openInNewTab';
18-
import { LogsModal } from './LogsModal';
19-
import { TimeLabel } from './TimeLabel';
20-
import { FlexSpacer } from '@epam/uui';
24+
25+
const logPlainMimeType = 'text/x.cucumber.log+plain';
26+
const responseMimeType = 'text/x.response.json';
2127

2228
const icon = (status: string) => {
2329
switch (status) {
@@ -69,11 +75,15 @@ const handleLogsClick = (logs: any[], svc: any) => {
6975
return () => svc.uuiModals.show((props: any) => <LogsModal { ...props } logs={logs}/>)
7076
}
7177

78+
const handleResponseClick = (response: any, svc: any) => {
79+
return () => svc.uuiModals.show((props: any) => <ResponseModal { ...props } response={response}/>)
80+
}
81+
7282
export const Step = ({step}: {step: any}) => {
7383
const svc = useUuiContext();
74-
const logs = step.embeddings
75-
? step.embeddings.filter((embedding: any) => embedding.mime_type === 'text/x.cucumber.log+plain')
76-
: [];
84+
const logs = step.embeddings?.filter((embedding: any) => embedding.mime_type === logPlainMimeType) ?? [];
85+
const responses = step.embeddings?.filter((embedding: any) => embedding.mime_type === responseMimeType) ?? [];
86+
const attachments = step.embeddings?.filter((embedding: any) => ![logPlainMimeType, responseMimeType].includes(embedding.mime_type)) ?? [];
7787
return <div>
7888
<FlexRow>
7989
{icon(step.result.status)}
@@ -88,10 +98,17 @@ export const Step = ({step}: {step: any}) => {
8898
caption='Logs'
8999
onClick={ handleLogsClick(logs, svc) }
90100
/>}
91-
{step.embeddings && step.embeddings
92-
.filter((embedding: any) => embedding.mime_type !== 'text/x.cucumber.log+plain')
101+
{responses
102+
.map((embedding: any, index: any) => <LinkButton
103+
key={ `response-${index}` }
104+
icon={ ResponseIcon }
105+
caption='Response'
106+
onClick={ handleResponseClick(embedding, svc) }
107+
/>)
108+
}
109+
{attachments
93110
.map((embedding: any, index: any) => <LinkButton
94-
key={index}
111+
key={ `attachment-${index}` }
95112
icon={ AttachmentIcon }
96113
caption={ embedding.fileName ?? embedding.mime_type }
97114
onClick={ handleAttachmentClick(embedding, svc) }

test/features/Response.feature

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Feature: Response
2+
3+
Scenario: scenario passed with logs
4+
When step with response

test/step_definitions/custom_steps.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,29 @@ When('failed step with log', function () {
7777
throw new Error('failed step');
7878
});
7979

80+
When('step with response', function () {
81+
const response = {
82+
request: {
83+
method: 'POST',
84+
url: 'http://localhost:3000/#/feature/featurefc7c2610-bd2a-450d-b311-d5fafa543ef66',
85+
body: 'cXdlcnR5MTIz',
86+
headers: {
87+
header1: 'value',
88+
otherHeader1: 'value2',
89+
anotherHeader1: 'value3',
90+
}
91+
},
92+
response: {
93+
status: 200,
94+
body: 'cXdlcnR5MTIzcmVzcG9uc2U=',
95+
headers: {
96+
headerresponse: 'value1'
97+
}
98+
}
99+
}
100+
this.attach(JSON.stringify(response), 'text/x.response.json')
101+
});
102+
80103
When(`I expect {string} to match {string}`, async function (one, two) {});
81104

82105
Before(async function () {});

0 commit comments

Comments
 (0)