-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Increase code coverage for Linker (#1215)
- Loading branch information
1 parent
8ac7dc8
commit 8c7207c
Showing
7 changed files
with
602 additions
and
9 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
162 changes: 162 additions & 0 deletions
162
packages/dashboard-core-plugins/src/linker/Linker.test.tsx
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,162 @@ | ||
import React from 'react'; | ||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
import { | ||
OpenedPanelMap, | ||
PanelComponent, | ||
PanelManager, | ||
} from '@deephaven/dashboard'; | ||
import GoldenLayout, { Config } from '@deephaven/golden-layout'; | ||
import { TypeValue as FilterTypeValue } from '@deephaven/filters'; | ||
import ToolType from './ToolType'; | ||
import { Linker } from './Linker'; | ||
import { Link, LinkPoint, LinkType } from './LinkerUtils'; | ||
|
||
// Disable CSSTransition delays to make testing simpler | ||
jest.mock('react-transition-group', () => ({ | ||
// eslint-disable-next-line react/display-name, react/prop-types | ||
Transition: ({ children, in: inProp }) => | ||
inProp !== false ? children : null, | ||
// eslint-disable-next-line react/display-name, react/prop-types | ||
CSSTransition: ({ children, in: inProp }) => | ||
inProp !== false ? children : null, | ||
})); | ||
|
||
function makeLayout() { | ||
return new GoldenLayout({} as Config, undefined); | ||
} | ||
|
||
function makePanelManager(layout = makeLayout()) { | ||
const PANEL_ID_A = 'PANEL_ID_A'; | ||
const PANEL_ID_B = 'PANEL_ID_B'; | ||
const openedMap: OpenedPanelMap = new Map([ | ||
[ | ||
PANEL_ID_A, | ||
{ | ||
getCoordinateForColumn: jest.fn(() => { | ||
const coordinate = [5, 5]; | ||
return coordinate; // make coordinates here | ||
}), | ||
} as PanelComponent, | ||
], | ||
[ | ||
PANEL_ID_B, | ||
{ | ||
getCoordinateForColumn: jest.fn(() => { | ||
const coordinate = [50, 50]; | ||
return coordinate; // make coordinates here | ||
}), | ||
} as PanelComponent, | ||
], | ||
]); | ||
return new PanelManager(layout, undefined, undefined, openedMap); | ||
} | ||
|
||
function makeLinkPoint( | ||
panelId: string | string[], | ||
columnName: string, | ||
columnType: string | null, | ||
panelComponent?: string | null | ||
): LinkPoint { | ||
return { panelId, panelComponent, columnName, columnType }; | ||
} | ||
|
||
function makeLink( | ||
start: LinkPoint, | ||
end: LinkPoint, | ||
id: string, | ||
type: LinkType, | ||
isReversed?: boolean | undefined, | ||
operator?: FilterTypeValue | undefined | ||
): Link { | ||
return { start, end, id, isReversed, type, operator }; | ||
} | ||
|
||
function mountLinker({ | ||
links = [] as Link[], | ||
timeZone = 'TIMEZONE', | ||
activeTool = ToolType.LINKER, | ||
localDashboardId = 'TEST_ID', | ||
layout = makeLayout(), | ||
panelManager = makePanelManager(), | ||
setActiveTool = jest.fn(), | ||
setDashboardLinks = jest.fn(), | ||
addDashboardLinks = jest.fn(), | ||
deleteDashboardLinks = jest.fn(), | ||
setDashboardIsolatedLinkerPanelId = jest.fn(), | ||
setDashboardColumnSelectionValidator = jest.fn(), | ||
} = {}) { | ||
return render( | ||
<Linker | ||
links={links} | ||
timeZone={timeZone} | ||
activeTool={activeTool} | ||
localDashboardId={localDashboardId} | ||
layout={layout} | ||
panelManager={panelManager} | ||
setActiveTool={setActiveTool} | ||
setDashboardLinks={setDashboardLinks} | ||
addDashboardLinks={addDashboardLinks} | ||
deleteDashboardLinks={deleteDashboardLinks} | ||
setDashboardIsolatedLinkerPanelId={setDashboardIsolatedLinkerPanelId} | ||
setDashboardColumnSelectionValidator={ | ||
setDashboardColumnSelectionValidator | ||
} | ||
/> | ||
); | ||
} | ||
|
||
it('closes Linker when escape key or Done button is pressed', async () => { | ||
const setActiveTool = jest.fn(); | ||
mountLinker({ setActiveTool }); | ||
const dialog = screen.getByTestId('linker-toast-dialog'); | ||
const buttons = await screen.findAllByRole('button'); | ||
expect(buttons).toHaveLength(3); | ||
|
||
const doneButton = screen.getByRole('button', { name: 'Done' }); | ||
fireEvent.click(doneButton); | ||
expect(setActiveTool).toHaveBeenCalledWith(ToolType.DEFAULT); | ||
|
||
fireEvent.keyDown(dialog, { key: 'Escape' }); | ||
expect(setActiveTool).toHaveBeenCalledWith(ToolType.DEFAULT); | ||
}); | ||
|
||
describe('tests link operations', () => { | ||
const deleteDashboardLinks = jest.fn(); | ||
const setDashboardLinks = jest.fn(); | ||
let linkPaths: HTMLElement[] = []; | ||
|
||
beforeEach(async () => { | ||
const links: Link[] = []; | ||
for (let i = 0; i < 4; i += 1) { | ||
const start = makeLinkPoint( | ||
'PANEL_ID_A', | ||
`COLUMN_A`, | ||
'int', | ||
'PANEL_COMPONENT' | ||
); | ||
const end = makeLinkPoint( | ||
'PANEL_ID_B', | ||
`COLUMN_B_${i}`, | ||
'long', | ||
'PANEL_COMPONENT' | ||
); | ||
const link = makeLink(start, end, `LINK_ID_${i}`, 'tableLink'); | ||
links.push(link); | ||
} | ||
mountLinker({ links, deleteDashboardLinks, setDashboardLinks }); | ||
linkPaths = screen.getAllByTestId('link-select'); | ||
expect(linkPaths).toHaveLength(4); | ||
}); | ||
|
||
it('deletes correct link with alt+click', async () => { | ||
expect(linkPaths).toHaveLength(4); | ||
fireEvent.click(linkPaths[0], { altKey: true }); | ||
expect(deleteDashboardLinks).toHaveBeenCalledWith('TEST_ID', ['LINK_ID_0']); | ||
}); | ||
|
||
it('deletes all links when Clear All is clicked', async () => { | ||
const clearAllButton = screen.getByRole('button', { name: 'Clear All' }); | ||
fireEvent.click(clearAllButton); | ||
expect(setDashboardLinks).toHaveBeenCalledWith('TEST_ID', []); | ||
}); | ||
}); |
163 changes: 163 additions & 0 deletions
163
packages/dashboard-core-plugins/src/linker/LinkerLink.test.tsx
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,163 @@ | ||
import React from 'react'; | ||
import { fireEvent, render, screen } from '@testing-library/react'; | ||
import { Type as FilterType } from '@deephaven/filters'; | ||
import LinkerLink from './LinkerLink'; | ||
|
||
function makeLinkerLink({ | ||
x1 = 10, | ||
x2 = 50, | ||
y1 = 10, | ||
y2 = 10, | ||
isSelected = true, | ||
startColumnType = 'int', | ||
id = 'LINK_ID', | ||
className = 'linker-link link-is-selected', | ||
operator = FilterType.eq, | ||
onClick = jest.fn(), | ||
onDelete = jest.fn(), | ||
onOperatorChanged = jest.fn(), | ||
} = {}) { | ||
return render( | ||
<LinkerLink | ||
x1={x1} | ||
x2={x2} | ||
y1={y1} | ||
y2={y2} | ||
id={id} | ||
className={className} | ||
operator={operator} | ||
isSelected={isSelected} | ||
startColumnType={startColumnType} | ||
onClick={onClick} | ||
onDelete={onDelete} | ||
onOperatorChanged={onOperatorChanged} | ||
/> | ||
); | ||
} | ||
|
||
it('mounts and renders correct comparison operators for strings', async () => { | ||
const onOperatorChanged = jest.fn(); | ||
const props = { | ||
startColumnType: 'java.lang.String', | ||
operator: FilterType.startsWith, | ||
onOperatorChanged, | ||
}; | ||
makeLinkerLink(props); | ||
|
||
const dropdownAndDeleteButton = await screen.findAllByRole('button'); | ||
expect(dropdownAndDeleteButton[0]).toHaveTextContent('a*'); | ||
|
||
dropdownAndDeleteButton[0].click(); | ||
const dropdownMenu = await screen.findAllByRole('button'); | ||
expect(dropdownMenu).toHaveLength(8); // includes dropdown and delete button | ||
expect(dropdownMenu[2]).toHaveTextContent('is exactly'); | ||
expect(dropdownMenu[3]).toHaveTextContent('is not exactly'); | ||
expect(dropdownMenu[4]).toHaveTextContent('contains'); | ||
expect(dropdownMenu[5]).toHaveTextContent('does not contain'); | ||
expect(dropdownMenu[6]).toHaveTextContent('starts with'); | ||
expect(dropdownMenu[7]).toHaveTextContent('ends with'); | ||
|
||
dropdownMenu[4].click(); | ||
expect(onOperatorChanged).toHaveBeenCalledWith( | ||
'LINK_ID', | ||
FilterType.contains | ||
); | ||
}); | ||
|
||
it('renders correct symbol for endsWith', async () => { | ||
makeLinkerLink({ operator: FilterType.endsWith }); | ||
const dropdownAndDeleteButton = await screen.findAllByRole('button'); | ||
expect(dropdownAndDeleteButton[0]).toHaveTextContent('*z'); | ||
}); | ||
|
||
it('mounts and renders correct comparison operators for numbers', async () => { | ||
const props = { | ||
x1: 10, | ||
x2: 10, | ||
y1: 30, | ||
y2: 50, | ||
startColumnType: 'long', | ||
operator: FilterType.notEq, | ||
}; | ||
makeLinkerLink(props); | ||
const dropdownAndDeleteButton = await screen.findAllByRole('button'); | ||
expect(dropdownAndDeleteButton[0]).toHaveTextContent('!='); | ||
|
||
dropdownAndDeleteButton[0].click(); | ||
const dropdownMenu = await screen.findAllByRole('button'); | ||
expect(dropdownMenu).toHaveLength(8); // includes dropdown and delete button | ||
expect(dropdownMenu[2]).toHaveTextContent('is equal to'); | ||
expect(dropdownMenu[3]).toHaveTextContent('is not equal to'); | ||
expect(dropdownMenu[4]).toHaveTextContent('greater than'); | ||
expect(dropdownMenu[5]).toHaveTextContent('greater than or equal to'); | ||
expect(dropdownMenu[6]).toHaveTextContent('less than'); | ||
expect(dropdownMenu[7]).toHaveTextContent('less than or equal to'); | ||
}); | ||
|
||
it('mounts and renders correct comparison operators for date/time', async () => { | ||
const props = { | ||
x1: 10, | ||
x2: 20, | ||
y1: 50, | ||
y2: 30, | ||
startColumnType: 'io.deephaven.time.DateTime', | ||
operator: FilterType.lessThan, | ||
}; | ||
makeLinkerLink(props); | ||
const dropdownAndDeleteButton = await screen.findAllByRole('button'); | ||
expect(dropdownAndDeleteButton[0]).toHaveTextContent('<'); | ||
|
||
dropdownAndDeleteButton[0].click(); | ||
const dropdownMenu = await screen.findAllByRole('button'); | ||
expect(dropdownMenu).toHaveLength(8); // includes dropdown and delete button | ||
expect(dropdownMenu[2]).toHaveTextContent('date is'); | ||
expect(dropdownMenu[3]).toHaveTextContent('date is not'); | ||
expect(dropdownMenu[4]).toHaveTextContent('date is after'); | ||
expect(dropdownMenu[5]).toHaveTextContent('date is after or equal'); | ||
expect(dropdownMenu[6]).toHaveTextContent('date is before'); | ||
expect(dropdownMenu[7]).toHaveTextContent('date is before or equal'); | ||
}); | ||
|
||
it('mounts and renders correct comparison operators for booleans', async () => { | ||
const props = { | ||
x1: 10, | ||
x2: 20, | ||
y1: 30, | ||
y2: 100, | ||
startColumnType: 'boolean', | ||
operator: FilterType.greaterThanOrEqualTo, | ||
}; | ||
makeLinkerLink(props); | ||
const dropdownAndDeleteButton = await screen.findAllByRole('button'); | ||
expect(dropdownAndDeleteButton[0]).toHaveTextContent('>='); | ||
|
||
dropdownAndDeleteButton[0].click(); | ||
const dropdownMenu = await screen.findAllByRole('button'); | ||
expect(dropdownMenu).toHaveLength(4); // includes dropdown and delete button | ||
expect(dropdownMenu[2]).toHaveTextContent('is equal to'); | ||
expect(dropdownMenu[3]).toHaveTextContent('is not equal to'); | ||
}); | ||
|
||
it('returns an empty label for invalid column type', async () => { | ||
const startColumnType = 'INVALID_TYPE'; | ||
makeLinkerLink({ startColumnType }); | ||
expect(LinkerLink.getLabelForLinkFilter(startColumnType, FilterType.eq)).toBe( | ||
'' | ||
); | ||
}); | ||
|
||
it('calls onClick when the link is clicked and onDelete on alt-click and button press', async () => { | ||
const onClick = jest.fn(); | ||
const onDelete = jest.fn(); | ||
makeLinkerLink({ onClick, onDelete }); | ||
|
||
const linkPath = screen.getByTestId('link-select'); | ||
fireEvent.click(linkPath); | ||
expect(onClick).toHaveBeenCalledTimes(1); | ||
|
||
fireEvent.click(linkPath, { altKey: true }); | ||
expect(onDelete).toHaveBeenCalledTimes(1); | ||
const dropdownAndDeleteButton = await screen.findAllByRole('button'); | ||
dropdownAndDeleteButton[1].click(); | ||
expect(onDelete).toHaveBeenCalledTimes(2); | ||
}); |
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
Oops, something went wrong.