mirror of https://github.com/grafana/grafana
Test: Add unit tests for the homepage (#95912)
* test: add skeleton for initial unit tests * test: add basic tests for DataTrailsHome * test-wip: add tests for recent metrics functionality, but need to move from DataTrailsHome.test to DataTrailsRecentMetrics.test * wip: tests for DataTrailBookmarks * test: add tests for recent metrics; refactor: make DataTrailsRecentMetrics accept onSelect as prop rather than whole trail; test: add tests for DataTrailCard (WIP) * test: add test for truncates long list of labels after 3 lines in recent explorations * refactor: make DataTrailBookmarks take in onSelect and onDelete as props rather than a whole trail and onDelete; test: add tests for bookmarks (WIP) * remove deprecated style * fix import issues * fix getTrailForBookmark tests by returning a trail, clean up tests * chore: delete notes to self --------- Co-authored-by: Brendan O'Handley <brendan.ohandley@grafana.com>pull/96862/head
parent
30b3fd2864
commit
c8bc1f8637
@ -0,0 +1,68 @@ |
||||
import { render, screen, fireEvent } from '@testing-library/react'; |
||||
|
||||
import { DataTrail } from './DataTrail'; |
||||
import { DataTrailsBookmarks } from './DataTrailBookmarks'; |
||||
import { getTrailStore, DataTrailBookmark } from './TrailStore/TrailStore'; |
||||
|
||||
jest.mock('./TrailStore/TrailStore', () => ({ |
||||
getTrailStore: jest.fn(), |
||||
getBookmarkKey: jest.fn(() => 'bookmark-key'), |
||||
})); |
||||
|
||||
const onSelect = jest.fn(); |
||||
const onDelete = jest.fn(); |
||||
|
||||
describe('DataTrailsBookmarks', () => { |
||||
const trail = new DataTrail({}); |
||||
const bookmark: DataTrailBookmark = { urlValues: { key: '1', metric: '' }, createdAt: Date.now() }; |
||||
|
||||
beforeEach(() => { |
||||
onSelect.mockClear(); |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [], |
||||
getTrailForBookmark: jest.fn(), |
||||
})); |
||||
}); |
||||
|
||||
it('does not render if there are no bookmarks', () => { |
||||
render(<DataTrailsBookmarks onSelect={onSelect} onDelete={onDelete} />); |
||||
expect(screen.queryByText('Or view bookmarks')).not.toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('renders the bookmarks header and toggle button', () => { |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [bookmark], |
||||
recent: [], |
||||
})); |
||||
render(<DataTrailsBookmarks onSelect={onSelect} onDelete={onDelete} />); |
||||
expect(screen.getByText('Or view bookmarks')).toBeInTheDocument(); |
||||
expect(screen.getByLabelText('bookmarkCarrot')).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('toggles the bookmark list when the toggle button is clicked', () => { |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [bookmark], |
||||
recent: [], |
||||
getTrailForBookmark: jest.fn().mockReturnValue(trail), |
||||
})); |
||||
render(<DataTrailsBookmarks onSelect={onSelect} onDelete={onDelete} />); |
||||
const button = screen.getByLabelText('bookmarkCarrot'); |
||||
fireEvent.click(button); |
||||
expect(screen.getByText('Select metric')).toBeInTheDocument(); |
||||
fireEvent.click(button); |
||||
expect(screen.queryByText('Select metric')).not.toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('calls onDelete when the delete button is clicked', () => { |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [bookmark], |
||||
recent: [], |
||||
getTrailForBookmark: jest.fn().mockReturnValue(trail), |
||||
})); |
||||
render(<DataTrailsBookmarks onSelect={onSelect} onDelete={onDelete} />); |
||||
fireEvent.click(screen.getByLabelText('bookmarkCarrot')); |
||||
fireEvent.click(screen.getByLabelText('Remove bookmark')); |
||||
expect(onDelete).toHaveBeenCalled(); |
||||
}); |
||||
}); |
@ -0,0 +1,73 @@ |
||||
import { render, screen, fireEvent } from '@testing-library/react'; |
||||
|
||||
import { DataTrail } from './DataTrail'; |
||||
import { DataTrailCard } from './DataTrailCard'; |
||||
import { DataTrailBookmark } from './TrailStore/TrailStore'; |
||||
|
||||
jest.mock('./utils', () => ({ |
||||
...jest.requireActual('./utils'), |
||||
getDataSource: jest.fn(() => 'Test DataSource'), |
||||
getDataSourceName: jest.fn(() => 'Test DataSource Name'), |
||||
})); |
||||
|
||||
describe('DataTrailCard', () => { |
||||
// trail is a recent metric exploration
|
||||
const trail = new DataTrail({ key: '1', metric: 'Test Recent Exploration' }); |
||||
// bookmark is a data trail stored in a url
|
||||
const bookmark: DataTrailBookmark = { urlValues: { key: '1', metric: 'Test Bookmark' }, createdAt: Date.now() }; |
||||
const onSelect = jest.fn(); |
||||
const onDelete = jest.fn(); |
||||
beforeEach(() => { |
||||
onSelect.mockClear(); |
||||
onDelete.mockClear(); |
||||
}); |
||||
|
||||
it('renders the card with recent metric exploration', () => { |
||||
render(<DataTrailCard trail={trail} onSelect={onSelect} onDelete={onDelete} />); |
||||
expect(screen.getByText('Test Recent Exploration')).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('renders the card with bookmark', () => { |
||||
render(<DataTrailCard bookmark={bookmark} onSelect={onSelect} onDelete={onDelete} />); |
||||
expect(screen.getByText('Test Bookmark')).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('calls onSelect when the card is clicked', () => { |
||||
render(<DataTrailCard bookmark={bookmark} onSelect={onSelect} onDelete={onDelete} />); |
||||
fireEvent.click(screen.getByText('Test Bookmark')); |
||||
expect(onSelect).toHaveBeenCalled(); |
||||
}); |
||||
|
||||
it('calls onDelete when the delete button is clicked', () => { |
||||
render(<DataTrailCard bookmark={bookmark} onSelect={onSelect} onDelete={onDelete} />); |
||||
fireEvent.click(screen.getByTestId('deleteButton')); |
||||
expect(onDelete).toHaveBeenCalled(); |
||||
}); |
||||
|
||||
it('truncates singular long label in recent explorations', () => { |
||||
const longLabel = |
||||
'aajalsdkfaldkjfalskdjfalsdkjfalsdkjflaskjdflaskjdflaskjdflaskjdflasjkdflaskjdflaskjdflaskjflaskdjfldaskjflasjflaskdjflaskjflasjflaskfjalsdfjlskdjflaskjdflajkfjfalkdfjaverylongalskdjlalsjflajkfklsajdfalskjdflkasjdflkadjf'; |
||||
const bookmarkWithLongLabel: DataTrailBookmark = { |
||||
urlValues: { key: '1', metric: 'metric', 'var-filters': `zone|=|${longLabel}` }, |
||||
createdAt: Date.now(), |
||||
}; |
||||
render(<DataTrailCard bookmark={bookmarkWithLongLabel} onSelect={onSelect} onDelete={onDelete} />); |
||||
expect(screen.getByText('...', { exact: false })).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('truncates long list of labels after 3 lines in recent explorations', () => { |
||||
const bookmarkWithLongLabel: DataTrailBookmark = { |
||||
urlValues: { |
||||
key: '1', |
||||
metric: 'metric', |
||||
// labels are in a comma separated list
|
||||
'var-filters': `zone|=|averylonglabeltotakeupspace,zone=averylonglabeltotakeupspace,zone1=averylonglabeltotakeupspace,zone2=averylonglabeltotakeupspace,zone3=averylonglabeltotakeupspace,zone4=averylonglabeltotakeupspace`, |
||||
}, |
||||
createdAt: Date.now(), |
||||
}; |
||||
render(<DataTrailCard bookmark={bookmarkWithLongLabel} onSelect={onSelect} onDelete={onDelete} />); |
||||
// to test the non-existence of a truncated label we need queryByText
|
||||
const truncatedLabel = screen.queryByText('zone4'); |
||||
expect(truncatedLabel).not.toBeInTheDocument(); |
||||
}); |
||||
}); |
@ -0,0 +1,73 @@ |
||||
import { render, screen } from '@testing-library/react'; |
||||
|
||||
import { AdHocFiltersVariable, sceneGraph, SceneObjectRef, SceneVariableSet } from '@grafana/scenes'; |
||||
|
||||
import { DataTrail } from './DataTrail'; |
||||
import { DataTrailsHome } from './DataTrailsHome'; |
||||
import { getTrailStore } from './TrailStore/TrailStore'; |
||||
import { VAR_FILTERS } from './shared'; |
||||
|
||||
jest.mock('./TrailStore/TrailStore', () => ({ |
||||
getTrailStore: jest.fn(), |
||||
})); |
||||
|
||||
describe('DataTrailsHome', () => { |
||||
let scene: DataTrailsHome; |
||||
beforeEach(() => { |
||||
const filtersVariable = new AdHocFiltersVariable({ name: VAR_FILTERS }); |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [], |
||||
})); |
||||
scene = new DataTrailsHome({ |
||||
$variables: new SceneVariableSet({ |
||||
variables: [filtersVariable], |
||||
}), |
||||
}); |
||||
}); |
||||
|
||||
it('renders the start button', () => { |
||||
render(<scene.Component model={scene} />); |
||||
expect(screen.getByText("Let's start!")).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('renders the learn more button and checks its href', () => { |
||||
render(<scene.Component model={scene} />); |
||||
const learnMoreButton = screen.getByText('Learn more'); |
||||
expect(learnMoreButton).toBeInTheDocument(); |
||||
expect(learnMoreButton.closest('a')).toHaveAttribute( |
||||
'href', |
||||
'https://grafana.com/docs/grafana/latest/explore/explore-metrics/' |
||||
); |
||||
}); |
||||
|
||||
it('does not show recent metrics and bookmarks headers for first time user', () => { |
||||
render(<scene.Component model={scene} />); |
||||
expect(screen.queryByText('Or view a recent exploration')).not.toBeInTheDocument(); |
||||
expect(screen.queryByText('Or view bookmarks')).not.toBeInTheDocument(); |
||||
expect(screen.queryByRole('separator')).not.toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('truncates singular long label in recent explorations', () => { |
||||
const trail = new DataTrail({}); |
||||
function getFilterVar() { |
||||
const variable = sceneGraph.lookupVariable(VAR_FILTERS, trail); |
||||
if (variable instanceof AdHocFiltersVariable) { |
||||
return variable; |
||||
} |
||||
throw new Error('getFilterVar failed'); |
||||
} |
||||
const filtersVariable = getFilterVar(); |
||||
const longLabel = 'averylongalskdjlalsjflajkfklsajdfalskjdflkasjdflkadjf'; |
||||
filtersVariable.setState({ |
||||
filters: [{ key: 'zone', operator: '=', value: longLabel }], |
||||
}); |
||||
const trailWithResolveMethod = new SceneObjectRef(trail); |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [trailWithResolveMethod], |
||||
})); |
||||
render(<scene.Component model={scene} />); |
||||
expect(screen.getByText('...', { exact: false })).toBeInTheDocument(); |
||||
}); |
||||
}); |
@ -0,0 +1,115 @@ |
||||
import { render, screen, fireEvent } from '@testing-library/react'; |
||||
|
||||
import { SceneObjectRef } from '@grafana/scenes'; |
||||
|
||||
import { DataTrail } from './DataTrail'; |
||||
import { DataTrailsRecentMetrics } from './DataTrailsRecentMetrics'; |
||||
import { getTrailStore } from './TrailStore/TrailStore'; |
||||
|
||||
jest.mock('./TrailStore/TrailStore', () => ({ |
||||
getTrailStore: jest.fn(), |
||||
})); |
||||
|
||||
const onSelect = jest.fn(); |
||||
|
||||
describe('DataTrailsRecentMetrics', () => { |
||||
beforeEach(() => { |
||||
onSelect.mockClear(); |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [], |
||||
})); |
||||
}); |
||||
|
||||
it('renders the recent metrics header if there is at least one recent metric', () => { |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [ |
||||
{ |
||||
resolve: () => ({ state: { key: '1' } }), |
||||
}, |
||||
], |
||||
})); |
||||
render(<DataTrailsRecentMetrics onSelect={onSelect} />); |
||||
expect(screen.getByText('Or view a recent exploration')).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('does not show the "Show more" button if there are 3 or fewer recent metrics', () => { |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [ |
||||
{ |
||||
resolve: () => ({ state: { key: '1' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '2' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '3' } }), |
||||
}, |
||||
], |
||||
})); |
||||
render(<DataTrailsRecentMetrics onSelect={onSelect} />); |
||||
expect(screen.queryByText('Show more')).not.toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('shows the "Show more" button if there are more than 3 recent metrics', () => { |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [ |
||||
{ |
||||
resolve: () => ({ state: { key: '1' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '2' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '3' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '4' } }), |
||||
}, |
||||
], |
||||
})); |
||||
render(<DataTrailsRecentMetrics onSelect={onSelect} />); |
||||
expect(screen.getByText('Show more')).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('toggles between "Show more" and "Show less" when the button is clicked', () => { |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [ |
||||
{ |
||||
resolve: () => ({ state: { key: '1' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '2' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '3' } }), |
||||
}, |
||||
{ |
||||
resolve: () => ({ state: { key: '4' } }), |
||||
}, |
||||
], |
||||
})); |
||||
render(<DataTrailsRecentMetrics onSelect={onSelect} />); |
||||
const button = screen.getByText('Show more'); |
||||
fireEvent.click(button); |
||||
expect(screen.getByText('Show less')).toBeInTheDocument(); |
||||
fireEvent.click(button); |
||||
expect(screen.getByText('Show more')).toBeInTheDocument(); |
||||
}); |
||||
|
||||
it('selecting a recent exploration card takes you to the metric', () => { |
||||
const trail = new DataTrail({ key: '1', metric: 'select me' }); |
||||
const trailWithResolveMethod = new SceneObjectRef(trail); |
||||
(getTrailStore as jest.Mock).mockImplementation(() => ({ |
||||
bookmarks: [], |
||||
recent: [trailWithResolveMethod], |
||||
})); |
||||
render(<DataTrailsRecentMetrics onSelect={onSelect} />); |
||||
fireEvent.click(screen.getByText('select me')); |
||||
expect(onSelect).toHaveBeenCalledWith(trail); |
||||
}); |
||||
}); |
Loading…
Reference in new issue