Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: display skip reason in new ui #618

Merged
merged 2 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions lib/adapters/test-result/testplane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {
hasUnrelatedToScreenshotsErrors,
isImageDiffError,
isInvalidRefImageError,
isNoRefImageError,
wrapLinkByTag
isNoRefImageError
} from '../../common-utils';
import {
ErrorDetails,
Expand Down Expand Up @@ -50,7 +49,7 @@ const getSkipComment = (suite: TestplaneTestResult | TestplaneSuite): string | n
};

const wrapSkipComment = (skipComment: string | null | undefined): string => {
return skipComment ? wrapLinkByTag(skipComment) : 'Unknown reason';
return skipComment ?? 'Unknown reason';
};

const getHistory = (history?: TestplaneTestResult['history']): TestStepCompressed[] => {
Expand Down
6 changes: 0 additions & 6 deletions lib/common-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,6 @@ export const getRelativeUrl = (absoluteUrl: string): string => {
}
};

export const wrapLinkByTag = (text: string): string => {
return text.replace(/https?:\/\/[^\s]*/g, (url) => {
return `<a target="_blank" href="${url}">${url}</a>`;
});
};

export const mkTestId = (fullTitle: string, browserId: string): string => {
return fullTitle + '.' + browserId;
};
Expand Down
4 changes: 2 additions & 2 deletions lib/static/components/section/section-browser.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {Fragment, useContext, useLayoutEffect} from 'react';
import {last, isEmpty} from 'lodash';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import Parser from 'html-react-parser';
import PropTypes from 'prop-types';
import * as actions from '../../modules/actions';
import BrowserTitle from './title/browser';
Expand All @@ -11,6 +10,7 @@ import Body from './body';
import {isSkippedStatus} from '../../../common-utils';
import {sectionStatusResolver} from './utils';
import {MeasurementContext} from '../measurement-context';
import {makeLinksClickable} from '@/static/new-ui/utils';

