Skip to content

Commit fea77cd

Browse files
[7.x] [Component template] Details flyout (#68732) (#69356)
1 parent cab833e commit fea77cd

File tree

29 files changed

+1044
-41
lines changed

29 files changed

+1044
-41
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { act } from 'react-dom/test-utils';
8+
9+
import { setupEnvironment, pageHelpers } from './helpers';
10+
import { ComponentTemplateDetailsTestBed } from './helpers/component_template_details.helpers';
11+
import { ComponentTemplateDeserialized } from '../../shared_imports';
12+
13+
const { setup } = pageHelpers.componentTemplateDetails;
14+
15+
jest.mock('ui/i18n', () => {
16+
const I18nContext = ({ children }: any) => children;
17+
return { I18nContext };
18+
});
19+
20+
const COMPONENT_TEMPLATE: ComponentTemplateDeserialized = {
21+
name: 'comp-1',
22+
template: {
23+
mappings: { properties: { ip_address: { type: 'ip' } } },
24+
aliases: { mydata: {} },
25+
settings: { number_of_shards: 1 },
26+
},
27+
version: 1,
28+
_meta: { description: 'component template test' },
29+
_kbnMeta: { usedBy: ['template_1'] },
30+
};
31+
32+
const COMPONENT_TEMPLATE_ONLY_REQUIRED_FIELDS: ComponentTemplateDeserialized = {
33+
name: 'comp-base',
34+
template: {},
35+
_kbnMeta: { usedBy: [] },
36+
};
37+
38+
describe('<ComponentTemplateDetails />', () => {
39+
const { server, httpRequestsMockHelpers } = setupEnvironment();
40+
let testBed: ComponentTemplateDetailsTestBed;
41+
42+
afterAll(() => {
43+
server.restore();
44+
});
45+
46+
describe('With component template details', () => {
47+
beforeEach(async () => {
48+
httpRequestsMockHelpers.setLoadComponentTemplateResponse(COMPONENT_TEMPLATE);
49+
50+
await act(async () => {
51+
testBed = setup({
52+
componentTemplateName: COMPONENT_TEMPLATE.name,
53+
onClose: () => {},
54+
});
55+
});
56+
57+
testBed.component.update();
58+
});
59+
60+
test('renders the details flyout', () => {
61+
const { exists, find, actions, component } = testBed;
62+
63+
// Verify flyout exists with correct title
64+
expect(exists('componentTemplateDetails')).toBe(true);
65+
expect(find('componentTemplateDetails.title').text()).toBe(COMPONENT_TEMPLATE.name);
66+
67+
// Verify footer does not display since "actions" prop was not provided
68+
expect(exists('componentTemplateDetails.footer')).toBe(false);
69+
70+
// Verify tabs exist
71+
expect(exists('settingsTab')).toBe(true);
72+
expect(exists('mappingsTab')).toBe(true);
73+
expect(exists('aliasesTab')).toBe(true);
74+
// Summary tab should be active by default
75+
expect(find('summaryTab').props()['aria-selected']).toBe(true);
76+
77+
// [Summary tab] Verify description list items
78+
expect(exists('summaryTabContent.usedByTitle')).toBe(true);
79+
expect(exists('summaryTabContent.versionTitle')).toBe(true);
80+
expect(exists('summaryTabContent.metaTitle')).toBe(true);
81+
82+
// [Settings tab] Navigate to tab and verify content
83+
act(() => {
84+
actions.clickSettingsTab();
85+
});
86+
87+
component.update();
88+
89+
expect(exists('settingsTabContent')).toBe(true);
90+
91+
// [Mappings tab] Navigate to tab and verify content
92+
act(() => {
93+
actions.clickMappingsTab();
94+
});
95+
96+
component.update();
97+
expect(exists('mappingsTabContent')).toBe(true);
98+
99+
// [Aliases tab] Navigate to tab and verify content
100+
act(() => {
101+
actions.clickAliasesTab();
102+
});
103+
104+
component.update();
105+
expect(exists('aliasesTabContent')).toBe(true);
106+
});
107+
});
108+
109+
describe('With only required component template fields', () => {
110+
beforeEach(async () => {
111+
httpRequestsMockHelpers.setLoadComponentTemplateResponse(
112+
COMPONENT_TEMPLATE_ONLY_REQUIRED_FIELDS
113+
);
114+
115+
await act(async () => {
116+
testBed = setup({
117+
componentTemplateName: COMPONENT_TEMPLATE_ONLY_REQUIRED_FIELDS.name,
118+
onClose: () => {},
119+
});
120+
});
121+
122+
testBed.component.update();
123+
});
124+
125+
test('renders the details flyout', () => {
126+
const { exists, actions, component } = testBed;
127+
128+
// [Summary tab] Verify optional description list items do not display
129+
expect(exists('summaryTabContent.usedByTitle')).toBe(false);
130+
expect(exists('summaryTabContent.versionTitle')).toBe(false);
131+
expect(exists('summaryTabContent.metaTitle')).toBe(false);
132+
// Verify callout renders indicating the component template is not in use
133+
expect(exists('notInUseCallout')).toBe(true);
134+
135+
// [Settings tab] Navigate to tab and verify info callout
136+
act(() => {
137+
actions.clickSettingsTab();
138+
});
139+
140+
component.update();
141+
142+
expect(exists('noSettingsCallout')).toBe(true);
143+
144+
// [Mappings tab] Navigate to tab and verify info callout
145+
act(() => {
146+
actions.clickMappingsTab();
147+
});
148+
149+
component.update();
150+
expect(exists('noMappingsCallout')).toBe(true);
151+
152+
// [Aliases tab] Navigate to tab and verify info callout
153+
act(() => {
154+
actions.clickAliasesTab();
155+
});
156+
157+
component.update();
158+
expect(exists('noAliasesCallout')).toBe(true);
159+
});
160+
});
161+
162+
describe('With actions', () => {
163+
beforeEach(async () => {
164+
httpRequestsMockHelpers.setLoadComponentTemplateResponse(COMPONENT_TEMPLATE);
165+
166+
await act(async () => {
167+
testBed = setup({
168+
componentTemplateName: COMPONENT_TEMPLATE.name,
169+
onClose: () => {},
170+
actions: [
171+
{
172+
name: 'Test',
173+
icon: 'info',
174+
closePopoverOnClick: true,
175+
handleActionClick: () => {},
176+
},
177+
],
178+
});
179+
});
180+
181+
testBed.component.update();
182+
});
183+
184+
test('should render a footer with context menu', () => {
185+
const { exists, actions, component, find } = testBed;
186+
187+
// Verify footer exists
188+
expect(exists('componentTemplateDetails.footer')).toBe(true);
189+
expect(exists('manageComponentTemplateButton')).toBe(true);
190+
191+
// Click manage button and verify actions
192+
act(() => {
193+
actions.clickManageButton();
194+
});
195+
196+
component.update();
197+
198+
expect(exists('manageComponentTemplateContextMenu')).toBe(true);
199+
expect(find('manageComponentTemplateContextMenu.action').length).toEqual(1);
200+
});
201+
});
202+
203+
describe('Error handling', () => {
204+
const error = {
205+
status: 500,
206+
error: 'Internal server error',
207+
message: 'Internal server error',
208+
};
209+
210+
beforeEach(async () => {
211+
httpRequestsMockHelpers.setLoadComponentTemplateResponse(undefined, { body: error });
212+
213+
await act(async () => {
214+
testBed = setup({
215+
componentTemplateName: COMPONENT_TEMPLATE.name,
216+
onClose: () => {},
217+
});
218+
});
219+
220+
testBed.component.update();
221+
});
222+
223+
test('should render an error message if error fetching pipelines', async () => {
224+
const { exists, find } = testBed;
225+
226+
expect(exists('sectionError')).toBe(true);
227+
expect(find('sectionError').text()).toContain('Error loading component template');
228+
});
229+
});
230+
});

x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_list.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66

77
import { act } from 'react-dom/test-utils';
88

9+
import { ComponentTemplateListItem } from '../../shared_imports';
10+
911
import { setupEnvironment, pageHelpers } from './helpers';
1012
import { ComponentTemplateListTestBed } from './helpers/component_template_list.helpers';
11-
import { API_BASE_PATH } from '../../../../../../common/constants';
12-
import { ComponentTemplateListItem } from '../../types';
13+
import { API_BASE_PATH } from './helpers/constants';
1314

1415
const { setup } = pageHelpers.componentTemplateList;
1516

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { registerTestBed, TestBed } from '../../../../../../../../../test_utils';
8+
import { WithAppDependencies } from './setup_environment';
9+
import { ComponentTemplateDetailsFlyout } from '../../../component_template_details';
10+
11+
export type ComponentTemplateDetailsTestBed = TestBed<ComponentTemplateDetailsTestSubjects> & {
12+
actions: ReturnType<typeof createActions>;
13+
};
14+
15+
const createActions = (testBed: TestBed<ComponentTemplateDetailsTestSubjects>) => {
16+
const { find } = testBed;
17+
18+
/**
19+
* User Actions
20+
*/
21+
const clickSettingsTab = () => {
22+
find('settingsTab').simulate('click');
23+
};
24+
25+
const clickMappingsTab = () => {
26+
find('mappingsTab').simulate('click');
27+
};
28+
29+
const clickAliasesTab = () => {
30+
find('aliasesTab').simulate('click');
31+
};
32+
33+
const clickManageButton = () => {
34+
find('manageComponentTemplateButton').simulate('click');
35+
};
36+
37+
return {
38+
clickSettingsTab,
39+
clickAliasesTab,
40+
clickMappingsTab,
41+
clickManageButton,
42+
};
43+
};
44+
45+
export const setup = (props: any): ComponentTemplateDetailsTestBed => {
46+
const setupTestBed = registerTestBed<ComponentTemplateDetailsTestSubjects>(
47+
WithAppDependencies(ComponentTemplateDetailsFlyout),
48+
{
49+
memoryRouter: {
50+
wrapComponent: false,
51+
},
52+
defaultProps: props,
53+
}
54+
);
55+
56+
const testBed = setupTestBed() as ComponentTemplateDetailsTestBed;
57+
58+
return {
59+
...testBed,
60+
actions: createActions(testBed),
61+
};
62+
};
63+
64+
export type ComponentTemplateDetailsTestSubjects =
65+
| 'componentTemplateDetails'
66+
| 'componentTemplateDetails.title'
67+
| 'componentTemplateDetails.footer'
68+
| 'summaryTab'
69+
| 'mappingsTab'
70+
| 'settingsTab'
71+
| 'aliasesTab'
72+
| 'sectionError'
73+
| 'summaryTabContent'
74+
| 'summaryTabContent.usedByTitle'
75+
| 'summaryTabContent.versionTitle'
76+
| 'summaryTabContent.metaTitle'
77+
| 'notInUseCallout'
78+
| 'aliasesTabContent'
79+
| 'noAliasesCallout'
80+
| 'mappingsTabContent'
81+
| 'noMappingsCallout'
82+
| 'settingsTabContent'
83+
| 'noSettingsCallout'
84+
| 'manageComponentTemplateButton'
85+
| 'manageComponentTemplateContextMenu'
86+
| 'manageComponentTemplateContextMenu.action';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export const API_BASE_PATH = '/api/index_management';

x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/http_requests.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
*/
66

77
import sinon, { SinonFakeServer } from 'sinon';
8-
import { API_BASE_PATH } from '../../../../../../../common';
8+
import { ComponentTemplateListItem, ComponentTemplateDeserialized } from '../../../shared_imports';
9+
import { API_BASE_PATH } from './constants';
910

1011
// Register helpers to mock HTTP Requests
1112
const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
12-
const setLoadComponentTemplatesResponse = (response?: any[], error?: any) => {
13+
const setLoadComponentTemplatesResponse = (
14+
response?: ComponentTemplateListItem[],
15+
error?: any
16+
) => {
1317
const status = error ? error.status || 400 : 200;
1418
const body = error ? error.body : response;
1519

@@ -20,6 +24,20 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
2024
]);
2125
};
2226

27+
const setLoadComponentTemplateResponse = (
28+
response?: ComponentTemplateDeserialized,
29+
error?: any
30+
) => {
31+
const status = error ? error.status || 400 : 200;
32+
const body = error ? error.body : response;
33+
34+
server.respondWith('GET', `${API_BASE_PATH}/component_templates/:name`, [
35+
status,
36+
{ 'Content-Type': 'application/json' },
37+
JSON.stringify(body),
38+
]);
39+
};
40+
2341
const setDeleteComponentTemplateResponse = (response?: object) => {
2442
server.respondWith('DELETE', `${API_BASE_PATH}/component_templates/:name`, [
2543
200,
@@ -31,6 +49,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
3149
return {
3250
setLoadComponentTemplatesResponse,
3351
setDeleteComponentTemplateResponse,
52+
setLoadComponentTemplateResponse,
3453
};
3554
};
3655

0 commit comments

Comments
 (0)