forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Write tests for React Router+EUI helper components * Update generate_breadcrumbs test - add test suite for generateBreadcrumb() itself (in order to cover a missing branch) - minor lint fixes - remove unnecessary import from set_breadcrumbs test * Write test for get_username util + update test to return a more consistent falsey value (null) * Add test for SetupGuide * [Refactor] Pull out various Kibana context mocks into separate files - I'm creating a reusable useContext mock for shallow()ed enzyme components + add more documentation comments + examples * Write tests for empty state components + test new usecontext shallow mock * Empty state components: Add extra getUserName branch test * Write test for app search index/routes * Write tests for engine overview table + fix bonus bug * Write Engine Overview tests + Update EngineOverview logic to account for issues found during tests :) - Move http to async/await syntax instead of promise syntax (works better with existing HttpServiceMock jest.fn()s) - hasValidData wasn't strict enough in type checking/object nest checking and was causing the app itself to crash (no bueno) * Refactor EngineOverviewHeader test to use shallow + to full coverage - missed adding this test during telemetry work - switching to shallow and beforeAll reduces the test time from 5s to 4s! * [Refactor] Pull out React Router history mocks into a test util helper + minor refactors/updates * Add small tests to increase branch coverage - mostly testing fallbacks or removing fallbacks in favor of strict type interface - these are slightly obsessive so I'd also be fine ditching them if they aren't terribly valuable
- Loading branch information
Showing
20 changed files
with
741 additions
and
123 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
...prise_search/public/applications/app_search/components/empty_states/empty_states.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,62 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import '../../../test_utils/mock_shallow_usecontext'; | ||
|
||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
import { EuiEmptyPrompt, EuiCode, EuiLoadingContent } from '@elastic/eui'; | ||
|
||
jest.mock('../../utils/get_username', () => ({ getUserName: jest.fn() })); | ||
import { getUserName } from '../../utils/get_username'; | ||
|
||
import { ErrorState, NoUserState, EmptyState, LoadingState } from './'; | ||
|
||
describe('ErrorState', () => { | ||
it('renders', () => { | ||
const wrapper = shallow(<ErrorState />); | ||
const prompt = wrapper.find(EuiEmptyPrompt); | ||
|
||
expect(prompt).toHaveLength(1); | ||
expect(prompt.prop('title')).toEqual(<h2>Cannot connect to App Search</h2>); | ||
}); | ||
}); | ||
|
||
describe('NoUserState', () => { | ||
it('renders', () => { | ||
const wrapper = shallow(<NoUserState />); | ||
const prompt = wrapper.find(EuiEmptyPrompt); | ||
|
||
expect(prompt).toHaveLength(1); | ||
expect(prompt.prop('title')).toEqual(<h2>Cannot find App Search account</h2>); | ||
}); | ||
|
||
it('renders with username', () => { | ||
getUserName.mockImplementationOnce(() => 'dolores-abernathy'); | ||
const wrapper = shallow(<NoUserState />); | ||
const prompt = wrapper.find(EuiEmptyPrompt).dive(); | ||
|
||
expect(prompt.find(EuiCode).prop('children')).toContain('dolores-abernathy'); | ||
}); | ||
}); | ||
|
||
describe('EmptyState', () => { | ||
it('renders', () => { | ||
const wrapper = shallow(<EmptyState />); | ||
const prompt = wrapper.find(EuiEmptyPrompt); | ||
|
||
expect(prompt).toHaveLength(1); | ||
expect(prompt.prop('title')).toEqual(<h2>There’s nothing here yet</h2>); | ||
}); | ||
}); | ||
|
||
describe('LoadingState', () => { | ||
it('renders', () => { | ||
const wrapper = shallow(<LoadingState />); | ||
|
||
expect(wrapper.find(EuiLoadingContent)).toHaveLength(2); | ||
}); | ||
}); |
153 changes: 153 additions & 0 deletions
153
...search/public/applications/app_search/components/engine_overview/engine_overview.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,153 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import '../../../test_utils/mock_rr_usehistory'; | ||
|
||
import React from 'react'; | ||
import { act } from 'react-dom/test-utils'; | ||
import { render } from 'enzyme'; | ||
|
||
import { KibanaContext } from '../../../'; | ||
import { mountWithKibanaContext, mockKibanaContext } from '../../../test_utils'; | ||
|
||
import { EmptyState, ErrorState, NoUserState } from '../empty_states'; | ||
import { EngineTable } from './engine_table'; | ||
|
||
import { EngineOverview } from './'; | ||
|
||
describe('EngineOverview', () => { | ||
describe('non-happy-path states', () => { | ||
it('isLoading', () => { | ||
// We use render() instead of mount() here to not trigger lifecycle methods (i.e., useEffect) | ||
const wrapper = render( | ||
<KibanaContext.Provider value={{ http: {} }}> | ||
<EngineOverview /> | ||
</KibanaContext.Provider> | ||
); | ||
|
||
// render() directly renders HTML which means we have to look for selectors instead of for LoadingState directly | ||
expect(wrapper.find('.euiLoadingContent')).toHaveLength(2); | ||
}); | ||
|
||
it('isEmpty', async () => { | ||
const wrapper = await mountWithApiMock({ | ||
get: () => ({ | ||
results: [], | ||
meta: { page: { total_results: 0 } }, | ||
}), | ||
}); | ||
|
||
expect(wrapper.find(EmptyState)).toHaveLength(1); | ||
}); | ||
|
||
it('hasErrorConnecting', async () => { | ||
const wrapper = await mountWithApiMock({ | ||
get: () => ({ invalidPayload: true }), | ||
}); | ||
expect(wrapper.find(ErrorState)).toHaveLength(1); | ||
}); | ||
|
||
it('hasNoAccount', async () => { | ||
const wrapper = await mountWithApiMock({ | ||
get: () => ({ message: 'no-as-account' }), | ||
}); | ||
expect(wrapper.find(NoUserState)).toHaveLength(1); | ||
}); | ||
}); | ||
|
||
describe('happy-path states', () => { | ||
const mockedApiResponse = { | ||
results: [ | ||
{ | ||
name: 'hello-world', | ||
created_at: 'somedate', | ||
document_count: 50, | ||
field_count: 10, | ||
}, | ||
], | ||
meta: { | ||
page: { | ||
current: 1, | ||
total_pages: 10, | ||
total_results: 100, | ||
size: 10, | ||
}, | ||
}, | ||
}; | ||
const mockApi = jest.fn(() => mockedApiResponse); | ||
let wrapper; | ||
|
||
beforeAll(async () => { | ||
wrapper = await mountWithApiMock({ get: mockApi }); | ||
}); | ||
|
||
it('renders', () => { | ||
expect(wrapper.find(EngineTable)).toHaveLength(2); | ||
}); | ||
|
||
it('calls the engines API', () => { | ||
expect(mockApi).toHaveBeenNthCalledWith(1, '/api/app_search/engines', { | ||
query: { | ||
type: 'indexed', | ||
pageIndex: 1, | ||
}, | ||
}); | ||
expect(mockApi).toHaveBeenNthCalledWith(2, '/api/app_search/engines', { | ||
query: { | ||
type: 'meta', | ||
pageIndex: 1, | ||
}, | ||
}); | ||
}); | ||
|
||
describe('pagination', () => { | ||
const getTablePagination = () => | ||
wrapper | ||
.find(EngineTable) | ||
.first() | ||
.prop('pagination'); | ||
|
||
it('passes down page data from the API', () => { | ||
const pagination = getTablePagination(); | ||
|
||
expect(pagination.totalEngines).toEqual(100); | ||
expect(pagination.pageIndex).toEqual(0); | ||
}); | ||
|
||
it('re-polls the API on page change', async () => { | ||
await act(async () => getTablePagination().onPaginate(5)); | ||
wrapper.update(); | ||
|
||
expect(mockApi).toHaveBeenLastCalledWith('/api/app_search/engines', { | ||
query: { | ||
type: 'indexed', | ||
pageIndex: 5, | ||
}, | ||
}); | ||
expect(getTablePagination().pageIndex).toEqual(4); | ||
}); | ||
}); | ||
}); | ||
|
||
/** | ||
* Test helpers | ||
*/ | ||
|
||
const mountWithApiMock = async ({ get }) => { | ||
let wrapper; | ||
const httpMock = { ...mockKibanaContext.http, get }; | ||
|
||
// We get a lot of act() warning/errors in the terminal without this. | ||
// TBH, I don't fully understand why since Enzyme's mount is supposed to | ||
// have act() baked in - could be because of the wrapping context provider? | ||
await act(async () => { | ||
wrapper = mountWithKibanaContext(<EngineOverview />, { http: httpMock }); | ||
}); | ||
wrapper.update(); // This seems to be required for the DOM to actually update | ||
|
||
return wrapper; | ||
}; | ||
}); |
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
80 changes: 80 additions & 0 deletions
80
...se_search/public/applications/app_search/components/engine_overview/engine_table.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,80 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import React from 'react'; | ||
import { EuiBasicTable, EuiPagination, EuiButtonEmpty, EuiLink } from '@elastic/eui'; | ||
|
||
import { mountWithKibanaContext } from '../../../test_utils'; | ||
jest.mock('../../../shared/telemetry', () => ({ sendTelemetry: jest.fn() })); | ||
import { sendTelemetry } from '../../../shared/telemetry'; | ||
|
||
import { EngineTable } from './engine_table'; | ||
|
||
describe('EngineTable', () => { | ||
const onPaginate = jest.fn(); // onPaginate updates the engines API call upstream | ||
|
||
const wrapper = mountWithKibanaContext( | ||
<EngineTable | ||
data={[ | ||
{ | ||
name: 'test-engine', | ||
created_at: 'Fri, 1 Jan 1970 12:00:00 +0000', | ||
document_count: 99999, | ||
field_count: 10, | ||
}, | ||
]} | ||
pagination={{ | ||
totalEngines: 50, | ||
pageIndex: 0, | ||
onPaginate, | ||
}} | ||
/> | ||
); | ||
const table = wrapper.find(EuiBasicTable); | ||
|
||
it('renders', () => { | ||
expect(table).toHaveLength(1); | ||
expect(table.prop('pagination').totalItemCount).toEqual(50); | ||
|
||
const tableContent = table.text(); | ||
expect(tableContent).toContain('test-engine'); | ||
expect(tableContent).toContain('January 1, 1970'); | ||
expect(tableContent).toContain('99,999'); | ||
expect(tableContent).toContain('10'); | ||
|
||
expect(table.find(EuiPagination).find(EuiButtonEmpty)).toHaveLength(5); // Should display 5 pages at 10 engines per page | ||
}); | ||
|
||
it('contains engine links which send telemetry', () => { | ||
const engineLinks = wrapper.find(EuiLink); | ||
|
||
engineLinks.forEach(link => { | ||
expect(link.prop('href')).toEqual('http://localhost:3002/as/engines/test-engine'); | ||
link.simulate('click'); | ||
|
||
expect(sendTelemetry).toHaveBeenCalledWith({ | ||
http: expect.any(Object), | ||
product: 'app_search', | ||
action: 'clicked', | ||
metric: 'engine_table_link', | ||
}); | ||
}); | ||
}); | ||
|
||
it('triggers onPaginate', () => { | ||
table.prop('onChange')({ page: { index: 4 } }); | ||
|
||
expect(onPaginate).toHaveBeenCalledWith(5); | ||
}); | ||
|
||
it('handles empty data', () => { | ||
const emptyWrapper = mountWithKibanaContext( | ||
<EngineTable data={[]} pagination={{ totalEngines: 0 }} /> | ||
); | ||
const emptyTable = wrapper.find(EuiBasicTable); | ||
expect(emptyTable.prop('pagination').pageIndex).toEqual(0); | ||
}); | ||
}); |
Oops, something went wrong.