function SectionBrowser(props) {
const onToggleSection = () => {
Expand All @@ -24,7 +24,7 @@ function SectionBrowser(props) {
<Fragment>
[skipped] {props.browser.name}
{skipReason && ', reason: '}
{skipReason && Parser(skipReason)}
{skipReason && makeLinksClickable(skipReason)}
</Fragment>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
--g-button-text-color-hover: hsl(208 88% 48% / 1);
}

.attempt-picker-item--skipped {
background-color: var(--g-color-private-black-50);
color: var(--g-color-private-black-600);
--box-shadow-color: var(--g-color-private-black-250);
--g-button-text-color-hover: var(--g-color-private-black-700);
}

.attempt-picker-item--success {
--box-shadow-color: #21a95661;
}
Expand Down
26 changes: 18 additions & 8 deletions lib/static/new-ui/components/MetaInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import path from 'path';

import {DefinitionList} from '@gravity-ui/components';
import {mapValues, isObject, omitBy, isEmpty} from 'lodash';
import {isEmpty, isObject, mapValues, omitBy} from 'lodash';
import React, {ReactNode} from 'react';
import {connect} from 'react-redux';

import {isUrl, getUrlWithBase, getRelativeUrl} from '@/common-utils';
import {getRelativeUrl, getUrlWithBase, isUrl} from '@/common-utils';
import {ResultEntity, State} from '@/static/new-ui/types/store';
import {HtmlReporterValues} from '@/plugin-api';
import {ReporterConfig} from '@/types';
import styles from './index.module.css';
import {makeLinksClickable} from '@/static/new-ui/utils';
import {TestStatus} from '@/constants';

const serializeMetaValues = (metaInfo: Record<string, unknown>): Record<string, string> =>
mapValues(metaInfo, (v): string => {
Expand All @@ -20,9 +22,9 @@ const serializeMetaValues = (metaInfo: Record<string, unknown>): Record<string,
return v?.toString() ?? 'No value';
});

interface MetaInfoItem {
interface MetaInfoItem<T> {
label: string;
content: string;
content: T;
url?: string;
copyText?: string;
}
Expand Down Expand Up @@ -58,12 +60,12 @@ function MetaInfoInternal(props: MetaInfoInternalProps): ReactNode {
...resolveMetaInfoExtenders()
});

const metaInfoItems: MetaInfoItem[] = Object.entries(serializedMetaValues).map(([key, value]) => ({
const metaInfoItems: MetaInfoItem<string>[] = Object.entries(serializedMetaValues).map(([key, value]) => ({
label: key,
content: value
}));

const metaInfoItemsWithResolvedUrls = metaInfoItems.map((item) => {
const metaInfoItemsWithResolvedUrls: MetaInfoItem<string | React.JSX.Element>[] = metaInfoItems.map((item) => {
if (item.label === 'url' || metaInfoBaseUrls[item.label] === 'auto') {
const url = getUrlWithBase(item.content, baseHost);
return {
Expand Down Expand Up @@ -109,6 +111,14 @@ function MetaInfoInternal(props: MetaInfoInternalProps): ReactNode {
});
}

if (result.status === TestStatus.SKIPPED && result.skipReason) {
metaInfoItemsWithResolvedUrls.push({
label: 'skipReason',
content: makeLinksClickable(result.skipReason),
copyText: result.skipReason
});
}

return <DefinitionList className={styles.metaInfo} qa={props.qa} items={
metaInfoItemsWithResolvedUrls.map((item) => {
if (item.url) {
Expand All @@ -117,14 +127,14 @@ function MetaInfoInternal(props: MetaInfoInternalProps): ReactNode {
content: <a className={styles.metaInfoValue} data-suite-view-link={item.url} target="_blank" href={item.url} rel="noreferrer">
{item.content}
</a>,
copyText: item.copyText ?? item.content
copyText: item.copyText ?? item.content as string
};
}

return {
name: item.label,
content: <span className={styles.metaInfoValue}>{item.content}</span>,
copyText: item.copyText ?? item.content
copyText: item.copyText ?? item.content as string
};
})
}/>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface TreeViewItemData {
errorStack?: string;
images?: ImageEntity[];
parentData?: TreeViewItemData;
skipReason?: string;
}

export interface TreeRoot {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
color: #fff !important;
/* Sets spinner color */
--g-color-line-brand: #fff;
--color-link: #fff;
--color-link-hover: rgba(255, 255, 255, 0.6);
}

.tree-view__item__title--current {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export const getTreeViewItems = createSelector(
images: resultImages,
errorTitle,
errorStack,
parentData
parentData,
skipReason: lastResult.skipReason
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ interface TestStepsProps {
}

function TestStepsInternal(props: TestStepsProps): ReactNode {
if (props.testSteps.length === 0) {
return null;
}

const items = useList({
items: props.testSteps,
withExpandedState: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,21 @@
font-size: 15px;
word-break: break-word;
}

.skip-reason-container {
font-size: 15px;
overflow: hidden;
}

.skip-reason {
overflow: hidden;
text-overflow: ellipsis;
}

.skip-reason-container a:link, .skip-reason-container a:visited {
color: var(--color-link);
}

.skip-reason-container a:hover {
color: var(--color-link-hover);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {ImageWithMagnifier} from '@/static/new-ui/components/ImageWithMagnifier'
import {ImageEntityFail} from '@/static/new-ui/types/store';
import styles from './index.module.css';
import {getAssertViewStatusMessage} from '@/static/new-ui/utils/assert-view-status';
import {makeLinksClickable} from '@/static/new-ui/utils';

interface TreeViewItemSubtitleProps {
item: TreeViewItemData;
Expand All @@ -16,7 +17,11 @@ interface TreeViewItemSubtitleProps {
}

export function TreeViewItemSubtitle(props: TreeViewItemSubtitleProps): ReactNode {
if (props.item.images?.length) {
if (props.item.skipReason) {
return <div className={styles.skipReasonContainer}>
<div className={styles.skipReason}>Skipped ⋅ {makeLinksClickable(props.item.skipReason)}</div>
</div>;
} else if (props.item.images?.length) {
return <div>
{props.item.images.map((imageEntity, index) => {
const image = (imageEntity as ImageEntityFail).diffImg ?? (imageEntity as ImageEntityFail).actualImg;
Expand Down
1 change: 1 addition & 0 deletions lib/static/new-ui/types/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface ResultEntityCommon {
suitePath: string[];
/** @note Browser Name/ID, e.g. `chrome-desktop` */
name: string;
skipReason?: string;
}

export interface ResultEntityError extends ResultEntityCommon {
Expand Down
40 changes: 33 additions & 7 deletions lib/static/new-ui/utils/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
CloudCheck
} from '@gravity-ui/icons';
import {Spin} from '@gravity-ui/uikit';
import React from 'react';
import React, {ReactNode} from 'react';

import {TestStatus} from '@/constants';
import {ImageFile} from '@/types';
Expand All @@ -34,12 +34,6 @@ export const getIconByStatus = (status: TestStatus): React.JSX.Element => {
return <CircleDashed />;
};

export const getFullTitleByTitleParts = (titleParts: string[]): string => {
const DELIMITER = ' ';

return titleParts.join(DELIMITER).trim();
};

export const getImageDisplayedSize = (image: ImageFile): string => `${image.size.width}×${image.size.height}`;

export const stringify = (value: unknown): string => {
Expand All @@ -61,3 +55,35 @@ export const stringify = (value: unknown): string => {

return toString(value);
};

export const makeLinksClickable = (text: string): React.JSX.Element => {
const urlRegex = /https?:\/\/[^\s]*/g;

const parts = text.split(urlRegex);
const urls = text.match(urlRegex) || [];

return <>{
parts.reduce((elements, part, index) => {
elements.push(part);

if (urls[index]) {
const href = urls[index].startsWith('www.')
? `http://${urls[index]}`
: urls[index];

elements.push(
<a
key={index}
href={href}
target="_blank"
rel="noopener noreferrer"
>
{urls[index]}
</a>
);
}

return elements;
}, [] as ReactNode[])
}</>;
};
49 changes: 34 additions & 15 deletions test/unit/lib/static/components/section/section-browser.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {expect} from 'chai';
import React from 'react';
import {defaults, set} from 'lodash';
import {defaults} from 'lodash';
import proxyquire from 'proxyquire';
import {SUCCESS, SKIPPED, ERROR} from 'lib/constants/test-statuses';
import {UNCHECKED} from 'lib/constants/checked-statuses';
Expand Down Expand Up @@ -30,7 +31,6 @@ describe('<SectionBrowser/>', () => {

SectionBrowser = proxyquire('lib/static/components/section/section-browser', {
'../../modules/actions': actionsStub,
'./title/browser-skipped': {default: BrowserSkippedTitle},
'./title/browser': {default: BrowserTitle},
'./body': {default: Body}
}).default;
Expand All @@ -45,12 +45,9 @@ describe('<SectionBrowser/>', () => {
const resultsById = mkResult({id: 'res', status: SKIPPED});
const tree = mkStateTree({browsersById, browsersStateById, resultsById});

mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree});
const section = mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree});

assert.calledWithMatch(
BrowserSkippedTitle,
set({}, 'title.props.children', [`[${SKIPPED}] `, 'yabro', undefined, undefined])
);
expect(section.getByText(/.*?\[skipped\].*?/)).to.exist;
});

it('should pass skip reason', () => {
Expand All @@ -59,12 +56,9 @@ describe('<SectionBrowser/>', () => {
const resultsById = mkResult({id: 'res', status: SKIPPED, skipReason: 'some-reason'});
const tree = mkStateTree({browsersById, browsersStateById, resultsById});

mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree});
const section = mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree});

assert.calledWithMatch(
BrowserSkippedTitle,
set({}, 'title.props.children', [`[${SKIPPED}] `, 'yabro', ', reason: ', 'some-reason'])
);
expect(section.getByText(/.*?reason:\s*some-reason.*?/)).to.exist;
});

it('should not render body even if browser in opened state', () => {
Expand All @@ -81,6 +75,13 @@ describe('<SectionBrowser/>', () => {

describe('executed test with fails in retries and skip in result', () => {
it('should render not skipped title', () => {
SectionBrowser = proxyquire('lib/static/components/section/section-browser', {
'../../modules/actions': actionsStub,
'./title/browser-skipped': {default: BrowserSkippedTitle},
'./title/browser': {default: BrowserTitle},
'./body': {default: Body}
}).default;

const browsersById = mkBrowser(
{id: 'yabro-1', name: 'yabro', resultIds: ['res-1', 'res-2'], parentId: 'test'}
);
Expand All @@ -93,10 +94,28 @@ describe('<SectionBrowser/>', () => {

mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree});

assert.calledWithMatch(
BrowserTitle,
set({}, 'title.props.children', [`[${SKIPPED}] `, 'yabro', ', reason: ', 'some-reason'])
assert.notCalled(BrowserSkippedTitle);
});

it('should render title with skip reason', () => {
SectionBrowser = proxyquire('lib/static/components/section/section-browser', {
'../../modules/actions': actionsStub,
'./body': {default: Body}
}).default;

const browsersById = mkBrowser(
{id: 'yabro-1', name: 'yabro', resultIds: ['res-1', 'res-2'], parentId: 'test'}
);
const browsersStateById = {'yabro-1': {shouldBeShown: true, shouldBeOpened: true}};
const resultsById = {
...mkResult({id: 'res-1', status: ERROR, error: {}}),
...mkResult({id: 'res-2', status: SKIPPED, skipReason: 'some-reason'})
};
const tree = mkStateTree({browsersById, browsersStateById, resultsById});

const section = mkSectionBrowserComponent({browserId: 'yabro-1'}, {tree});

expect(section.getByText(/.*?reason:\s*some-reason.*?/)).to.exist;
});

it('should render body if browser in opened state', () => {
Expand Down