diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx
new file mode 100644
index 0000000000000..19ea683eb878c
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx
@@ -0,0 +1,56 @@
+/*
+ * 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 { EngineOverviewHeader } from '../engine_overview_header';
+import { mountWithKibanaContext } from '../../../test_utils/helpers';
+
+describe('EngineOverviewHeader', () => {
+ describe('when enterpriseSearchUrl is set', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mountWithKibanaContext(, {
+ enterpriseSearchUrl: 'http://localhost:3002',
+ });
+ });
+
+ describe('the Launch App Search button', () => {
+ const subject = () => wrapper.find('EuiButton[data-test-subj="launchButton"]');
+
+ it('should not be disabled', () => {
+ expect(subject().props().isDisabled).toBeFalsy();
+ });
+
+ it('should use the enterpriseSearchUrl as the base path for its href', () => {
+ expect(subject().props().href).toBe('http://localhost:3002/as');
+ });
+ });
+ });
+
+ describe('when enterpriseSearchUrl is not set', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = mountWithKibanaContext(, {
+ enterpriseSearchUrl: undefined,
+ });
+ });
+
+ describe('the Launch App Search button', () => {
+ const subject = () => wrapper.find('EuiButton[data-test-subj="launchButton"]');
+
+ it('should be disabled', () => {
+ expect(subject().props().isDisabled).toBe(true);
+ });
+
+ it('should not have an href', () => {
+ expect(subject().props().href).toBeUndefined();
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx
index 14b2d00668c0d..69bfb9ad124eb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx
@@ -15,6 +15,7 @@ export const EngineOverviewHeader: React.FC<> = () => {
const buttonProps = {
fill: true,
iconType: 'popout',
+ ['data-test-subj']: 'launchButton',
};
if (enterpriseSearchUrl) {
buttonProps.href = `${enterpriseSearchUrl}/as`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.ts b/x-pack/plugins/enterprise_search/public/applications/index.test.ts
new file mode 100644
index 0000000000000..7ece7e153c154
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/index.test.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 { coreMock } from 'src/core/public/mocks';
+import { renderApp } from '../applications';
+
+describe('renderApp', () => {
+ it('mounts and unmounts UI', () => {
+ const params = coreMock.createAppMountParamters();
+ const core = coreMock.createStart();
+
+ const unmount = renderApp(core, params, {});
+ expect(params.element.querySelector('.setup-guide')).not.toBeNull();
+ unmount();
+ expect(params.element.innerHTML).toEqual('');
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.test.ts
new file mode 100644
index 0000000000000..aa2b584d98425
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.test.ts
@@ -0,0 +1,151 @@
+/*
+ * 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 { appSearchBreadcrumbs, enterpriseSearchBreadcrumbs } from '../kibana_breadcrumbs';
+
+jest.mock('../react_router_helpers', () => ({
+ letBrowserHandleEvent: () => false,
+}));
+
+describe('appSearchBreadcrumbs', () => {
+ const historyMock = {
+ createHref: jest.fn().mockImplementation(path => path.pathname),
+ push: jest.fn(),
+ };
+
+ const breadCrumbs = [
+ {
+ text: 'Page 1',
+ path: '/page1',
+ },
+ {
+ text: 'Page 2',
+ path: '/page2',
+ },
+ ];
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const subject = () => appSearchBreadcrumbs(historyMock)(breadCrumbs);
+
+ it('Builds a chain of breadcrumbs with Enterprise Search and App Search at the root', () => {
+ expect(subject()).toEqual([
+ {
+ href: '/',
+ onClick: expect.any(Function),
+ text: 'Enterprise Search',
+ },
+ {
+ href: '/app_search',
+ onClick: expect.any(Function),
+ text: 'App Search',
+ },
+ {
+ href: '/page1',
+ onClick: expect.any(Function),
+ text: 'Page 1',
+ },
+ {
+ href: '/page2',
+ onClick: expect.any(Function),
+ text: 'Page 2',
+ },
+ ]);
+ });
+
+ describe('links', () => {
+ const eventMock = {
+ preventDefault: jest.fn(),
+ };
+
+ it('has a link to Enterprise Search Home page first', () => {
+ subject()[0].onClick(eventMock);
+ expect(historyMock.push).toHaveBeenCalledWith('/');
+ });
+
+ it('has a link to App Search second', () => {
+ subject()[1].onClick(eventMock);
+ expect(historyMock.push).toHaveBeenCalledWith('/app_search');
+ });
+
+ it('has a link to page 1 third', () => {
+ subject()[2].onClick(eventMock);
+ expect(historyMock.push).toHaveBeenCalledWith('/page1');
+ });
+
+ it('has a link to page 2 last', () => {
+ subject()[3].onClick(eventMock);
+ expect(historyMock.push).toHaveBeenCalledWith('/page2');
+ });
+ });
+});
+
+describe('enterpriseSearchBreadcrumbs', () => {
+ const historyMock = {
+ createHref: jest.fn(),
+ push: jest.fn(),
+ };
+
+ const breadCrumbs = [
+ {
+ text: 'Page 1',
+ path: '/page1',
+ },
+ {
+ text: 'Page 2',
+ path: '/page2',
+ },
+ ];
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const subject = () => enterpriseSearchBreadcrumbs(historyMock)(breadCrumbs);
+
+ it('Builds a chain of breadcrumbs with Enterprise Search at the root', () => {
+ expect(subject()).toEqual([
+ {
+ href: undefined,
+ onClick: expect.any(Function),
+ text: 'Enterprise Search',
+ },
+ {
+ href: undefined,
+ onClick: expect.any(Function),
+ text: 'Page 1',
+ },
+ {
+ href: undefined,
+ onClick: expect.any(Function),
+ text: 'Page 2',
+ },
+ ]);
+ });
+
+ describe('links', () => {
+ const eventMock = {
+ preventDefault: jest.fn(),
+ };
+
+ it('has a link to Enterprise Search Home page first', () => {
+ subject()[0].onClick(eventMock);
+ expect(historyMock.push).toHaveBeenCalledWith('/');
+ });
+
+ it('has a link to page 1 second', () => {
+ subject()[1].onClick(eventMock);
+ expect(historyMock.push).toHaveBeenCalledWith('/page1');
+ });
+
+ it('has a link to page 2 last', () => {
+ subject()[2].onClick(eventMock);
+ expect(historyMock.push).toHaveBeenCalledWith('/page2');
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.test.tsx
new file mode 100644
index 0000000000000..788800d86ec84
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.test.tsx
@@ -0,0 +1,75 @@
+/*
+ * 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 { SetAppSearchBreadcrumbs } from '../kibana_breadcrumbs';
+import { mountWithKibanaContext } from '../../test_utils/helpers';
+
+jest.mock('./generate_breadcrumbs', () => ({
+ appSearchBreadcrumbs: jest.fn(),
+}));
+import { appSearchBreadcrumbs } from './generate_breadcrumbs';
+
+jest.mock('react-router-dom', () => ({
+ useHistory: () => ({
+ createHref: jest.fn(),
+ push: jest.fn(),
+ location: {
+ pathname: '/current-path',
+ },
+ }),
+}));
+
+describe('SetAppSearchBreadcrumbs', () => {
+ const setBreadcrumbs = jest.fn();
+ const builtBreadcrumbs = [];
+ const appSearchBreadCrumbsInnerCall = jest.fn().mockReturnValue(builtBreadcrumbs);
+ const appSearchBreadCrumbsOuterCall = jest.fn().mockReturnValue(appSearchBreadCrumbsInnerCall);
+ appSearchBreadcrumbs.mockImplementation(appSearchBreadCrumbsOuterCall);
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const mountSetAppSearchBreadcrumbs = props => {
+ return mountWithKibanaContext(, {
+ http: {},
+ enterpriseSearchUrl: 'http://localhost:3002',
+ setBreadcrumbs,
+ });
+ };
+
+ describe('when isRoot is false', () => {
+ const subject = () => mountSetAppSearchBreadcrumbs({ text: 'Page 1', isRoot: false });
+
+ it('calls appSearchBreadcrumbs to build breadcrumbs, then registers them with Kibana', () => {
+ subject();
+
+ // calls appSearchBreadcrumbs to build breadcrumbs with the target page and current location
+ expect(appSearchBreadCrumbsInnerCall).toHaveBeenCalledWith([
+ { text: 'Page 1', path: '/current-path' },
+ ]);
+
+ // then registers them with Kibana
+ expect(setBreadcrumbs).toHaveBeenCalledWith(builtBreadcrumbs);
+ });
+ });
+
+ describe('when isRoot is true', () => {
+ const subject = () => mountSetAppSearchBreadcrumbs({ text: 'Page 1', isRoot: true });
+
+ it('calls appSearchBreadcrumbs to build breadcrumbs with an empty breadcrumb, then registers them with Kibana', () => {
+ subject();
+
+ // uses an empty bredcrumb
+ expect(appSearchBreadCrumbsInnerCall).toHaveBeenCalledWith([]);
+
+ // then registers them with Kibana
+ expect(setBreadcrumbs).toHaveBeenCalledWith(builtBreadcrumbs);
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.test.ts
new file mode 100644
index 0000000000000..49ab5ed920e36
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.test.ts
@@ -0,0 +1,102 @@
+/*
+ * 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 { letBrowserHandleEvent } from '../react_router_helpers';
+
+describe('letBrowserHandleEvent', () => {
+ const event = {
+ defaultPrevented: false,
+ metaKey: false,
+ altKey: false,
+ ctrlKey: false,
+ shiftKey: false,
+ button: 0,
+ target: {
+ getAttribute: () => '_self',
+ },
+ };
+
+ describe('the browser should handle the link when', () => {
+ it('default is prevented', () => {
+ expect(letBrowserHandleEvent({ ...event, defaultPrevented: true })).toBe(true);
+ });
+
+ it('is modified with metaKey', () => {
+ expect(letBrowserHandleEvent({ ...event, metaKey: true })).toBe(true);
+ });
+
+ it('is modified with altKey', () => {
+ expect(letBrowserHandleEvent({ ...event, altKey: true })).toBe(true);
+ });
+
+ it('is modified with ctrlKey', () => {
+ expect(letBrowserHandleEvent({ ...event, ctrlKey: true })).toBe(true);
+ });
+
+ it('is modified with shiftKey', () => {
+ expect(letBrowserHandleEvent({ ...event, shiftKey: true })).toBe(true);
+ });
+
+ it('it is not a left click event', () => {
+ expect(letBrowserHandleEvent({ ...event, button: 2 })).toBe(true);
+ });
+
+ it('the target is anything value other than _self', () => {
+ expect(
+ letBrowserHandleEvent({
+ ...event,
+ target: targetValue('_blank'),
+ })
+ ).toBe(true);
+ });
+ });
+
+ describe('the browser should NOT handle the link when', () => {
+ it('default is not prevented', () => {
+ expect(letBrowserHandleEvent({ ...event, defaultPrevented: false })).toBe(false);
+ });
+
+ it('is not modified', () => {
+ expect(
+ letBrowserHandleEvent({
+ ...event,
+ metaKey: false,
+ altKey: false,
+ ctrlKey: false,
+ shiftKey: false,
+ })
+ ).toBe(false);
+ });
+
+ it('it is a left click event', () => {
+ expect(letBrowserHandleEvent({ ...event, button: 0 })).toBe(false);
+ });
+
+ it('the target is a value of _self', () => {
+ expect(
+ letBrowserHandleEvent({
+ ...event,
+ target: targetValue('_self'),
+ })
+ ).toBe(false);
+ });
+
+ it('the target has no value', () => {
+ expect(
+ letBrowserHandleEvent({
+ ...event,
+ target: targetValue(null),
+ })
+ ).toBe(false);
+ });
+ });
+});
+
+const targetValue = value => {
+ return {
+ getAttribute: () => value,
+ };
+};
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.ts
similarity index 95%
rename from x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.tsx
rename to x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.ts
index dba5d576faa7d..bb87ecaf6877b 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.ts
@@ -26,5 +26,5 @@ const isLeftClickEvent: THandleEvent = event => event.button === 0;
const isTargetBlank: THandleEvent = event => {
const target = event.target.getAttribute('target');
- return target && target !== '_self';
+ return !!target && target !== '_self';
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/test_utils/helpers.tsx b/x-pack/plugins/enterprise_search/public/applications/test_utils/helpers.tsx
new file mode 100644
index 0000000000000..9343e927e82ac
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/test_utils/helpers.tsx
@@ -0,0 +1,25 @@
+/*
+ * 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 { mount } from 'enzyme';
+
+import { KibanaContext } from '..';
+
+export const mountWithKibanaContext = (node, contextProps) => {
+ return mount(
+
+ {node}
+
+ );
+};