Feature  Change report summary layout
ADD
FrontEnd/Sailfish-JS-report/resources/icons/report-summary-error.svg
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.85786 0H14.1421L20 5.85786V14.1421L14.1421 20H5.85786L0 14.1421V5.85786L5.85786 0ZM9.99969 15.0778C9.3904 15.0778 8.88403 14.5752 8.88403 13.9705C8.88403 13.3657 9.3904 12.8632 9.99969 12.8632C10.609 12.8632 11.1154 13.3657 11.0907 13.9991C11.1195 14.5752 10.5843 15.0778 9.99969 15.0778ZM10.9404 9.20394C10.9661 8.77597 10.9919 8.34801 11.0166 7.91902C11.0288 7.64596 11.0481 7.37191 11.0674 7.09685C11.0872 6.81625 11.107 6.5346 11.1195 6.25192C11.1195 6.04762 11.0948 5.87192 11.0166 5.69622C10.786 5.19363 10.2549 4.9403 9.71976 5.06697C9.18457 5.21815 8.85522 5.69622 8.85522 6.27643C8.88229 6.6064 8.90571 6.93997 8.92892 7.27038L8.93344 7.33472C8.9705 8.00279 9.00858 8.66473 9.04666 9.32667C9.08474 9.9886 9.12282 10.6505 9.15987 11.3186C9.18869 11.7721 9.54274 12.099 9.9997 12.099C10.4567 12.099 10.8148 11.7476 10.8395 11.2941C10.8395 11.0203 10.8395 10.767 10.8642 10.4891C10.8889 10.0601 10.9147 9.63199 10.9404 9.20394Z" fill="#E23636"/>
+</svg>

ADD
FrontEnd/Sailfish-JS-report/resources/icons/report-summary-warning.svg
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6002 14.6641L12.5933 2.28388C11.4489 0.238707 8.55474 0.238707 7.41438 2.28388L0.403419 14.6641C-0.741059 16.7093 0.683364 19.25 2.9929 19.25H16.986C19.2955 19.25 20.7447 16.6841 19.6002 14.6641ZM9.99969 16.5033C9.3904 16.5033 8.88403 15.9868 8.88403 15.3653C8.88403 14.7437 9.3904 14.2272 9.99969 14.2272C10.609 14.2272 11.1154 14.7437 11.0907 15.3947C11.1195 15.9868 10.5843 16.5033 9.99969 16.5033ZM10.9404 10.4663C10.9661 10.0265 10.9919 9.58665 11.0166 9.14575C11.0288 8.86511 11.0481 8.58344 11.0674 8.30074C11.0872 8.01234 11.107 7.72288 11.1195 7.43234C11.1195 7.22236 11.0948 7.04178 11.0166 6.8612C10.786 6.34466 10.2549 6.08429 9.71976 6.21447C9.18457 6.36986 8.85522 6.8612 8.85522 7.45754C8.88227 7.79653 8.9057 8.13923 8.9289 8.47868L8.93344 8.54521C8.9705 9.23184 9.00858 9.91216 9.04666 10.5925C9.08474 11.2728 9.12282 11.9531 9.15987 12.6398C9.18869 13.1059 9.54274 13.4419 9.9997 13.4419C10.4567 13.4419 10.8148 13.0807 10.8395 12.6146C10.8395 12.3332 10.8395 12.0728 10.8642 11.7873C10.8889 11.3463 10.9147 10.9063 10.9404 10.4663Z" fill="#FF9966"/>
+</svg>

