diff --git a/package.json b/package.json
index 19ba902..30339de 100644
--- a/package.json
+++ b/package.json
@@ -17,10 +17,10 @@
],
"author": "Vinoth",
"license": "ISC",
-"repository": {
- "type": "git",
- "url": "https://github.com/vinothdevelop/HTMLReport4Jest"
- },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/vinothdevelop/HTMLReport4Jest"
+ },
"bugs": {
"url": "https://github.com/vinothdevelop/HTMLReport4Jest/issues"
},
diff --git a/src/App.js b/src/App.js
index 9d49747..6b7e0d8 100644
--- a/src/App.js
+++ b/src/App.js
@@ -6,6 +6,7 @@ import Header from './Components/Header/Header';
import Sidebar from './Components/Sidebar/Sidebar';
import Main from './Components/Main/Main';
let data;
+const statusList = [];
class App extends Component {
constructor(props) {
data = window.resultData;
@@ -13,14 +14,19 @@ class App extends Component {
this.state = {
menuState: 'close',
testResults: data,
- treeViewData: this.formatTreeViewData(data),
+ treeViewData: this.formatTreeViewData(data, []),
information: this.getInformation(data),
};
this.onTreeNodeClick = this.onTreeNodeClick.bind(this);
this.state.gridData = this.state.treeViewData;
this.menuStateChange = this.menuStateChange.bind(this);
+ this.onStatusChecked = this.onStatusChecked.bind(this);
}
- formatTreeViewData(testResults) {
+ getStatusList() {
+ return statusList;
+ }
+
+ formatTreeViewData(testResults, statusFilter) {
const testResultData = {};
Object.assign(testResultData, testResults);
let treeViewData = [];
@@ -40,13 +46,14 @@ class App extends Component {
testResultData.testResults,
[],
id,
+ statusFilter,
);
}
treeViewData = rootElement;
return treeViewData;
}
- parseTreeData(testResults, parentArray, id) {
+ parseTreeData(testResults, parentArray, id, statusFilter) {
let subArray = [];
testResults.forEach(element => {
if (element.testFilePath || element.fullName) {
@@ -70,15 +77,30 @@ class App extends Component {
element.testResults,
[],
id,
+ statusFilter,
);
- parentArray.push(nodeValue);
+ if (
+ statusFilter.length === 0 ||
+ nodeValue.children.length > 0
+ ) {
+ parentArray.push(nodeValue);
+ }
} else if (element.ancestorTitles) {
- [subArray, id] = this.parseAncestor(
- element.ancestorTitles,
- element,
- subArray,
- id,
- );
+ if (statusList.indexOf(element.status) < 0) {
+ statusList.push(element.status);
+ }
+ if (
+ statusFilter.length === 0 ||
+ statusFilter.indexOf(element.status) >= 0
+ ) {
+ [subArray, id] = this.parseAncestor(
+ element.ancestorTitles,
+ element,
+ subArray,
+ id,
+ statusFilter,
+ );
+ }
}
}
});
@@ -88,7 +110,7 @@ class App extends Component {
return [parentArray, id];
}
- parseAncestor(ancestors, testCase, parentArray, id) {
+ parseAncestor(ancestors, testCase, parentArray, id, statusFilter) {
const ancestorCopy = [...ancestors];
if (ancestors.length > 0) {
const itemTitle = ancestors[0];
@@ -113,6 +135,7 @@ class App extends Component {
testCase,
[],
id,
+ statusFilter,
);
parentArray.push(nodeValue);
} else {
@@ -139,6 +162,7 @@ class App extends Component {
testCase,
parentArray[elementIndex].children,
id,
+ statusFilter,
);
}
} else {
@@ -197,6 +221,15 @@ class App extends Component {
return information;
}
+ onStatusChecked = checkedStatuses => {
+ this.setState({
+ gridData: this.formatTreeViewData(
+ this.state.testResults,
+ checkedStatuses,
+ ),
+ });
+ };
+
render() {
return (
@@ -219,6 +252,8 @@ class App extends Component {
this.state.testResults?.reporterOptions?.expandResults
}
information={this.state.information}
+ statusList={this.getStatusList()}
+ onStatusChecked={this.onStatusChecked}
/>
);
diff --git a/src/App.test.js b/src/App.test.js
index 4740815..5af7ac5 100644
--- a/src/App.test.js
+++ b/src/App.test.js
@@ -73,3 +73,20 @@ describe('Tree click', () => {
expect(container.querySelector('.main')).toMatchSnapshot();
});
});
+
+describe('Status filter', () => {
+ test('Should have checkboxes', () => {
+ window.resultData = sampleData;
+ const { container } = render();
+ expect(container.querySelectorAll('.checkboxLabel').length).toEqual(4);
+ });
+ test('Should filter on checkbox click', () => {
+ window.resultData = sampleData;
+ const { container } = render();
+ const statusCheckboxes = container.querySelectorAll('.checkboxLabel');
+ fireEvent.click(statusCheckboxes[3]);
+ expect(container.querySelectorAll('.tab-content').length).toEqual(2);
+ fireEvent.click(statusCheckboxes[3]);
+ expect(container.querySelectorAll('.tab-content').length).toEqual(40);
+ });
+});
diff --git a/src/Components/FilterToggler/CheckBox.css b/src/Components/FilterToggler/CheckBox.css
new file mode 100644
index 0000000..20b6931
--- /dev/null
+++ b/src/Components/FilterToggler/CheckBox.css
@@ -0,0 +1,51 @@
+.checkboxLabel {
+ text-transform: capitalize;
+ position: relative;
+ padding-left: 35px;
+ margin-bottom: 5px;
+ cursor: pointer;
+ display: 'inline';
+}
+
+/* Hide the default checkbox */
+.checkboxLabel input[type='checkbox'] {
+ visibility: hidden;
+}
+
+/* creating a custom checkbox based
+ on demand */
+.checkboxSpan {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 1em;
+ width: 1em;
+ background-color: white;
+ border: solid black;
+ border-width: 1px 1px 1px 1px;
+}
+
+/* specify the background color to be
+shown when checkbox is checked */
+.checkboxLabel input:checked ~ .checkboxSpan {
+ background-color: #333334;
+}
+
+/* checkmark to be shown in checkbox */
+/* It is not be shown when not checked */
+.checkboxSpan:after {
+ position: absolute;
+ display: none;
+}
+
+/* styling the checkmark using webkit */
+/* creating a square to be the sign of
+ checkmark */
+.checkboxLabel .checkboxSpan:after {
+ left: 6px;
+ bottom: 5px;
+ width: 6px;
+ height: 6px;
+ border: solid black;
+ border-width: 4px 4px 4px 4px;
+}
diff --git a/src/Components/FilterToggler/CheckBox.js b/src/Components/FilterToggler/CheckBox.js
new file mode 100644
index 0000000..4bfe208
--- /dev/null
+++ b/src/Components/FilterToggler/CheckBox.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import './CheckBox.css';
+export const CheckBox = props => {
+ return (
+
+ );
+};
+CheckBox.propTypes = {
+ handleCheck: PropTypes.func.isRequired,
+ isChecked: PropTypes.bool,
+ value: PropTypes.string.isRequired,
+};
+export default CheckBox;
diff --git a/src/Components/FilterToggler/CheckBox.test.js b/src/Components/FilterToggler/CheckBox.test.js
new file mode 100644
index 0000000..8182f15
--- /dev/null
+++ b/src/Components/FilterToggler/CheckBox.test.js
@@ -0,0 +1,50 @@
+import React from 'react';
+import CheckBox from './CheckBox';
+import { render, fireEvent } from '@testing-library/react';
+test('Should contain value', () => {
+ const { container } = render(
+ ,
+ );
+ expect(container).toHaveTextContent('Test');
+});
+
+test('Should not be checked', () => {
+ const { container } = render(
+ ,
+ );
+ expect(container.firstChild.lastChild.previousSibling).not.toBeChecked();
+});
+
+test('Should be checked', () => {
+ const { container } = render(
+ ,
+ );
+ expect(container.firstChild.lastChild.previousSibling).toBeChecked();
+});
+
+test('Should call function on change', () => {
+ const mockCallback = jest.fn();
+ const { container } = render(
+ ,
+ );
+ fireEvent.click(container.firstChild);
+ expect(mockCallback.mock.calls.length).toBe(1);
+ fireEvent.click(container.firstChild);
+ expect(mockCallback.mock.calls.length).toBe(2);
+});
diff --git a/src/Components/FilterToggler/FilterToggler.js b/src/Components/FilterToggler/FilterToggler.js
new file mode 100644
index 0000000..b100787
--- /dev/null
+++ b/src/Components/FilterToggler/FilterToggler.js
@@ -0,0 +1,61 @@
+import React from 'react';
+import CheckBox from './CheckBox';
+import PropTypes from 'prop-types';
+export default class FilterToggler extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ statusList: this.init(this.props.statusList),
+ };
+ }
+
+ init(statusList) {
+ if (statusList && statusList.length > 0) {
+ return statusList.map(status => {
+ return { value: status, isChecked: false };
+ });
+ } else {
+ return [];
+ }
+ }
+
+ handleChecked = event => {
+ const checkStatuses = [];
+ this.state.statusList.forEach(status => {
+ if (status.value === event.target.value) {
+ status.isChecked = event.target.checked;
+ }
+ if (status.isChecked) {
+ checkStatuses.push(status.value);
+ }
+ });
+ this.setState(this.state.statusList);
+ this.props.onStatusChecked(checkStatuses);
+ };
+ render() {
+ if (this.props.statusList && this.props.statusList.length > 0) {
+ return (
+
+
+ Filter:{' '}
+ {this.state.statusList.map(status => {
+ return (
+
+ );
+ })}
+
+
+ );
+ } else {
+ return null;
+ }
+ }
+}
+FilterToggler.propTypes = {
+ statusList: PropTypes.array,
+ onStatusChecked: PropTypes.func.isRequired,
+};
diff --git a/src/Components/FilterToggler/FilterToggler.test.js b/src/Components/FilterToggler/FilterToggler.test.js
new file mode 100644
index 0000000..9090d48
--- /dev/null
+++ b/src/Components/FilterToggler/FilterToggler.test.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import FilterToggler from './FilterToggler';
+import { render, fireEvent } from '@testing-library/react';
+test('Should contain one checkbox', () => {
+ const { container } = render(
+ ,
+ );
+ expect(container.getElementsByTagName('input').length).toEqual(1);
+});
+
+test('Should contain two checkbox', () => {
+ const { container } = render(
+ ,
+ );
+ expect(container.getElementsByTagName('input').length).toEqual(2);
+});
+
+test('Should call function on change', () => {
+ const mockCallback = jest.fn();
+ const { container } = render(
+ ,
+ );
+ fireEvent.click(container.firstChild.getElementsByTagName('input')[0]);
+ expect(mockCallback.mock.calls.length).toBe(1);
+ expect(mockCallback.mock.calls[0][0].length).toBe(1);
+ expect(mockCallback.mock.calls[0][0][0]).toBe('passed');
+ fireEvent.click(container.firstChild.getElementsByTagName('input')[0]);
+ expect(mockCallback.mock.calls.length).toBe(2);
+ expect(mockCallback.mock.calls[1][0].length).toBe(0);
+});
diff --git a/src/Components/Grid/GridTabView.css b/src/Components/Grid/GridTabView.css
index a24ad9b..937c7d3 100644
--- a/src/Components/Grid/GridTabView.css
+++ b/src/Components/Grid/GridTabView.css
@@ -1,17 +1,16 @@
-
- /* Accordion styles */
- .tabs {
+/* Accordion styles */
+.tabs {
border-radius: 8px;
box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.5);
padding: 1vh 0vw;
- }
+}
- .tab {
+.tab {
width: 100%;
color: white;
overflow: hidden;
- }
- .tab-label {
+}
+.tab-label {
display: -webkit-box;
display: flex;
-webkit-box-pack: justify;
@@ -21,21 +20,21 @@
font-weight: bold;
cursor: pointer;
color: #ffffff;
- font-size: .75rem;
+ font-size: 0.75rem;
white-space: nowrap;
- }
- .tab-label:hover {
+}
+.tab-label:hover {
background: #1a252f;
- }
- .tab-label::after {
- content: "\276F";
+}
+.tab-label::after {
+ content: '\276F';
width: 1em;
height: 1em;
text-align: center;
-webkit-transition: all 0.35s;
transition: all 0.35s;
- }
- .tab-content {
+}
+.tab-content {
padding: 3px;
color: #2c3e50;
background: white;
@@ -43,8 +42,8 @@
transition: all 0.35s;
font-size: 1rem;
display: none;
- }
- .tab-close {
+}
+.tab-close {
display: -webkit-box;
display: flex;
-webkit-box-pack: end;
@@ -53,26 +52,25 @@
font-size: 0.75em;
background: #2c3e50;
cursor: pointer;
- }
- .tab-close:hover {
+}
+.tab-close:hover {
background: #1a252f;
- }
+}
- input:checked + .tab-label {
+input.togglerCheckBox:checked + .tab-label {
background: #1a252f;
- }
- input:checked + .tab-label::after {
+}
+input.togglerCheckBox:checked + .tab-label::after {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
- }
- input:checked ~ .tab-content {
+}
+input.togglerCheckBox:checked ~ .tab-content {
max-height: fit-content;
display: block;
- }
+}
-input {
+input.togglerCheckBox {
position: absolute;
opacity: 0;
z-index: -1;
- }
-
+}
diff --git a/src/Components/Grid/Status.js b/src/Components/Grid/Status.js
index 9a0957d..5ebe13f 100644
--- a/src/Components/Grid/Status.js
+++ b/src/Components/Grid/Status.js
@@ -101,6 +101,6 @@ class Status extends Component {
}
}
Status.propTypes = {
- status: PropTypes.string.isRequired,
+ status: PropTypes.string,
};
export default Status;
diff --git a/src/Components/Grid/TabHeading.js b/src/Components/Grid/TabHeading.js
index 6632d12..27a5e66 100644
--- a/src/Components/Grid/TabHeading.js
+++ b/src/Components/Grid/TabHeading.js
@@ -29,6 +29,7 @@ class TabHeading extends Component {
id={`elem_${this.props.item.id}`}
checked={this.state.isChecked}
onChange={this.toggleChange}
+ className="togglerCheckBox"
/>