Skip to content

Commit

Permalink
Add a column to show the status (unconfirmed, confirmed, false positi… (
Browse files Browse the repository at this point in the history
#1081)

Add a column to show the status (unconfirmed, confirmed, false positive, etc.) of security warnings, violations, etc. so that the user doesn't have to expand them to see the status. Closes #1070.
  • Loading branch information
fniessink authored Mar 4, 2020
1 parent 4c536e9 commit a73c0ef
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 21 deletions.
26 changes: 15 additions & 11 deletions components/frontend/src/source/SourceEntities.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import React, { useState } from 'react';
import { Button, Popup, Table } from 'semantic-ui-react';
import { SourceEntity } from './SourceEntity';
import { capitalize } from '../utils';

export function SourceEntities(props) {
const [hideIgnoredEntities, setHideIgnoredEntities] = useState(false);
const [sortColumn, setSortColumn] = useState(null);
const [sortDirection, setSortDirection] = useState('ascending');

function sort(column) {
if (column === sortColumn) {
setSortDirection(sortDirection === "ascending" ? "descending" : "ascending")
} else {
setSortColumn(column)
}
}
function sorted(column) {
return column === sortColumn ? sortDirection : null
}

const report_source = props.metric.sources[props.source.source_uuid];
const source_type = report_source.type;
const metric_entities = props.datamodel.sources[source_type].entities[props.metric.type];
Expand All @@ -29,18 +41,10 @@ export function SourceEntities(props) {
/>
} content={hideIgnoredEntities ? `Show resolved ${entity_name_plural}` : `Hide resolved ${entity_name_plural}`} />
</Table.HeaderCell>
<Table.HeaderCell sorted={sorted("entity_status")} onClick={() => sort("entity_status")}>
{`${capitalize(entity_name)} status`}</Table.HeaderCell>
{entity_attributes.map((entity_attribute) =>
<Table.HeaderCell
key={entity_attribute.key}
sorted={sortColumn === entity_attribute.key ? sortDirection : null}
onClick={() => {
if (entity_attribute.key === sortColumn) {
setSortDirection(sortDirection === 'ascending' ? 'descending' : 'ascending')
} else {
setSortColumn(entity_attribute.key)
}
}}
>
<Table.HeaderCell key={entity_attribute.key} sorted={sorted(entity_attribute.key)} onClick={() => sort(entity_attribute.key)}>
{entity_attribute.name}
</Table.HeaderCell>)
}
Expand Down
56 changes: 56 additions & 0 deletions components/frontend/src/source/SourceEntities.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import { mount } from 'enzyme';
import { SourceEntities } from './SourceEntities';

const data_model = {
sources: {
source_type: {
entities: {
metric_type:
{
name: "entity name",
attributes: [{key: "1"}, {key: "2"}]
}
}
}
}
}

const metric = {
type: "metric_type",
sources: {
source_uuid: {
type: "source_type"
}
}
}

const source = {
source_uuid: "source_uuid",
entities: [
{
key: "1"
},
{
key: "2"
}
]
}

describe('<SourceEntities />', () => {
it('renders', () => {
const wrapper = mount(<SourceEntities datamodel={data_model} metric={metric} source={source} />);
expect(wrapper.find("TableRow").at(1).hasClass("status_unknown"))
});
it('sorts', () => {
const wrapper = mount(<SourceEntities datamodel={data_model} metric={metric} source={source} />);
wrapper.find("TableHeaderCell").at(1).simulate("click");
expect(wrapper.find("TableHeaderCell").at(1).prop("sorted")).toBe("ascending");
wrapper.find("TableHeaderCell").at(1).simulate("click");
expect(wrapper.find("TableHeaderCell").at(1).prop("sorted")).toBe("descending");
wrapper.find("TableHeaderCell").at(2).simulate("click");
expect(wrapper.find("TableHeaderCell").at(2).prop("sorted")).toBe("descending");
wrapper.find("TableHeaderCell").at(2).simulate("click");
expect(wrapper.find("TableHeaderCell").at(2).prop("sorted")).toBe("ascending");
})
});
12 changes: 8 additions & 4 deletions components/frontend/src/source/SourceEntity.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Table } from 'semantic-ui-react';
import { TableRowWithDetails } from '../widgets/TableRowWithDetails';
import { SourceEntityDetails } from './SourceEntityDetails';
import { SourceEntityAttribute } from './SourceEntityAttribute';
import { source_entity_status_name } from './source_entity_status';
import "./SourceEntity.css";

export function SourceEntity(props) {
Expand Down Expand Up @@ -30,10 +31,13 @@ export function SourceEntity(props) {
/>;
return (
<TableRowWithDetails className={status} details={details} key={props.entity.key} style={style}>
{props.entity_attributes.map((entity_attribute, col_index) =>
<Table.Cell key={col_index}>
<SourceEntityAttribute entity={props.entity} entity_attribute={entity_attribute} />
</Table.Cell>)}
<>
<Table.Cell>{source_entity_status_name[props.status]}</Table.Cell>
{props.entity_attributes.map((entity_attribute, col_index) =>
<Table.Cell key={col_index}>
<SourceEntityAttribute entity={props.entity} entity_attribute={entity_attribute} />
</Table.Cell>)}
</>
</TableRowWithDetails>
);
}
2 changes: 1 addition & 1 deletion components/frontend/src/source/SourceEntity.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Table } from 'semantic-ui-react';
import { mount } from 'enzyme';
import { SourceEntity } from './SourceEntity';

describe('<Source />', () => {
describe('<SourceEntity />', () => {
it('render', () => {
const wrapper = mount(<Table><Table.Body><SourceEntity entity_attributes={["attr"]} entity={{key:"1"}} /></Table.Body></Table>);
expect(wrapper.find("TableRow").hasClass("status_unknown"))
Expand Down
11 changes: 6 additions & 5 deletions components/frontend/src/source/SourceEntityDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TextInput } from '../fields/TextInput';
import { SingleChoiceInput } from '../fields/SingleChoiceInput';
import { set_source_entity_attribute } from '../api/source';
import { capitalize } from '../utils';
import { source_entity_status_name as status_name } from './source_entity_status';

function entity_status_option(status, text, content, subheader) {
return {
Expand All @@ -13,11 +14,11 @@ function entity_status_option(status, text, content, subheader) {

function entity_status_options(entity_type) {
return [
entity_status_option('unconfirmed', 'Unconfirmed', 'Unconfirm', `This ${entity_type} should be reviewed to decide what to do with it.`),
entity_status_option('confirmed', 'Confirmed', 'Confirm', `This ${entity_type} has been reviewed and should be dealt with.`),
entity_status_option('fixed', 'Fixed', 'Resolve as fixed', `This ${entity_type} has been fixed and will disappear shortly.`),
entity_status_option('false_positive', 'False positive', 'Resolve as false positive', `This ${entity_type} can be ignored because it's been incorrectly identified as ${entity_type}.`),
entity_status_option('wont_fix', "Won't fix", "Resolve as won't fix", `This ${entity_type} will not be fixed.`)
entity_status_option('unconfirmed', status_name.unconfirmed, 'Unconfirm', `This ${entity_type} should be reviewed to decide what to do with it.`),
entity_status_option('confirmed', status_name.confirmed, 'Confirm', `This ${entity_type} has been reviewed and should be dealt with.`),
entity_status_option('fixed', 'Fixed', status_name.fixed, `This ${entity_type} has been fixed and will disappear shortly.`),
entity_status_option('false_positive', status_name.false_positive, 'Resolve as false positive', `This ${entity_type} can be ignored because it's been incorrectly identified as ${entity_type}.`),
entity_status_option('wont_fix', status_name.wont_fix, "Resolve as won't fix", `This ${entity_type} will not be fixed.`)
]
}

Expand Down
7 changes: 7 additions & 0 deletions components/frontend/src/source/source_entity_status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const source_entity_status_name = {
unconfirmed: "Unconfirmed",
confirmed: "Confirmed",
fixed: "Fixed",
false_positive: "False positive",
wont_fix: "Won't fix"
}
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added

- Add a column to show the status (unconfirmed, confirmed, false positive, etc.) of security warnings, violations, etc. so that the user doesn't have to expand them to see the status. Closes [#1070](https://github.com/ICTU/quality-time/issues/1070).
- Show end date of technical debt in the measurement target column. Closes [#1072](https://github.com/ICTU/quality-time/issues/1072).
- Allow for accepting technical debt for a metric that has no sources or failing sources. Closes [#1076](https://github.com/ICTU/quality-time/issues/1076).

Expand Down

0 comments on commit a73c0ef

Please sign in to comment.