Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Frontend] snapshot diff setup #3166

Merged
merged 4 commits into from
Feb 25, 2020
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
9 changes: 6 additions & 3 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,16 @@ To understand more what prettier is: [What is Prettier](https://prettier.io/docs
this project's config automatically.
Recommend setting the following in [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settings-file-locations) for vscode to autoformat on save.
```
"[typescript]": {
"editor.formatOnSave": true,
"[typescript]": {
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": false,
},
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": false,
},
```
Also, vscode builtin trailing whitespace [conflicts with jest inline snapshot](https://github.com/Microsoft/vscode/issues/52711), so recommend disabling it.
- For others, refer to https://prettier.io/docs/en/editors.html

### Format Code Manually
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
],
"snapshotSerializers": [
"./src/__serializers__/mock-function",
"snapshot-diff/serializer.js",
"enzyme-to-json/serializer"
]
},
Expand Down
42 changes: 42 additions & 0 deletions frontend/src/TestUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { match } from 'react-router';
import { mount, ReactWrapper } from 'enzyme';
import { object } from 'prop-types';
import { format } from 'prettier';
import snapshotDiff from 'snapshot-diff';

export default class TestUtils {
/**
Expand Down Expand Up @@ -110,3 +111,44 @@ export default class TestUtils {
export function formatHTML(html: string): string {
return format(html, { parser: 'html' });
}

/**
* Generate diff text for two HTML strings.
* Recommend providing base and update annotations to clarify context in the diff directly.
*/
export function diffHTML({
base,
update,
baseAnnotation,
updateAnnotation,
}: {
base: string;
baseAnnotation?: string;
update: string;
updateAnnotation?: string;
}) {
return diff({
base: formatHTML(base),
update: formatHTML(update),
baseAnnotation,
updateAnnotation,
});
}

export function diff({
base,
update,
baseAnnotation,
updateAnnotation,
}: {
base: string;
baseAnnotation?: string;
update: string;
updateAnnotation?: string;
}) {
return snapshotDiff(base, update, {
stablePatchmarks: true, // Avoid line numbers in diff, so that diffs are stable against irrelevant changes
aAnnotation: baseAnnotation,
bAnnotation: updateAnnotation,
});
}
33 changes: 29 additions & 4 deletions frontend/src/components/SideNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@
import * as React from 'react';

import SideNav, { css } from './SideNav';
import TestUtils from '../TestUtils';
import TestUtils, { diff } from '../TestUtils';
import { Apis } from '../lib/Apis';
import { LocalStorage } from '../lib/LocalStorage';
import { ReactWrapper, ShallowWrapper, shallow } from 'enzyme';
import { RoutePage } from './Router';
import { RouterProps } from 'react-router';
import snapshotDiff from 'snapshot-diff';

const wideWidth = 1000;
const narrowWidth = 200;
Expand Down Expand Up @@ -259,10 +258,36 @@ describe('SideNav', () => {
buildInfoSpy.mockImplementationOnce(() => Promise.reject('Error when fetching build info'));

tree = shallow(<SideNav page={RoutePage.PIPELINES} {...routerProps} />);
const baseTree = tree.debug();
const base = tree.debug();
await TestUtils.flushPromises();
tree.update();
expect(snapshotDiff(baseTree, tree.debug())).toMatchSnapshot();
expect(diff({ base, update: tree.debug() })).toMatchInlineSnapshot(`
Snapshot Diff:
- Expected
+ Received

@@ --- --- @@
<WithStyles(IconButton) className="chevron" onClick={[Function: bound _toggleNavClicked]}>
<pure(ChevronLeftIcon) />
</WithStyles(IconButton)>
</div>
<div className="infoVisible">
+ <WithStyles(Tooltip) title="Cluster name: some-cluster-name, Project ID: some-project-id" enterDelay={300} placement="top-start">
+ <div className="envMetadata">
+ <span>
+ Cluster name:
+ </span>
+ <a href="https://console.cloud.google.com/kubernetes/list?project=some-project-id&filter=name:some-cluster-name" className="link unstyled" rel="noopener" target="_blank">
+ some-cluster-name
+ </a>
+ </div>
+ </WithStyles(Tooltip)>
<WithStyles(Tooltip) title="Report an Issue" enterDelay={300} placement="top-start">
<div className="envMetadata">
<a href="https://github.com/kubeflow/pipelines/issues/new?template=BUG_REPORT.md" className="link unstyled" rel="noopener" target="_blank">
Report an Issue
</a>
`);
});

it('displays the frontend commit hash if the api server hash is not returned', async () => {
Expand Down
28 changes: 0 additions & 28 deletions frontend/src/components/__snapshots__/SideNav.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -1,33 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`SideNav populates the cluster information using the response from the system endpoints 1`] = `
"Snapshot Diff:
- First value
+ Second value

@@ -64,10 +64,20 @@
<WithStyles(IconButton) className=\\"chevron\\" onClick={[Function: bound _toggleNavClicked]}>
<pure(ChevronLeftIcon) />
</WithStyles(IconButton)>
</div>
<div className=\\"infoVisible\\">
+ <WithStyles(Tooltip) title=\\"Cluster name: some-cluster-name, Project ID: some-project-id\\" enterDelay={300} placement=\\"top-start\\">
+ <div className=\\"envMetadata\\">
+ <span>
+ Cluster name:
+ </span>
+ <a href=\\"https://console.cloud.google.com/kubernetes/list?project=some-project-id&filter=name:some-cluster-name\\" className=\\"link unstyled\\" rel=\\"noopener\\" target=\\"_blank\\">
+ some-cluster-name
+ </a>
+ </div>
+ </WithStyles(Tooltip)>
<WithStyles(Tooltip) title=\\"Report an Issue\\" enterDelay={300} placement=\\"top-start\\">
<div className=\\"envMetadata\\">
<a href=\\"https://github.com/kubeflow/pipelines/issues/new?template=BUG_REPORT.md\\" className=\\"link unstyled\\" rel=\\"noopener\\" target=\\"_blank\\">
Report an Issue
</a>"
`;

exports[`SideNav populates the display build information using the response from the healthz endpoint 1`] = `
<div
className="root flexColumn noShrink"
Expand Down
63 changes: 55 additions & 8 deletions frontend/src/components/viewers/Tensorboard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@

import * as React from 'react';
import TensorboardViewer from './Tensorboard';
import TestUtils from '../../TestUtils';
import TestUtils, { diff } from '../../TestUtils';
import { Apis } from '../../lib/Apis';
import { PlotType } from './Viewer';
import { ReactWrapper, ShallowWrapper, shallow, mount } from 'enzyme';
import snapshotDiff from 'snapshot-diff';

describe('Tensorboard', () => {
let tree: ReactWrapper | ShallowWrapper;
Expand Down Expand Up @@ -50,21 +49,53 @@ describe('Tensorboard', () => {
const getAppMock = () => Promise.resolve({ podAddress: '', tfVersion: '' });
jest.spyOn(Apis, 'getTensorboardApp').mockImplementation(getAppMock);
tree = shallow(<TensorboardViewer configs={[]} />);
const baseTree = tree.debug();
const base = tree.debug();

await TestUtils.flushPromises();
expect(snapshotDiff(tree.debug(), baseTree)).toMatchSnapshot();
expect(diff({ base, update: tree.debug() })).toMatchInlineSnapshot(`
Snapshot Diff:
- Expected
+ Received

@@ --- --- @@
</WithStyles(MenuItem)>
</WithStyles(WithFormControlContext(Select))>
</WithStyles(FormControl)>
</div>
<div>
- <BusyButton className="buttonAction" disabled={false} onClick={[Function]} busy={true} title="Start Tensorboard" />
+ <BusyButton className="buttonAction" disabled={false} onClick={[Function]} busy={false} title="Start Tensorboard" />
</div>
</div>
</div>
`);
});

it('does not break on empty data', async () => {
const getAppMock = () => Promise.resolve({ podAddress: '', tfVersion: '' });
jest.spyOn(Apis, 'getTensorboardApp').mockImplementation(getAppMock);
const config = { type: PlotType.TENSORBOARD, url: '' };
tree = shallow(<TensorboardViewer configs={[config]} />);
const baseTree = tree.debug();
const base = tree.debug();

await TestUtils.flushPromises();
expect(snapshotDiff(tree.debug(), baseTree)).toMatchSnapshot();
expect(diff({ base, update: tree.debug() })).toMatchInlineSnapshot(`
Snapshot Diff:
- Expected
+ Received

@@ --- --- @@
</WithStyles(MenuItem)>
</WithStyles(WithFormControlContext(Select))>
</WithStyles(FormControl)>
</div>
<div>
- <BusyButton className="buttonAction" disabled={false} onClick={[Function]} busy={true} title="Start Tensorboard" />
+ <BusyButton className="buttonAction" disabled={false} onClick={[Function]} busy={false} title="Start Tensorboard" />
</div>
</div>
</div>
`);
});

it('shows a link to the tensorboard instance if exists', async () => {
Expand All @@ -82,10 +113,26 @@ describe('Tensorboard', () => {
const getAppMock = () => Promise.resolve({ podAddress: '', tfVersion: '' });
const spy = jest.spyOn(Apis, 'getTensorboardApp').mockImplementation(getAppMock);
tree = shallow(<TensorboardViewer configs={[config]} />);
const baseTree = tree.debug();
const base = tree.debug();

await TestUtils.flushPromises();
expect(snapshotDiff(tree.debug(), baseTree)).toMatchSnapshot();
expect(diff({ base, update: tree.debug() })).toMatchInlineSnapshot(`
Snapshot Diff:
- Expected
+ Received

@@ --- --- @@
</WithStyles(MenuItem)>
</WithStyles(WithFormControlContext(Select))>
</WithStyles(FormControl)>
</div>
<div>
- <BusyButton className="buttonAction" disabled={false} onClick={[Function]} busy={true} title="Start Tensorboard" />
+ <BusyButton className="buttonAction" disabled={false} onClick={[Function]} busy={false} title="Start Tensorboard" />
</div>
</div>
</div>
`);
expect(spy).toHaveBeenCalledWith(config.url);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,42 +111,6 @@ exports[`Tensorboard base component snapshot 1`] = `
</div>
`;

exports[`Tensorboard does not break on empty data 1`] = `
"Snapshot Diff:
- First value
+ Second value

@@ -53,9 +53,9 @@
</WithStyles(MenuItem)>
</WithStyles(WithFormControlContext(Select))>
</WithStyles(FormControl)>
</div>
<div>
- <BusyButton className=\\"buttonAction\\" disabled={false} onClick={[Function]} busy={false} title=\\"Start Tensorboard\\" />
+ <BusyButton className=\\"buttonAction\\" disabled={false} onClick={[Function]} busy={true} title=\\"Start Tensorboard\\" />
</div>
</div>
</div>"
`;

exports[`Tensorboard does not break on no config 1`] = `
"Snapshot Diff:
- First value
+ Second value

@@ -53,9 +53,9 @@
</WithStyles(MenuItem)>
</WithStyles(WithFormControlContext(Select))>
</WithStyles(FormControl)>
</div>
<div>
- <BusyButton className=\\"buttonAction\\" disabled={false} onClick={[Function]} busy={false} title=\\"Start Tensorboard\\" />
+ <BusyButton className=\\"buttonAction\\" disabled={false} onClick={[Function]} busy={true} title=\\"Start Tensorboard\\" />
</div>
</div>
</div>"
`;

exports[`Tensorboard shows a link to the tensorboard instance if exists 1`] = `
<div>
<div>
Expand Down Expand Up @@ -218,21 +182,3 @@ exports[`Tensorboard shows a link to the tensorboard instance if exists 1`] = `
</div>
</div>
`;

exports[`Tensorboard shows start button if no instance exists 1`] = `
"Snapshot Diff:
- First value
+ Second value

@@ -53,9 +53,9 @@
</WithStyles(MenuItem)>
</WithStyles(WithFormControlContext(Select))>
</WithStyles(FormControl)>
</div>
<div>
- <BusyButton className=\\"buttonAction\\" disabled={false} onClick={[Function]} busy={false} title=\\"Start Tensorboard\\" />
+ <BusyButton className=\\"buttonAction\\" disabled={false} onClick={[Function]} busy={true} title=\\"Start Tensorboard\\" />
</div>
</div>
</div>"
`;
Loading