Skip to content

Commit

Permalink
Suggestion edits and error handling
Browse files Browse the repository at this point in the history
I had to make edits after accepting suggestions. I also added the
tests for the corresponding error handling that was added.
  • Loading branch information
JasonStoltz committed Sep 22, 2020
1 parent 0ede789 commit 77e519b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.
*/

/*
* This function ends the current event loop which will give all promises a chance
* to complete. It is similar to calling `setTimeout` without specifying a length of
* time.
*
* For example:
* // Mock http.get to return a rejected promise immediately
* (HttpLogic.values.http.get as jest.Mock).mockReturnValue(Promise.reject('An error occured'));
* // Call an action, which calls http.get and awaits it
* CredentialsLogic.actions.fetchCredentials(2);
* // Give the http.get promise a chance to be rejected
* await flushPromises();
* // Make assertions
* expect(flashAPIErrors).toHaveBeenCalledWith('An error occured');
*/
export function flushPromises() {
return new Promise((resolve) => setImmediate(resolve));
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ jest.mock('../../../shared/http', () => ({
HttpLogic: { values: { http: { get: jest.fn(), delete: jest.fn() } } },
}));
import { HttpLogic } from '../../../shared/http';
jest.mock('../../../shared/flash_messages', () => ({
flashAPIErrors: jest.fn(),
}));
import { flashAPIErrors } from '../../../shared/flash_messages';
import { IApiToken, ICredentialsDetails } from '../../../../../common/types/app_search';
import { IMeta } from '../../../../../common/types';
import { flushPromises } from '../../../../../common/__mocks__/flush_promises';

describe('CredentialsLogic', () => {
const DEFAULT_VALUES = {
Expand Down Expand Up @@ -47,7 +52,7 @@ describe('CredentialsLogic', () => {
defaults: {
enterprise_search: {
app_search: {
credentials: {
credentials_logic: {
...defaults,
},
},
Expand Down Expand Up @@ -924,6 +929,7 @@ describe('CredentialsLogic', () => {

describe('initializeCredentialsData', () => {
it('should call fetchCredentials and fetchDetails', () => {
mount();
jest.spyOn(CredentialsLogic.actions, 'fetchCredentials').mockImplementationOnce(() => {});
jest.spyOn(CredentialsLogic.actions, 'fetchDetails').mockImplementationOnce(() => {});
CredentialsLogic.actions.initializeCredentialsData();
Expand All @@ -944,60 +950,81 @@ describe('CredentialsLogic', () => {
const results: IApiToken[] = [];

it('will call an API endpoint and set the results with the `setCredentialsData` action', async () => {
mount();
jest.spyOn(CredentialsLogic.actions, 'setCredentialsData').mockImplementationOnce(() => {});
(HttpLogic.values.http.get as jest.Mock).mockReturnValue({
then: (fn: (response: { meta: IMeta; results: IApiToken[] }) => object) =>
fn({ meta, results }),
});
await CredentialsLogic.actions.fetchCredentials(2);
(HttpLogic.values.http.get as jest.Mock).mockReturnValue(Promise.resolve({ meta, results }));

CredentialsLogic.actions.fetchCredentials(2);

expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/app_search/credentials', {
query: {
'page[current]': 2,
},
});
await flushPromises();
expect(CredentialsLogic.actions.setCredentialsData).toHaveBeenCalledWith(meta, results);
});

it('handles errors', async () => {
// TODO when error handler is available
mount();
(HttpLogic.values.http.get as jest.Mock).mockReturnValue(Promise.reject('An error occured'));

CredentialsLogic.actions.fetchCredentials(2);

await flushPromises();
expect(flashAPIErrors).toHaveBeenCalledWith('An error occured');
});
});
describe('fetchDetails', () => {
it('will call an API endpoint and set the results with the `setCredentialsDetails` action', async () => {
mount();
jest
.spyOn(CredentialsLogic.actions, 'setCredentialsDetails')
.mockImplementationOnce(() => {});
(HttpLogic.values.http.get as jest.Mock).mockReturnValue({
then: (fn: (response: ICredentialsDetails) => object) => fn(credentialsDetails),
});
await CredentialsLogic.actions.fetchDetails();
(HttpLogic.values.http.get as jest.Mock).mockReturnValue(Promise.resolve(credentialsDetails));
CredentialsLogic.actions.fetchDetails();
expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/app_search/credentials/details');
await flushPromises();
expect(CredentialsLogic.actions.setCredentialsDetails).toHaveBeenCalledWith(
credentialsDetails
);
});

it('handles errors', async () => {
// TODO when error handler is available
mount();
(HttpLogic.values.http.get as jest.Mock).mockReturnValue(Promise.reject('An error occured'));

CredentialsLogic.actions.fetchDetails();

await flushPromises();
expect(flashAPIErrors).toHaveBeenCalledWith('An error occured');
});
});
describe('deleteApiKey', () => {
const tokenName = 'abc123';

it('will call an API endpoint and set the results with the `onApiKeyDelete` action', async () => {
mount();
jest.spyOn(CredentialsLogic.actions, 'onApiKeyDelete').mockImplementationOnce(() => {});
(HttpLogic.values.http.delete as jest.Mock).mockReturnValue({
then: (fn: () => object) => fn(),
});
await CredentialsLogic.actions.deleteApiKey(tokenName);
(HttpLogic.values.http.delete as jest.Mock).mockReturnValue(Promise.resolve());
CredentialsLogic.actions.deleteApiKey(tokenName);
expect(HttpLogic.values.http.delete).toHaveBeenCalledWith(
`/api/app_search/credentials/${tokenName}`
);
await flushPromises();
expect(CredentialsLogic.actions.onApiKeyDelete).toHaveBeenCalledWith(tokenName);
});

it('handles errors', async () => {
// TODO when error handler is available
mount();
(HttpLogic.values.http.delete as jest.Mock).mockReturnValue(
Promise.reject('An error occured')
);

CredentialsLogic.actions.deleteApiKey(tokenName);

await flushPromises();
expect(flashAPIErrors).toHaveBeenCalledWith('An error occured');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ADMIN, PRIVATE } from './constants';
import { HttpLogic } from '../../../shared/http';
import { IApiToken, IEngine, ICredentialsDetails } from '../../../../../common/types/app_search';
import { IMeta } from '../../../../../common/types';
import { flashAPIErrors } from '../../../shared/flash_messages';

const defaultApiToken: IApiToken = {
name: '',
Expand Down Expand Up @@ -237,7 +238,6 @@ export const CredentialsLogic = kea<
const { http } = HttpLogic.values;
const query = { 'page[current]': page };
const response = await http.get('/api/app_search/credentials', { query });

actions.setCredentialsData(response.meta, response.results);
} catch (e) {
flashAPIErrors(e);
Expand All @@ -248,7 +248,7 @@ export const CredentialsLogic = kea<
const { http } = HttpLogic.values;
const response = await http.get('/api/app_search/credentials/details');

actions.setCredentialsDetails(response));
actions.setCredentialsDetails(response);
} catch (e) {
flashAPIErrors(e);
}
Expand All @@ -258,7 +258,7 @@ export const CredentialsLogic = kea<
const { http } = HttpLogic.values;
await http.delete(`/api/app_search/credentials/${tokenName}`);

actions.onApiKeyDelete(tokenName));
actions.onApiKeyDelete(tokenName);
} catch (e) {
flashAPIErrors(e);
}
Expand Down

0 comments on commit 77e519b

Please sign in to comment.