Skip to content

Commit

Permalink
[Enterprise Search] Convert IndexingStatus to use logic for fetching (#…
Browse files Browse the repository at this point in the history
…84710) (#84825)

* Add IndexingStatusLogic

* Replace IndexingStatusFetcher with logic

* Refactor out unnecessary conditional

onComplete is not optional so these if blocks can be consolidated

* Misc styling - destructuring and typing

Co-authored-by: Constance <[email protected]>

* Misc styling - imports

Co-authored-by: Constance <[email protected]>

* Remove div

* Refactor test

* Replace method with string for statusPath

In ent-search, we use Rails helpers to generate paths. These were in the form of routes.whateverPath(). We passed these method to the IndexingStatus component to generate the app-specific rotues in the shared component.

In Kibana, we will not have these generators and should instead pass the path strings directly

Co-authored-by: Constance <[email protected]>

Co-authored-by: Constance <[email protected]>
  • Loading branch information
scottybollinger and Constance authored Dec 2, 2020
1 parent 9d71a8a commit b9c3a96
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,61 @@
* you may not use this file except in compliance with the Elastic License.
*/

import '../../__mocks__/kea.mock';
import '../../__mocks__/shallow_useeffect.mock';

import { setMockActions, setMockValues } from '../../__mocks__';

import React from 'react';
import { shallow } from 'enzyme';

import { EuiPanel } from '@elastic/eui';

import { IndexingStatusContent } from './indexing_status_content';
import { IndexingStatusErrors } from './indexing_status_errors';
import { IndexingStatusFetcher } from './indexing_status_fetcher';
import { IndexingStatus } from './indexing_status';

describe('IndexingStatus', () => {
const getItemDetailPath = jest.fn();
const getStatusPath = jest.fn();
const onComplete = jest.fn();
const setGlobalIndexingStatus = jest.fn();
const fetchIndexingStatus = jest.fn();

const props = {
percentageComplete: 50,
numDocumentsWithErrors: 1,
activeReindexJobId: 12,
viewLinkPath: '/path',
statusPath: '/other_path',
itemId: '1',
getItemDetailPath,
getStatusPath,
onComplete,
setGlobalIndexingStatus,
};

beforeEach(() => {
setMockActions({ fetchIndexingStatus });
});

it('renders', () => {
setMockValues({
percentageComplete: 50,
numDocumentsWithErrors: 0,
});
const wrapper = shallow(<IndexingStatus {...props} />);
const fetcher = wrapper.find(IndexingStatusFetcher).prop('children')(
props.percentageComplete,
props.numDocumentsWithErrors
);

expect(shallow(fetcher).find(EuiPanel)).toHaveLength(1);
expect(shallow(fetcher).find(IndexingStatusContent)).toHaveLength(1);
expect(wrapper.find(EuiPanel)).toHaveLength(1);
expect(wrapper.find(IndexingStatusContent)).toHaveLength(1);
expect(fetchIndexingStatus).toHaveBeenCalled();
});

it('renders errors', () => {
const wrapper = shallow(<IndexingStatus {...props} percentageComplete={100} />);
const fetcher = wrapper.find(IndexingStatusFetcher).prop('children')(100, 1);
expect(shallow(fetcher).find(IndexingStatusErrors)).toHaveLength(1);
setMockValues({
percentageComplete: 100,
numDocumentsWithErrors: 1,
});
const wrapper = shallow(<IndexingStatus {...props} />);

expect(wrapper.find(IndexingStatusErrors)).toHaveLength(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,52 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import React, { useEffect } from 'react';

import { useValues, useActions } from 'kea';

import { EuiPanel, EuiSpacer } from '@elastic/eui';

import { IndexingStatusContent } from './indexing_status_content';
import { IndexingStatusErrors } from './indexing_status_errors';
import { IndexingStatusFetcher } from './indexing_status_fetcher';
import { IndexingStatusLogic } from './indexing_status_logic';

import { IIndexingStatus } from '../types';

export interface IIndexingStatusProps extends IIndexingStatus {
export interface IIndexingStatusProps {
viewLinkPath: string;
itemId: string;
statusPath: string;
getItemDetailPath?(itemId: string): string;
getStatusPath(itemId: string, activeReindexJobId: number): string;
onComplete(numDocumentsWithErrors: number): void;
setGlobalIndexingStatus?(activeReindexJob: IIndexingStatus): void;
}

export const IndexingStatus: React.FC<IIndexingStatusProps> = (props) => (
<IndexingStatusFetcher {...props}>
{(percentageComplete, numDocumentsWithErrors) => (
<div>
{percentageComplete < 100 && (
<EuiPanel paddingSize="l" hasShadow>
<IndexingStatusContent percentageComplete={percentageComplete} />
</EuiPanel>
)}
{percentageComplete === 100 && numDocumentsWithErrors > 0 && (
<>
<EuiSpacer />
<IndexingStatusErrors viewLinkPath={props.viewLinkPath} />
</>
)}
</div>
)}
</IndexingStatusFetcher>
);
export const IndexingStatus: React.FC<IIndexingStatusProps> = ({
viewLinkPath,
statusPath,
onComplete,
}) => {
const { percentageComplete, numDocumentsWithErrors } = useValues(IndexingStatusLogic);
const { fetchIndexingStatus } = useActions(IndexingStatusLogic);

useEffect(() => {
fetchIndexingStatus({ statusPath, onComplete });
}, []);

return (
<>
{percentageComplete < 100 && (
<EuiPanel paddingSize="l" hasShadow={true}>
<IndexingStatusContent percentageComplete={percentageComplete} />
</EuiPanel>
)}
{percentageComplete === 100 && numDocumentsWithErrors > 0 && (
<>
<EuiSpacer />
<IndexingStatusErrors viewLinkPath={viewLinkPath} />
</>
)}
</>
);
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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 { resetContext } from 'kea';

jest.mock('../http', () => ({
HttpLogic: {
values: { http: { get: jest.fn() } },
},
}));
import { HttpLogic } from '../http';

jest.mock('../flash_messages', () => ({
flashAPIErrors: jest.fn(),
}));
import { flashAPIErrors } from '../flash_messages';

import { IndexingStatusLogic } from './indexing_status_logic';

describe('IndexingStatusLogic', () => {
let unmount: any;

const mockStatusResponse = {
percentageComplete: 50,
numDocumentsWithErrors: 3,
activeReindexJobId: 1,
};

beforeEach(() => {
jest.clearAllMocks();
resetContext({});
unmount = IndexingStatusLogic.mount();
});

it('has expected default values', () => {
expect(IndexingStatusLogic.values).toEqual({
percentageComplete: 100,
numDocumentsWithErrors: 0,
});
});

describe('setIndexingStatus', () => {
it('sets reducers', () => {
IndexingStatusLogic.actions.setIndexingStatus(mockStatusResponse);

expect(IndexingStatusLogic.values.percentageComplete).toEqual(
mockStatusResponse.percentageComplete
);
expect(IndexingStatusLogic.values.numDocumentsWithErrors).toEqual(
mockStatusResponse.numDocumentsWithErrors
);
});
});

describe('fetchIndexingStatus', () => {
jest.useFakeTimers();
const statusPath = '/api/workplace_search/path/123';
const onComplete = jest.fn();
const TIMEOUT = 3000;

it('calls API and sets values', async () => {
const setIndexingStatusSpy = jest.spyOn(IndexingStatusLogic.actions, 'setIndexingStatus');
const promise = Promise.resolve(mockStatusResponse);
(HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);

IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete });
jest.advanceTimersByTime(TIMEOUT);

expect(HttpLogic.values.http.get).toHaveBeenCalledWith(statusPath);
await promise;

expect(setIndexingStatusSpy).toHaveBeenCalledWith(mockStatusResponse);
});

it('handles error', async () => {
const promise = Promise.reject('An error occured');
(HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);

IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete });
jest.advanceTimersByTime(TIMEOUT);

try {
await promise;
} catch {
// Do nothing
}
expect(flashAPIErrors).toHaveBeenCalledWith('An error occured');
});

it('handles indexing complete state', async () => {
const promise = Promise.resolve({ ...mockStatusResponse, percentageComplete: 100 });
(HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise);
IndexingStatusLogic.actions.fetchIndexingStatus({ statusPath, onComplete });
jest.advanceTimersByTime(TIMEOUT);

await promise;

expect(clearInterval).toHaveBeenCalled();
expect(onComplete).toHaveBeenCalledWith(mockStatusResponse.numDocumentsWithErrors);
});

it('handles unmounting', async () => {
unmount();
expect(clearInterval).toHaveBeenCalled();
});
});
});
Loading

0 comments on commit b9c3a96

Please sign in to comment.