Skip to content
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
29 changes: 23 additions & 6 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
{
"extends": [
"eslint:recommended",
"plugin:jsx-a11y/recommended",
"plugin:react/recommended",
"plugin:prettier/recommended",
"plugin:sonarjs/recommended",
"plugin:security/recommended",
"plugin:react-hooks/recommended",
"xo",
"xo-react",
"prettier",
"prettier/react",
"plugin:react-hooks/recommended"
"prettier/react"
],
"parser": "babel-eslint",
"rules": {
Expand Down Expand Up @@ -41,13 +46,25 @@
"no-multiple-empty-lines": "error",
"prefer-const": "error",
"no-use-before-define": "error",
"prettier/prettier": "error"
"prettier/prettier": "error",
"comma-dangle": "off",
"indent": "off",
"object-curly-spacing": "off",
"no-tabs": ["error", { "allowIndentationTabs": true }],
"operator-linebreak": "off",
"no-else-return": "off",
"react/no-array-index-key": "off",
"jsx-a11y/click-events-have-key-events": "off",
"sonarjs/no-duplicate-string": "off",
"jsx-a11y/no-static-element-interactions": "off"
},
"plugins": ["prettier"],
"parserOptions": {
"sourceType": "module",
"allowImportExportEverywhere": false,
"codeFrame": true
"ecmaVersion": 2020, // Allows for the parsing of modern ECMAScript features
"sourceType": "module", // Allows for the use of imports
"ecmaFeatures": {
"jsx": true // Allows for the parsing of JSX
}
},
"env": {
"browser": true,
Expand Down
15 changes: 10 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,17 @@
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.0.1",
"eslint": "^7.4.0",
"eslint-config-prettier": "^7.1.0",
"eslint-loader": "^4.0.2",
"eslint-config-prettier": "^7.2.0",
"eslint-config-xo": "^0.35.0",
"eslint-config-xo-react": "^0.23.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.20.3",
"eslint-plugin-react-hooks": "^4.0.8",
"html-loader": "^1.1.0",
"eslint-plugin-security": "^1.4.0",
"eslint-plugin-sonarjs": "^0.6.0",
"eslint-webpack-plugin": "^2.4.0",
"html-loader": "^2.0.0",
"html-webpack-plugin": "4",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.1.0",
Expand All @@ -91,8 +96,8 @@
"ts-loader": "^8.0.4",
"typescript": "^4.1.2",
"webpack": "^5.6.0",
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^3.11.0",
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.0.9"
}
}
62 changes: 42 additions & 20 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class App extends Component {
constructor(props) {
data = window.resultData;
super(props);
// eslint-disable-next-line react/state-in-constructor
this.state = {
menuState: 'close',
testResults: data,
Expand All @@ -21,8 +22,10 @@ class App extends Component {
this.state.gridData = this.state.treeViewData;
this.menuStateChange = this.menuStateChange.bind(this);
this.onStatusChecked = this.onStatusChecked.bind(this);
this.state.toggleState = this.state.testResults?.reporterOptions?.expandResults;
this.state.isResultExpanded = this.state.testResults?.reporterOptions?.expandResults;
this.onExpandToggle = this.onExpandToggle.bind(this);
}

getStatusList() {
return statusList;
}
Expand Down Expand Up @@ -50,10 +53,12 @@ class App extends Component {
statusFilter,
);
}

treeViewData = rootElement;
return treeViewData;
}

// eslint-disable-next-line sonarjs/cognitive-complexity
parseTreeData(testResults, parentArray, id, statusFilter) {
let subArray = [];
testResults.forEach(element => {
Expand Down Expand Up @@ -90,6 +95,7 @@ class App extends Component {
if (statusList.indexOf(element.status) < 0) {
statusList.push(element.status);
}

if (
statusFilter.length === 0 ||
statusFilter.indexOf(element.status) >= 0
Expand All @@ -108,9 +114,11 @@ class App extends Component {
if (subArray.length > 0) {
parentArray = subArray;
}

return [parentArray, id];
}

// eslint-disable-next-line max-params, sonarjs/cognitive-complexity
parseAncestor(ancestors, testCase, parentArray, id, statusFilter) {
const ancestorCopy = [...ancestors];
if (ancestors.length > 0) {
Expand All @@ -120,15 +128,16 @@ class App extends Component {
});

if (elementIndex === -1) {
const nodeValue = {};
nodeValue.title = itemTitle;
nodeValue.numPassedTests = testCase.status === 'passed' ? 1 : 0;
nodeValue.numFailedTests = testCase.status === 'failed' ? 1 : 0;
nodeValue.numTotalTests = 1;
nodeValue.numPendingTests =
testCase.status === 'pending' ? 1 : 0;
nodeValue.numTodoTests = testCase.status === 'todo' ? 1 : 0;
nodeValue.id = `id${id}`;
const nodeValue = {
title: itemTitle,
numPassedTests: testCase.status === 'passed' ? 1 : 0,
numFailedTests: testCase.status === 'failed' ? 1 : 0,
numTotalTests: 1,
numPendingTests: testCase.status === 'pending' ? 1 : 0,
numTodoTests: testCase.status === 'todo' ? 1 : 0,
id: `id${id}`,
};

id++;
ancestorCopy.shift();
[nodeValue.children, id] = this.parseAncestor(
Expand All @@ -141,26 +150,34 @@ class App extends Component {
parentArray.push(nodeValue);
} else {
ancestorCopy.shift();
// eslint-disable-next-line security/detect-object-injection
parentArray[elementIndex].numTotalTests++;
switch (testCase.status) {
case 'passed':
// eslint-disable-next-line security/detect-object-injection
parentArray[elementIndex].numPassedTests++;
break;
case 'failed':
// eslint-disable-next-line security/detect-object-injection
parentArray[elementIndex].numFailedTests++;
break;
case 'pending':
// eslint-disable-next-line security/detect-object-injection
parentArray[elementIndex].numPendingTests++;
break;
case 'todo':
// eslint-disable-next-line security/detect-object-injection
parentArray[elementIndex].numTodoTests++;
break;
default:
break;
}

// eslint-disable-next-line security/detect-object-injection
[parentArray[elementIndex].children, id] = this.parseAncestor(
ancestorCopy,
testCase,
// eslint-disable-next-line security/detect-object-injection
parentArray[elementIndex].children,
id,
statusFilter,
Expand All @@ -173,6 +190,7 @@ class App extends Component {
id++;
parentArray.push(nodeValue);
}

return [parentArray, id];
}

Expand Down Expand Up @@ -203,17 +221,19 @@ class App extends Component {
});
information.push({
title: 'Elapsed',
value: data?.endTime - data?.startTime,
value: data.endTime - data.startTime,
type: 'time',
});
}

if (data?.openHandles && data.openHandles.length > 0) {
information.push({
title: 'Open Handles',
value: data?.openHandles?.length,
type: 'number',
});
}

information.push({
title: 'Interupted',
value: data?.wasInterrupted,
Expand All @@ -223,37 +243,39 @@ class App extends Component {
}

onStatusChecked = checkedStatuses => {
this.setState({
this.setState(prevState => ({
gridData: this.formatTreeViewData(
this.state.testResults,
prevState.testResults,
checkedStatuses,
),
});
}));
};

onExpandToggle = toggleState => {
this.setState({ toggleState: toggleState });
onExpandToggle = isResultExpanded => {
this.setState({ isResultExpanded: isResultExpanded });
};

render() {
return (
<div className="App">
<Header
hideMenu={this.state.testResults?.reporterOptions?.hideMenu}
isMenuHidden={
this.state.testResults?.reporterOptions?.hideMenu
}
heading={this.state.testResults?.reporterOptions?.title}
menuStateChange={this.menuStateChange}
/>
<Sidebar
treeViewData={this.state.treeViewData}
menuState={this.state.menuState}
onTreeNodeClick={this.onTreeNodeClick}
expandMenuItems={
isMenuExpanded={
this.state.testResults?.reporterOptions?.expandMenuItems
}
onTreeNodeClick={this.onTreeNodeClick}
/>
<Main
testResults={this.state.gridData}
expandResults={this.state.toggleState}
isResultExpanded={this.state.isResultExpanded}
information={this.state.information}
statusList={this.getStatusList()}
onStatusChecked={this.onStatusChecked}
Expand Down
29 changes: 20 additions & 9 deletions src/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const sampleData = require('./data/sample.json');
describe('Page Information', () => {
test('Should contain minimal information', () => {
window.resultData = data;
const { container } = render(<App></App>);
const { container } = render(<App />);
expect(
container.querySelector('.infoWrapper').childNodes.length,
).toEqual(4);
Expand All @@ -18,15 +18,15 @@ describe('Page Information', () => {
};
infoData.reporterOptions = reporterOptions;
window.resultData = infoData;
const { container } = render(<App></App>);
const { container } = render(<App />);
expect(
container.querySelector('.infoWrapper').childNodes.length,
).toEqual(6);
});
test('Should contain project level information', () => {
const infoData = { ...sampleData };
window.resultData = infoData;
const { container } = render(<App></App>);
const { container } = render(<App />);
expect(
container.querySelector('.infoWrapper').childNodes.length,
).toEqual(8);
Expand All @@ -35,7 +35,7 @@ describe('Page Information', () => {
const infoData = { ...sampleData };
infoData.openHandles = ['handle'];
window.resultData = infoData;
const { container } = render(<App></App>);
const { container } = render(<App />);
expect(
container.querySelector('.infoWrapper').childNodes.length,
).toEqual(10);
Expand All @@ -45,7 +45,7 @@ describe('Page Information', () => {
describe('Menu click', () => {
test('Should show and hide tree on click', () => {
window.resultData = data;
const { container } = render(<App></App>);
const { container } = render(<App />);
fireEvent.click(container.querySelector('#menu'));
expect(
container.querySelector('.sidenav').classList.contains('open'),
Expand All @@ -60,7 +60,7 @@ describe('Menu click', () => {
describe('Tree click', () => {
test('Should call function on tree node click', () => {
window.resultData = data;
const { container } = render(<App></App>);
const { container } = render(<App />);
const date = container.querySelector('.box2');
date.textContent = '';
fireEvent.click(container.querySelector('#menu'));
Expand All @@ -79,12 +79,12 @@ describe('Tree click', () => {
describe('Status filter', () => {
test('Should have checkboxes', () => {
window.resultData = sampleData;
const { container } = render(<App></App>);
const { container } = render(<App />);
expect(container.querySelectorAll('.checkboxLabel').length).toEqual(4);
});
test('Should filter on checkbox click', () => {
window.resultData = sampleData;
const { container } = render(<App></App>);
const { container } = render(<App />);
const statusCheckboxes = container.querySelectorAll('.checkboxLabel');
fireEvent.click(statusCheckboxes[3]);
expect(container.querySelectorAll('.tab-content').length).toEqual(2);
Expand All @@ -95,7 +95,7 @@ describe('Status filter', () => {
describe('Toggle Button', () => {
test('Should fire event', () => {
window.resultData = data;
const { container } = render(<App></App>);
const { container } = render(<App />);
expect(
container.querySelectorAll('.togglerCheckBox:checked'),
).toHaveLength(0);
Expand All @@ -104,4 +104,15 @@ describe('Toggle Button', () => {
container.querySelectorAll('.togglerCheckBox:checked'),
).toHaveLength(12);
});

test('Should use default expand results flag', () => {
const copiedData = JSON.parse(JSON.stringify(data));
copiedData.reporterOptions = {};
copiedData.reporterOptions.expandResults = true;
window.resultData = copiedData;
const { container } = render(<App />);
expect(
container.querySelectorAll('.togglerCheckBox:checked'),
).toHaveLength(12);
});
});
5 changes: 3 additions & 2 deletions src/Components/FilterToggler/CheckBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ export const CheckBox = props => {
{props.value}
<input
key={props.value}
onChange={props.handleCheck}
type="checkbox"
checked={props.isChecked}
value={props.value}
id={props.value}
onChange={props.handleCheck}
/>
<span className="checkboxSpan"></span>
<span className="checkboxSpan" />
</label>
);
};

CheckBox.propTypes = {
handleCheck: PropTypes.func.isRequired,
isChecked: PropTypes.bool,
Expand Down
Loading