MODIFY
FrontEnd/Sailfish-JS-report/src/components/AlertCard.tsx
FrontEnd/Sailfish-JS-report/src/components/AlertCard.tsx
@@ -31,14 +31,21 @@ export const AlertCard = ({
const rootClassName = createBemBlock(
"alert",
type,
- ), typeClassName = createBemElement(
+ ), iconClassName = createBemElement(
'alert',
- 'type',
+ 'icon',
type
);
return (
<div className={rootClassName}>
+ <div className="alert__row">
+ <div className="alert__message">
+ {message}
+ </div>
+ <div className={iconClassName}/>
+ </div>
+ <div className="alert__row">
<div className="alert__column">
<span className="alert__column-title">column:</span>
<span className="alert__column-value">{column}</span>
@@ -47,11 +54,6 @@ export const AlertCard = ({
<span className="alert__column-title">lines:</span>
<span className="alert__lines-value">{lines}</span>
</div>
- <div className={typeClassName}>
- {type}
- </div>
- <div className="alert__message">
- {message}
</div>
</div>
)

MODIFY
FrontEnd/Sailfish-JS-report/src/components/ReportLayout.tsx
FrontEnd/Sailfish-JS-report/src/components/ReportLayout.tsx
import * as React from 'react';
import { connect } from 'react-redux';
import AppState from '../state/models/AppState';
-import { getSecondsPeriod, formatTime } from '../helpers/date';
-import { TestCaseMetadata, isTestCaseMetadata } from '../models/TestcaseMetadata';
+import { getSecondsPeriod } from '../helpers/date';
+import { isTestCaseMetadata, TestCaseMetadata } from '../models/TestcaseMetadata';
import "../styles/report.scss";
-import { StatusType, statusValues } from '../models/Status';
+import { StatusType } from '../models/Status';
import HeatmapScrollbar from './heatmap/HeatmapScrollbar';
import { testCasesHeatmap } from '../helpers/heatmapCreator';
import { createStyleSelector, createBemElement } from '../helpers/styleCreators';
@@ -28,7 +28,9 @@ import { ExceptionChain } from './ExceptionChain';
import TestCaseCard from './TestCaseCard';
import { FailedTestCaseCarousel } from './FailedTestCaseCarousel';
import ReportState from '../state/models/ReportState';
-import AlertCard from './AlertCard';
+import { ToggleButton } from './ToggleButton';
+import RunInformation from './RunInformation';
+import ReportSummary from './ReportSummary';
import Tag from './Tag';
const OLD_REPORT_PATH = 'report.html';
@@ -40,8 +42,11 @@ interface StateProps {
interface Props extends StateProps { }
+enum Panel {ReportSummary, RunInfo}
+
interface State {
showKnownBugs: boolean;
+ panel: Panel;
}
export class ReportLayoutBase extends React.Component<Props, State> {
@@ -50,7 +55,8 @@ export class ReportLayoutBase extends React.Component<Props, State> {
super(props);
this.state = {
- showKnownBugs: true
+ showKnownBugs: true,
+ panel: Panel.ReportSummary
};
}
@@ -60,7 +66,25 @@ export class ReportLayoutBase extends React.Component<Props, State> {
})
}
+ getPanel(): React.ReactNode {
+ return this.state.panel === Panel.RunInfo &&
+ <RunInformation report={this.props.report} /> ||
+ this.state.panel === Panel.ReportSummary &&
+ <ReportSummary report={this.props.report} />;
+ }
+
+ changePanel(panel: Panel) {
+ this.setState({
+ panel: panel
+ })
+ }
+
render() {
+ const changePanel= (panel: Panel) => {
+ return () => {
+ this.changePanel(panel)
+ }
+ }
const { report, selectedTestCaseId } = this.props,
{ showKnownBugs } = this.state;
@@ -70,9 +94,7 @@ export class ReportLayoutBase extends React.Component<Props, State> {
knownBugsClass = showKnownBugs ? "active" : "enabled",
failedTestCasesEnabled = filteredMetadata.some(({status}) => status.status === StatusType.FAILED),
failedTcTitleClass = createBemElement('report', 'title', failedTestCasesEnabled ? 'failed': 'disabled'),
- isLive = report.finishTime == null,
- alerts = report.alerts || [],
- tags = report.tags || [];
+ isLive = report.finishTime == null;
const knownBugsButton = (
knownBugsPresent ?
@@ -89,9 +111,6 @@ export class ReportLayoutBase extends React.Component<Props, State> {
)
);
- const executionTime = getSecondsPeriod(report.startTime, report.finishTime),
- plugins = report.plugins ? Object.entries(report.plugins) : [];
-
return (
<div className="report">
<div className="report__header report-header">
@@ -120,80 +139,29 @@ export class ReportLayoutBase extends React.Component<Props, State> {
<div className="report__summary">
<div className="report-summary">
<div className="report-summary__card">
- <div className="report-summary__logo"/>
<div className="report-summary__element">
+ <div className="report-summary__logo"/>
+ <div className="report-summary__vertical_element">
<div className="report-summary__element-title">Version</div>
<div className="report-summary__element-value">{report.version}</div>
</div>
- <div className="report-summary__divider"/>
- <div className="report-summary__element">
- <div className="report-summary__element-title">Host</div>
- <div className="report-summary__element-value">{report.hostName}</div>
- </div>
- <div className="report-summary__element">
- <div className="report-summary__element-title">User</div>
- <div className="report-summary__element-value">{report.userName}</div>
- </div>
- <div className="report-summary__divider"/>
- <div className="report-summary__element">
- <div className="report-summary__element-title">ScriptRun ID</div>
- <div className="report-summary__element-value">{report.scriptRunId}</div>
- </div>
- <div className="report-summary__element">
- <div className="report-summary__element-title">Report Date</div>
- <div className="report-summary__element-value">{formatTime(report.startTime)}</div>
- </div>
- <div className="report-summary__element">
- <div className="report-summary__element-title">Execution time</div>
- <div className="report-summary__element-value">{executionTime}</div>
- </div>
- <div className="report-summary__divider"/>
- <div className="report-summary__element">
- <div className="report-summary__element-title">Test Cases</div>
- <div className="report-summary__element-value">{report.metadata.length}</div>
- </div>
- {
- statusValues.map(statusValue => this.renderStatusInfo(statusValue, filteredMetadata))
- }
- <div className="report-summary__divider"/>
- {
- plugins.length ?
- (
- <div className="report-summary__element">
- <div className="report-summary__element-title">Plugins</div>
- <div className="report-summary__element-value">
- {plugins.map(([name, version], index) => <p key={index}>{name}: {version}</p>)}
- </div>
</div>
- ) : (
- <div className="report-summary__element">
- <div className="report-summary__element-title">No plugins</div>
+ <div className="layout-panel__tabs">
+ <ToggleButton
+ textClass="report-summary__button_text"
+ isToggled={this.state.panel == Panel.ReportSummary}
+ onClick={changePanel(Panel.ReportSummary)}
+ text="Report Summary"/>
+ <ToggleButton
+ textClass="report-summary__button_text"
+ isToggled={this.state.panel == Panel.RunInfo}
+ onClick={changePanel(Panel.RunInfo)}
+ text="Run Information"/>
</div>
- )
- }
- <div className="report-summary__divider"/>
{
- tags.length ?
- (
- <div className="report-summary__tags">
- <div className="report-summary__element report-summary__tags-header">
- <div className="report-summary__element-title">Tags</div>
- <div className="report-summary__element-value">{tags.length}</div>
- </div>
- <div className="report-summary__tags-list">
- {tags.map((tag, i) => <Tag tag={tag} key={i}/>)}
- </div>
- </div>
- ) : (
- <div className="report-summary__element">
- <div className="report-summary__element-title">No tags</div>
- </div>
- )
+ this.getPanel()
}
</div>
- <div className="report-summary__alerts">
- {alerts.map((alert, i) => <AlertCard {...alert} key={i}/>)}
- </div>
</div>
</div>
<div className="report__testcases">
@@ -222,26 +190,6 @@ export class ReportLayoutBase extends React.Component<Props, State> {
</div>
)
}
-
- private renderStatusInfo(status: StatusType, metadata: TestCaseMetadata[]): React.ReactNode {
- const testCasesCount = metadata.filter(metadata => metadata.status.status == status).length,
- valueClassName = createStyleSelector(
- "report-summary__element-value",
- "bold",
- status.toLowerCase()
- );
-
- if (!testCasesCount) {
- return null;
- }
-
- return (
- <div className="report-summary__element" key={status}>
- <div className={valueClassName}>{status.toUpperCase()}</div>
- <div className={valueClassName}>{testCasesCount}</div>
- </div>
- )
- }
}
const ReportLayout = connect(

ADD
FrontEnd/Sailfish-JS-report/src/components/ReportSummary.tsx
+/******************************************************************************
+* Copyright 2009-2020 Exactpro (Exactpro Systems Limited)
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+******************************************************************************/
+import React from 'react';
+import ReportState from '../state/models/ReportState';
+import AlertCard from './AlertCard';
+import Tag from './Tag';
+import { statusValues, StatusType } from '../models/Status';
+import { TestCaseMetadata, isTestCaseMetadata } from '../models/TestcaseMetadata';
+import { createStyleSelector } from '../helpers/styleCreators';
+
+export interface ReportSummaryProps {
+ report: ReportState;
+}
+
+function renderStatusInfo(status: StatusType, metadata: TestCaseMetadata[]): React.ReactNode {
+ const testCasesCount = metadata.filter(metadata => metadata.status.status == status).length,
+ valueClassName = createStyleSelector(
+ "report-summary__element-value",
+ "bold",
+ status
+ );
+
+ if (!testCasesCount) {
+ return null;
+ }
+
+ return (
+ <div className="report-summary__element" key={status}>
+ <div className={valueClassName}>{status.toUpperCase()}</div>
+ <div className={valueClassName}>{testCasesCount}</div>
+ </div>
+ )
+}
+
+const ReportSummary = ({ report }: ReportSummaryProps) => {
+ const filteredMetadata = report.metadata.filter(isTestCaseMetadata);
+ const tags = report.tags || [];
+ const alerts = report.alerts || [];
+ return (
+ <React.Fragment>
+ <div className="report-summary__block">
+ <div className="report-summary__element">
+ <div className="report-summary__element-title">Test Cases</div>
+ <div className="report-summary__element-value">{report.metadata.length}</div>
+ </div>
+ {
+ statusValues.map(statusValue => renderStatusInfo(statusValue, filteredMetadata))
+ }
+ </div>
+ <div className="report-summary__divider"/>
+ {
+ tags.length ?
+ (
+ <div className="report-summary__tags">
+ <div className="report-summary__element report-summary__tags-header">
+ <div className="report-summary__element-title">Tags</div>
+ <div className="report-summary__element-value">{tags.length}</div>
+ </div>
+ <div className="report-summary__tags-list">
+ {tags.map(tag => <Tag tag={tag}/>)}
+ </div>
+ </div>
+ ) : (
+ <div className="report-summary__element">
+ <div className="report-summary__element-title">No tags</div>
+ </div>
+ )
+ }
+ <div className="report-summary__divider"/>
+ <div className="report-summary__element">
+ <div className="report-summary__element-title">Errors and Warnings</div>
+ <div className="report-summary__element-value">{alerts.length}</div>
+ </div>
+ <div className="report-summary__alerts">
+ {alerts.map((alert, i) => <AlertCard {...alert} key={i}/>)}
+ </div>
+ </React.Fragment>)
+}
+
+export default ReportSummary;
\ No newline at end of file

ADD
FrontEnd/Sailfish-JS-report/src/components/RunInformation.tsx
+/******************************************************************************
+* Copyright 2009-2020 Exactpro (Exactpro Systems Limited)
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+******************************************************************************/
+import React from 'react';
+import ReportState from '../state/models/ReportState';
+import { formatTime, getSecondsPeriod } from '../helpers/date';
+
+export interface RunInformationProps {
+ report: ReportState;
+}
+
+const RunInformation = ({ report }: RunInformationProps) => {
+ const executionTime = getSecondsPeriod(report.startTime, report.finishTime);
+ const plugins = report.plugins ? Object.entries(report.plugins) : [];
+ return (<React.Fragment>
+ <div className="report-summary__block">
+ <div className="run-information__element">
+ <div className="run-information__element-title">Host</div>
+ <div className="run-information__element-value">{report.hostName}</div>
+ </div>
+ <div className="run-information__element">
+ <div className="run-information__element-title">User</div>
+ <div className="run-information__element-value">{report.userName}</div>
+ </div>
+ </div>
+ <div className="report-summary__divider"/>
+ <div className="report-summary__block">
+ <div className="run-information__element">
+ <div className="run-information__element-title">ScriptRun ID</div>
+ <div className="run-information__element-value">{report.scriptRunId}</div>
+ </div>
+ <div className="run-information__element">
+ <div className="run-information__element-title">Report Date</div>
+ <div className="run-information__element-value">{formatTime(report.startTime)}</div>
+ </div>
+ <div className="run-information__element">
+ <div className="run-information__element-title">Execution time</div>
+ <div className="run-information__element-value">{executionTime}</div>
+ </div>
+ </div>
+ <div className="report-summary__divider"/>
+ {
+ plugins.length ?
+ (
+ <div className="report-summary__element">
+ <div className="report-summary__element-title">Plugins</div>
+ <div className="report-summary__element-value">
+ {plugins.map(([name, version], index) => <p key={index}>{name}: {version}</p>)}
+ </div>
+ </div>
+ ) : (
+ <div className="report-summary__element">
+ <div className="report-summary__element-title">No plugins</div>
+ </div>
+ )
+ }
+ </React.Fragment>)
+}
+
+export default RunInformation;
\ No newline at end of file

MODIFY
FrontEnd/Sailfish-JS-report/src/components/ToggleButton.tsx
FrontEnd/Sailfish-JS-report/src/components/ToggleButton.tsx
@@ -25,9 +25,10 @@ interface Props {
text: string;
title?: string;
theme?: string;
+ textClass?: string;
}
-export function ToggleButton({ onClick = () => {}, isToggled = false, text, theme = 'default', isDisabled = false, title = '' }: Props) {
+export function ToggleButton({ onClick = () => {}, isToggled = false, text, textClass, theme = 'default', isDisabled = false, title = '' }: Props) {
const className = createStyleSelector(
"toggle-button",
theme,
@@ -38,7 +39,7 @@ export function ToggleButton({ onClick = () => {}, isToggled = false, text, them
return (
<div className={className} onClick={() => !isDisabled && onClick(text)} title={title}>
<div className="toggle-button__title">
- <p>{text}</p>
+ <p className={textClass}>{text}</p>
</div>
</div>
)

MODIFY
FrontEnd/Sailfish-JS-report/src/styles/alert.scss
FrontEnd/Sailfish-JS-report/src/styles/alert.scss
******************************************************************************/
@import "./common/vars";
+@import "./common/mixins";
.alert {
- border: 2px solid;
- padding: 7px 15px;
+ border-top: 2px solid;
font-size: 13px;
margin-bottom: 6px;
- border-radius: 5px;
- display: grid;
- grid-template-columns: 1fr 100px;
- grid-template-rows: 1fr 1fr;
- grid-template-areas: "column type"
- "lines type"
- "message message";
+ min-height: 65px;
+ display: flex;
+ flex-direction: column;
+ padding: 0;
+
+ &__row {
+ margin-top: 10px;
+ justify-content: space-between;
+ display: flex;
+ }
&__column {
- grid-area: column;
+ width: 60%;
&-title {
color: $secondaryTextColor;
}
}
&__lines {
- grid-area: lines;
+ text-align: end;
+ width: 40%;
&-title {
color: $secondaryTextColor;
}
}
&__type {
- grid-area: type;
text-align: right;
font-weight: bold;
font-size: 15px;
}
&__message {
- grid-area: message;
+ width: 70%;
+ margin-right: 0px;
}
&.warning {
border-color: $failedBorderColorSecondary;
color: $failedSecondaryTextColor;
}
+
+ &__icon {
+ &.error {
+ @include icon(url(../../resources/icons/report-summary-error.svg), 20px, 20px);
+ }
+ &.warning {
+ @include icon(url(../../resources/icons/report-summary-warning.svg), 20px, 20px);
+ }
+ }
}
\ No newline at end of file

MODIFY
FrontEnd/Sailfish-JS-report/src/styles/report.scss
FrontEnd/Sailfish-JS-report/src/styles/report.scss
border-radius: 5px;
padding: 15px;
margin-bottom: 12px;
+ height: 100%;
}
&__logo {
justify-content: space-between;
}
+ &__vertical_element {
+ width: 100%;
+ padding: 15px 0;
+ display: flex;
+ align-items: flex-end;
+ flex-direction: column;
+ }
+
+ &__button_text {
+ font-family: Open Sans;
+ font-style: normal;
+ font-weight: 600;
+ font-size: 12px;
+ line-height: 22px;
+ white-space: nowrap;
+
+ display: flex;
+ align-items: center;
+ text-align: center
+ }
+
&__element-title {
font-size: 13px;
color: $secondaryTextColor;
&__tags-header {
margin-bottom: 5px;
}
+
+ &__block {
+ margin-top: 30px;
+ }
+
+ &__alerts {
+ margin-top: 10px;
+ }
+
+
+}
+
+.run-information {
+
+ &__element {
+ width: 100%;
+ padding: 5px 0;
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ }
+
+ &__element-title {
+ font-size: 13px;
+ color: $secondaryTextColor;
+ }
+
+ &__element-value {
+ @include status-text;
+ font-size: 15px;
+ color: $primaryTextColor;
+
+ &.bold {
+ font-weight: bold;
+ }
+ }
}
.tc-card {

Feature #62433.  Change report summary layout

Change-Id: I6ae2dd440ea2d8390fc0a0699c0f54ca66023c2f