-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds metrics table to Compare page, creates Metric component (#1284)
* Adds metrics table to Compare page, creates Metric atom * Cleanup * Adds tests for metric component * Adds CompareUtils tests * Adds MetricUtils tests * Adds compare page tests
- Loading branch information
1 parent
8382595
commit 814fe28
Showing
14 changed files
with
1,481 additions
and
412 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright 2019 Google LLC | ||
* | ||
* 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 | ||
* | ||
* https://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 * as React from 'react'; | ||
import Metric from './Metric'; | ||
import { ReactWrapper, ShallowWrapper, shallow } from 'enzyme'; | ||
import { RunMetricFormat } from '../apis/run'; | ||
|
||
describe('Metric', () => { | ||
let tree: ShallowWrapper | ReactWrapper; | ||
|
||
const onErrorSpy = jest.fn(); | ||
|
||
beforeEach(() => { | ||
onErrorSpy.mockClear(); | ||
}); | ||
|
||
afterEach(async () => { | ||
// unmount() should be called before resetAllMocks() in case any part of the unmount life cycle | ||
// depends on mocks/spies | ||
if (tree) { | ||
await tree.unmount(); | ||
} | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('renders an empty metric when there is no metric', () => { | ||
tree = shallow(<Metric />); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders an empty metric when metric has no value', () => { | ||
tree = shallow(<Metric metric={{}} />); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders a metric when metric has value and percentage format', () => { | ||
tree = shallow(<Metric metric={{ format: RunMetricFormat.PERCENTAGE, number_value: 0.54 }} />); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders an empty metric when metric has no metadata and unspecified format', () => { | ||
tree = shallow(<Metric metric={{ format: RunMetricFormat.UNSPECIFIED, number_value: 0.54 }} />); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders an empty metric when metric has no metadata and raw format', () => { | ||
tree = shallow(<Metric metric={{ format: RunMetricFormat.RAW, number_value: 0.54 }} />); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders a metric when metric has max and min value of 0', () => { | ||
tree = shallow( | ||
<Metric | ||
metadata={{ name: 'some metric', count: 1, maxValue: 0, minValue: 0 }} | ||
metric={{ format: RunMetricFormat.RAW, number_value: 0.54 }} | ||
/>); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders a metric and does not log an error when metric is between max and min value', () => { | ||
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); | ||
tree = shallow( | ||
<Metric | ||
metadata={{ name: 'some metric', count: 1, maxValue: 1, minValue: 0 }} | ||
metric={{ format: RunMetricFormat.RAW, number_value: 0.54 }} | ||
/>); | ||
expect(consoleSpy).toHaveBeenCalledTimes(0); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders a metric and logs an error when metric has value less than min value', () => { | ||
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); | ||
tree = shallow( | ||
<Metric | ||
metadata={{ name: 'some metric', count: 1, maxValue: 1, minValue: 0 }} | ||
metric={{ format: RunMetricFormat.RAW, number_value: -0.54 }} | ||
/>); | ||
expect(consoleSpy).toHaveBeenCalled(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('renders a metric and logs an error when metric has value greater than max value', () => { | ||
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); | ||
tree = shallow( | ||
<Metric | ||
metadata={{ name: 'some metric', count: 1, maxValue: 1, minValue: 0 }} | ||
metric={{ format: RunMetricFormat.RAW, number_value: 2 }} | ||
/>); | ||
expect(consoleSpy).toHaveBeenCalled(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* Copyright 2019 Google LLC | ||
* | ||
* 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 | ||
* | ||
* https://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 * as React from 'react'; | ||
import { ApiRunMetric, RunMetricFormat } from '../apis/run'; | ||
import { MetricMetadata } from '../lib/RunUtils'; | ||
import { stylesheet } from 'typestyle'; | ||
import { logger } from '../lib/Utils'; | ||
import MetricUtils from '../lib/MetricUtils'; | ||
|
||
const css = stylesheet({ | ||
metricContainer: { | ||
background: '#f6f7f9', | ||
marginLeft: 6, | ||
marginRight: 10, | ||
}, | ||
metricFill: { | ||
background: '#cbf0f8', | ||
boxSizing: 'border-box', | ||
color: '#202124', | ||
fontFamily: 'Roboto', | ||
fontSize: 13, | ||
textIndent: 6, | ||
}, | ||
}); | ||
|
||
interface MetricProps { | ||
metadata?: MetricMetadata; | ||
metric?: ApiRunMetric; | ||
} | ||
|
||
class Metric extends React.PureComponent<MetricProps> { | ||
|
||
public render(): JSX.Element { | ||
const { metric, metadata } = this.props; | ||
if (!metric || metric.number_value === undefined) { | ||
return <div />; | ||
} | ||
|
||
const displayString = MetricUtils.getMetricDisplayString(metric); | ||
let width = ''; | ||
|
||
if (metric.format === RunMetricFormat.PERCENTAGE) { | ||
width = `calc(${displayString})`; | ||
} else { | ||
// Non-percentage metrics must contain metadata | ||
if (!metadata) { | ||
return <div />; | ||
} | ||
|
||
const leftSpace = 6; | ||
|
||
if (metadata.maxValue === 0 && metadata.minValue === 0) { | ||
return <div style={{ paddingLeft: leftSpace }}>{displayString}</div>; | ||
} | ||
|
||
if (metric.number_value - metadata.minValue < 0) { | ||
logger.error(`Metric ${metadata.name}'s value:` | ||
+ ` (${metric.number_value}) was lower than the supposed minimum of` | ||
+ ` (${metadata.minValue})`); | ||
return <div style={{ paddingLeft: leftSpace }}>{displayString}</div>; | ||
} | ||
|
||
if (metadata.maxValue - metric.number_value < 0) { | ||
logger.error(`Metric ${metadata.name}'s value:` | ||
+ ` (${metric.number_value}) was greater than the supposed maximum of` | ||
+ ` (${metadata.maxValue})`); | ||
return <div style={{ paddingLeft: leftSpace }}>{displayString}</div>; | ||
} | ||
|
||
const barWidth = | ||
(metric.number_value - metadata.minValue) | ||
/ (metadata.maxValue - metadata.minValue) | ||
* 100; | ||
|
||
width = `calc(${barWidth}%)`; | ||
} | ||
return ( | ||
<div className={css.metricContainer}> | ||
<div className={css.metricFill} style={{ width }}> | ||
{displayString} | ||
</div> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default Metric; |
79 changes: 79 additions & 0 deletions
79
frontend/src/components/__snapshots__/Metric.test.tsx.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`Metric renders a metric and does not log an error when metric is between max and min value 1`] = ` | ||
<div | ||
className="metricContainer" | ||
> | ||
<div | ||
className="metricFill" | ||
style={ | ||
Object { | ||
"width": "calc(54%)", | ||
} | ||
} | ||
> | ||
0.540 | ||
</div> | ||
</div> | ||
`; | ||
|
||
exports[`Metric renders a metric and logs an error when metric has value greater than max value 1`] = ` | ||
<div | ||
style={ | ||
Object { | ||
"paddingLeft": 6, | ||
} | ||
} | ||
> | ||
2.000 | ||
</div> | ||
`; | ||
|
||
exports[`Metric renders a metric and logs an error when metric has value less than min value 1`] = ` | ||
<div | ||
style={ | ||
Object { | ||
"paddingLeft": 6, | ||
} | ||
} | ||
> | ||
-0.540 | ||
</div> | ||
`; | ||
|
||
exports[`Metric renders a metric when metric has max and min value of 0 1`] = ` | ||
<div | ||
style={ | ||
Object { | ||
"paddingLeft": 6, | ||
} | ||
} | ||
> | ||
0.540 | ||
</div> | ||
`; | ||
|
||
exports[`Metric renders a metric when metric has value and percentage format 1`] = ` | ||
<div | ||
className="metricContainer" | ||
> | ||
<div | ||
className="metricFill" | ||
style={ | ||
Object { | ||
"width": "calc(54.000%)", | ||
} | ||
} | ||
> | ||
54.000% | ||
</div> | ||
</div> | ||
`; | ||
|
||
exports[`Metric renders an empty metric when metric has no metadata and raw format 1`] = `<div />`; | ||
|
||
exports[`Metric renders an empty metric when metric has no metadata and unspecified format 1`] = `<div />`; | ||
|
||
exports[`Metric renders an empty metric when metric has no value 1`] = `<div />`; | ||
|
||
exports[`Metric renders an empty metric when there is no metric 1`] = `<div />`; |
Oops, something went wrong.