mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
parent
ba55349d27
commit
59436fb735
24 changed files with 1711 additions and 73 deletions
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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 axios from 'axios';
|
||||
|
||||
import { registerTestBed } from '../utils';
|
||||
import { rollupJobsStore } from '../../public/crud_app/store';
|
||||
import {
|
||||
setHttp,
|
||||
} from '../../public/crud_app/services';
|
||||
|
||||
import { JobCreate } from '../../public/crud_app/sections';
|
||||
|
||||
// axios has a $http like interface so using it to simulate $http
|
||||
setHttp(axios.create());
|
||||
|
||||
// This is the Rollup job we will be creating in our tests
|
||||
const JOB_TO_CREATE = {
|
||||
id: 'test-job',
|
||||
indexPattern: 'test-pattern-*',
|
||||
rollupIndex: 'rollup-index',
|
||||
interval: '24h'
|
||||
};
|
||||
|
||||
const initUserActions = (component, findTestSubject) => {
|
||||
const clickNextStep = () => {
|
||||
const button = findTestSubject('rollupJobNextButton');
|
||||
button.simulate('click');
|
||||
component.update();
|
||||
};
|
||||
|
||||
const clickPreviousStep = () => {
|
||||
const button = findTestSubject('rollupJobBackButton');
|
||||
button.simulate('click');
|
||||
component.update();
|
||||
};
|
||||
|
||||
const clickSave = () => {
|
||||
const button = findTestSubject('rollupJobSaveButton');
|
||||
button.simulate('click');
|
||||
component.update();
|
||||
};
|
||||
|
||||
return {
|
||||
clickNextStep,
|
||||
clickPreviousStep,
|
||||
clickSave,
|
||||
};
|
||||
};
|
||||
|
||||
const initFillFormFields = form => async (step) => {
|
||||
switch (step) {
|
||||
case 'logistics':
|
||||
form.setInputValue('rollupJobName', JOB_TO_CREATE.id);
|
||||
await form.setInputValue('rollupIndexPattern', JOB_TO_CREATE.indexPattern, true);
|
||||
form.setInputValue('rollupIndexName', JOB_TO_CREATE.rollupIndex);
|
||||
break;
|
||||
case 'date-histogram':
|
||||
form.setInputValue('rollupJobInterval', JOB_TO_CREATE.interval);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const initGoToStep = (fillFormFields, clickNextStep) => async (targetStep) => {
|
||||
const stepHandlers = {
|
||||
1: () => fillFormFields('logistics'),
|
||||
2: () => fillFormFields('date-histogram')
|
||||
};
|
||||
|
||||
let currentStep = 1;
|
||||
while(currentStep < targetStep) {
|
||||
if (stepHandlers[currentStep]) {
|
||||
await stepHandlers[currentStep]();
|
||||
}
|
||||
clickNextStep();
|
||||
currentStep++;
|
||||
}
|
||||
};
|
||||
|
||||
export const initTestBed = () => {
|
||||
const testBed = registerTestBed(JobCreate, {}, rollupJobsStore)();
|
||||
const userActions = initUserActions(testBed.component, testBed.findTestSubject);
|
||||
const fillFormFields = initFillFormFields(testBed.form);
|
||||
const goToStep = initGoToStep(fillFormFields, userActions.clickNextStep);
|
||||
const getEuiStepsHorizontalActive = () => testBed.component.find('.euiStepHorizontal-isSelected').text();
|
||||
|
||||
return {
|
||||
...testBed,
|
||||
userActions: {
|
||||
...userActions
|
||||
},
|
||||
form: {
|
||||
...testBed.form,
|
||||
fillFormFields,
|
||||
},
|
||||
goToStep,
|
||||
getEuiStepsHorizontalActive,
|
||||
};
|
||||
};
|
||||
|
||||
export const nextTick = async () => new Promise((resolve) => setTimeout(resolve));
|
||||
|
||||
export const mockServerResponses = server => {
|
||||
const mockIndexPatternValidityResponse = (response) => {
|
||||
const defaultResponse = {
|
||||
doesMatchIndices: true,
|
||||
doesMatchRollupIndices: false,
|
||||
dateFields: ['foo', 'bar'],
|
||||
numericFields: [],
|
||||
keywordFields: [],
|
||||
};
|
||||
server.respondWith(/\/api\/rollup\/index_pattern_validity\/.*/, [
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({ ...defaultResponse, ...response }),
|
||||
]);
|
||||
};
|
||||
|
||||
const mockCreateJob = () => {
|
||||
server.respondWith(/\/api\/rollup\/create/, [
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({}),
|
||||
]);
|
||||
};
|
||||
|
||||
const mockUserActions = () => {
|
||||
server.respondWith(/\/api\/user_action\/.*/, [
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({}),
|
||||
]);
|
||||
};
|
||||
|
||||
mockIndexPatternValidityResponse();
|
||||
mockCreateJob();
|
||||
mockUserActions();
|
||||
|
||||
return { mockIndexPatternValidityResponse };
|
||||
};
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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 sinon from 'sinon';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import { initTestBed, mockServerResponses } from './job_create.test_helpers';
|
||||
|
||||
jest.mock('ui/index_patterns', () => {
|
||||
const { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } = require.requireActual('../../../../../src/legacy/ui/public/index_patterns/constants'); // eslint-disable-line max-len
|
||||
return { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE };
|
||||
});
|
||||
|
||||
jest.mock('ui/chrome', () => ({
|
||||
addBasePath: () => '/api/rollup',
|
||||
breadcrumbs: { set: () => {} },
|
||||
}));
|
||||
|
||||
jest.mock('lodash/function/debounce', () => fn => fn);
|
||||
|
||||
describe('Create Rollup Job, step 2: Date histogram', () => {
|
||||
let server;
|
||||
let findTestSubject;
|
||||
let testSubjectExists;
|
||||
let userActions;
|
||||
let getFormErrorsMessages;
|
||||
let form;
|
||||
let mockIndexPatternValidityResponse;
|
||||
let getEuiStepsHorizontalActive;
|
||||
let goToStep;
|
||||
|
||||
beforeEach(() => {
|
||||
server = sinon.fakeServer.create();
|
||||
server.respondImmediately = true;
|
||||
({ mockIndexPatternValidityResponse } = mockServerResponses(server));
|
||||
({
|
||||
findTestSubject,
|
||||
testSubjectExists,
|
||||
userActions,
|
||||
getFormErrorsMessages,
|
||||
form,
|
||||
getEuiStepsHorizontalActive,
|
||||
goToStep,
|
||||
} = initTestBed());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStep(2);
|
||||
});
|
||||
|
||||
it('should have the horizontal step active on "Date histogram"', () => {
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Date histogram');
|
||||
});
|
||||
|
||||
it('should have the title set to "Date histogram"', () => {
|
||||
expect(testSubjectExists('rollupJobCreateDateHistogramTitle')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a link to the documentation', () => {
|
||||
expect(testSubjectExists('rollupJobCreateDateHistogramDocsButton')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have the "next" and "back" button visible', () => {
|
||||
expect(testSubjectExists('rollupJobBackButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobNextButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobSaveButton')).toBe(false);
|
||||
});
|
||||
|
||||
it('should go to the "Logistics" step when clicking the back button', async () => {
|
||||
userActions.clickPreviousStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Logistics');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Date field select', () => {
|
||||
it('should set the options value from the index pattern', async () => {
|
||||
const dateFields = ['field1', 'field2', 'field3'];
|
||||
mockIndexPatternValidityResponse({ dateFields });
|
||||
|
||||
await goToStep(2);
|
||||
|
||||
const dateFieldSelectOptionsValues = findTestSubject('rollupJobCreateDateFieldSelect').find('option').map(option => option.text());
|
||||
expect(dateFieldSelectOptionsValues).toEqual(dateFields);
|
||||
});
|
||||
});
|
||||
|
||||
describe('time zone', () => {
|
||||
it('should have a select with all the timezones', async () => {
|
||||
await goToStep(2);
|
||||
|
||||
const timeZoneSelect = findTestSubject('rollupJobCreateTimeZoneSelect');
|
||||
const options = timeZoneSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(moment.tz.names());
|
||||
});
|
||||
});
|
||||
|
||||
describe('form validation', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStep(2);
|
||||
});
|
||||
|
||||
it('should display errors when clicking "next" without filling the form', () => {
|
||||
expect(testSubjectExists('rollupJobCreateStepError')).toBeFalsy();
|
||||
|
||||
userActions.clickNextStep();
|
||||
|
||||
expect(testSubjectExists('rollupJobCreateStepError')).toBeTruthy();
|
||||
expect(getFormErrorsMessages()).toEqual(['Interval is required.']);
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
describe('interval', () => {
|
||||
afterEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate the interval format', () => {
|
||||
form.setInputValue('rollupJobInterval', 'abc');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Invalid interval format.');
|
||||
});
|
||||
|
||||
it('should validate the calendar format', () => {
|
||||
form.setInputValue('rollupJobInterval', '3y');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain(`The 'y' unit only allows values of 1. Try 1y.`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* 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 sinon from 'sinon';
|
||||
|
||||
import { initTestBed, mockServerResponses } from './job_create.test_helpers';
|
||||
|
||||
jest.mock('ui/index_patterns', () => {
|
||||
const { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } = require.requireActual('../../../../../src/legacy/ui/public/index_patterns/constants'); // eslint-disable-line max-len
|
||||
return { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE };
|
||||
});
|
||||
|
||||
jest.mock('ui/chrome', () => ({
|
||||
addBasePath: () => '/api/rollup',
|
||||
breadcrumbs: { set: () => {} },
|
||||
}));
|
||||
|
||||
jest.mock('lodash/function/debounce', () => fn => fn);
|
||||
|
||||
describe('Create Rollup Job, step 4: Histogram', () => {
|
||||
let server;
|
||||
let findTestSubject;
|
||||
let testSubjectExists;
|
||||
let userActions;
|
||||
let mockIndexPatternValidityResponse;
|
||||
let getEuiStepsHorizontalActive;
|
||||
let goToStep;
|
||||
let getMetadataFromEuiTable;
|
||||
let form;
|
||||
let getFormErrorsMessages;
|
||||
|
||||
beforeEach(() => {
|
||||
server = sinon.fakeServer.create();
|
||||
server.respondImmediately = true;
|
||||
({ mockIndexPatternValidityResponse } = mockServerResponses(server));
|
||||
({
|
||||
findTestSubject,
|
||||
testSubjectExists,
|
||||
userActions,
|
||||
getEuiStepsHorizontalActive,
|
||||
goToStep,
|
||||
getMetadataFromEuiTable,
|
||||
form,
|
||||
getFormErrorsMessages,
|
||||
} = initTestBed());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
const numericFields = ['a-numericField', 'b-numericField'];
|
||||
|
||||
const goToStepAndOpenFieldChooser = async () => {
|
||||
await goToStep(4);
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
};
|
||||
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStep(4);
|
||||
});
|
||||
|
||||
it('should have the horizontal step active on "Histogram"', () => {
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Histogram');
|
||||
});
|
||||
|
||||
it('should have the title set to "Terms"', () => {
|
||||
expect(testSubjectExists('rollupJobCreateHistogramTitle')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a link to the documentation', () => {
|
||||
expect(testSubjectExists('rollupJobCreateHistogramDocsButton')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have the "next" and "back" button visible', () => {
|
||||
expect(testSubjectExists('rollupJobBackButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobNextButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobSaveButton')).toBe(false);
|
||||
});
|
||||
|
||||
it('should go to the "Terms" step when clicking the back button', async () => {
|
||||
userActions.clickPreviousStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Terms');
|
||||
});
|
||||
|
||||
it('should go to the "Metrics" step when clicking the next button', async () => {
|
||||
userActions.clickNextStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Metrics');
|
||||
});
|
||||
|
||||
it('should have a button to display the list of histogram fields to chose from', () => {
|
||||
expect(testSubjectExists('rollupJobHistogramFieldChooser')).toBe(false);
|
||||
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
|
||||
expect(testSubjectExists('rollupJobHistogramFieldChooser')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('histogram field chooser (flyout)', () => {
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStepAndOpenFieldChooser();
|
||||
});
|
||||
|
||||
it('should have the title set to "Add histogram fields"', async () => {
|
||||
expect(findTestSubject('rollupJobCreateFlyoutTitle').text()).toEqual('Add histogram fields');
|
||||
});
|
||||
|
||||
it('should have a button to close the flyout', () => {
|
||||
expect(testSubjectExists('rollupJobHistogramFieldChooser')).toBe(true);
|
||||
|
||||
findTestSubject('euiFlyoutCloseButton').simulate('click');
|
||||
|
||||
expect(testSubjectExists('rollupJobHistogramFieldChooser')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no histogram fields are availalbe', () => {
|
||||
it('should indicate it to the user', async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields: [] });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobHistogramFieldChooser-table');
|
||||
|
||||
expect(tableCellsValues).toEqual([['No items found']]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when histogram fields are available', () => {
|
||||
beforeEach(async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
});
|
||||
|
||||
it('should display the histogram fields available', async () => {
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobHistogramFieldChooser-table');
|
||||
|
||||
expect(tableCellsValues).toEqual([
|
||||
['a-numericField'],
|
||||
['b-numericField'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add histogram field to the field list when clicking on it', () => {
|
||||
let { tableCellsValues } = getMetadataFromEuiTable('rollupJobHistogramFieldList');
|
||||
expect(tableCellsValues).toEqual([['No histogram fields added']]); // make sure the field list is empty
|
||||
|
||||
const { rows } = getMetadataFromEuiTable('rollupJobHistogramFieldChooser-table');
|
||||
rows[0].reactWrapper.simulate('click'); // Select first row
|
||||
|
||||
({ tableCellsValues } = getMetadataFromEuiTable('rollupJobHistogramFieldList'));
|
||||
const [firstRow] = tableCellsValues;
|
||||
expect(firstRow[0]).toEqual('a-numericField');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fields list', () => {
|
||||
it('should have an empty field list', async () => {
|
||||
await goToStep(4);
|
||||
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobHistogramFieldList');
|
||||
expect(tableCellsValues).toEqual([['No histogram fields added']]);
|
||||
});
|
||||
|
||||
it('should have a delete button on each row to remove an histogram field', async () => {
|
||||
// First let's add a term to the list
|
||||
mockIndexPatternValidityResponse({ numericFields });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
const { rows: fieldChooserRows } = getMetadataFromEuiTable('rollupJobHistogramFieldChooser-table');
|
||||
fieldChooserRows[0].reactWrapper.simulate('click');
|
||||
|
||||
// Make sure rows value has been set
|
||||
let { rows: fieldListRows } = getMetadataFromEuiTable('rollupJobHistogramFieldList');
|
||||
expect(fieldListRows[0].columns[0].value).not.toEqual('No histogram fields added');
|
||||
|
||||
const columnsFirstRow = fieldListRows[0].columns;
|
||||
// The last column is the eui "actions" column
|
||||
const deleteButton = columnsFirstRow[columnsFirstRow.length - 1].reactWrapper.find('button');
|
||||
deleteButton.simulate('click');
|
||||
|
||||
({ rows: fieldListRows } = getMetadataFromEuiTable('rollupJobHistogramFieldList'));
|
||||
expect(fieldListRows[0].columns[0].value).toEqual('No histogram fields added');
|
||||
});
|
||||
});
|
||||
|
||||
describe('interval', () => {
|
||||
const addHistogramFieldToList = () => {
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
const { rows } = getMetadataFromEuiTable('rollupJobHistogramFieldChooser-table');
|
||||
rows[0].reactWrapper.simulate('click');
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields });
|
||||
await goToStep(4);
|
||||
addHistogramFieldToList();
|
||||
});
|
||||
|
||||
describe('input validation', () => {
|
||||
afterEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should display errors when clicking "next" without filling the interval', () => {
|
||||
expect(testSubjectExists('rollupJobCreateStepError')).toBeFalsy();
|
||||
|
||||
userActions.clickNextStep();
|
||||
|
||||
expect(testSubjectExists('rollupJobCreateStepError')).toBeTruthy();
|
||||
expect(getFormErrorsMessages()).toEqual(['Interval must be a whole number.']);
|
||||
});
|
||||
|
||||
it('should be a whole number', () => {
|
||||
form.setInputValue('rollupJobCreateHistogramInterval', 5.5);
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toEqual(['Interval must be a whole number.']);
|
||||
});
|
||||
|
||||
it('should be greater than zero', () => {
|
||||
form.setInputValue('rollupJobCreateHistogramInterval', -1);
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toEqual(['Interval must be greater than zero.']);
|
||||
});
|
||||
});
|
||||
|
||||
it('should go to next "Metrics" step if value is valid', () => {
|
||||
form.setInputValue('rollupJobCreateHistogramInterval', 3);
|
||||
userActions.clickNextStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Metrics');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* 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 sinon from 'sinon';
|
||||
|
||||
import { MINUTE, HOUR, DAY, WEEK, MONTH, YEAR } from '../../public/crud_app/services';
|
||||
import { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } from '../../../../../src/legacy/ui/public/index_patterns';
|
||||
import { initTestBed, mockServerResponses } from './job_create.test_helpers';
|
||||
|
||||
jest.mock('ui/index_patterns', () => {
|
||||
const { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } = require.requireActual('../../../../../src/legacy/ui/public/index_patterns/constants'); // eslint-disable-line max-len
|
||||
return { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE };
|
||||
});
|
||||
|
||||
jest.mock('ui/chrome', () => ({
|
||||
addBasePath: () => '/api/rollup',
|
||||
breadcrumbs: { set: () => {} },
|
||||
}));
|
||||
|
||||
jest.mock('lodash/function/debounce', () => fn => fn);
|
||||
|
||||
describe('Create Rollup Job, step 1: Logistics', () => {
|
||||
let server;
|
||||
let findTestSubject;
|
||||
let testSubjectExists;
|
||||
let userActions;
|
||||
let getFormErrorsMessages;
|
||||
let form;
|
||||
let mockIndexPatternValidityResponse;
|
||||
let getEuiStepsHorizontalActive;
|
||||
|
||||
beforeEach(() => {
|
||||
server = sinon.fakeServer.create();
|
||||
server.respondImmediately = true;
|
||||
({ mockIndexPatternValidityResponse } = mockServerResponses(server));
|
||||
({
|
||||
findTestSubject,
|
||||
testSubjectExists,
|
||||
userActions,
|
||||
getFormErrorsMessages,
|
||||
form,
|
||||
getEuiStepsHorizontalActive,
|
||||
} = initTestBed());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
it('should have the horizontal step active on "Logistics"', () => {
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Logistics');
|
||||
});
|
||||
|
||||
it('should have the title set to "Logistics"', () => {
|
||||
expect(testSubjectExists('rollupJobCreateLogisticsTitle')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a link to the documentation', () => {
|
||||
expect(testSubjectExists('rollupJobCreateLogisticsDocsButton')).toBe(true);
|
||||
});
|
||||
|
||||
it('should only have the "next" button visible', () => {
|
||||
expect(testSubjectExists('rollupJobBackButton')).toBe(false);
|
||||
expect(testSubjectExists('rollupJobNextButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobSaveButton')).toBe(false);
|
||||
});
|
||||
|
||||
it('should display errors when clicking "next" without filling the form', () => {
|
||||
expect(testSubjectExists('rollupJobCreateStepError')).toBeFalsy();
|
||||
|
||||
userActions.clickNextStep();
|
||||
|
||||
expect(testSubjectExists('rollupJobCreateStepError')).toBeTruthy();
|
||||
expect(getFormErrorsMessages()).toEqual([
|
||||
'Name is required.',
|
||||
'Index pattern is required.',
|
||||
'Rollup index is required.',
|
||||
]);
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
describe('form validations', () => {
|
||||
describe('index pattern', () => {
|
||||
beforeEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow spaces', async () => {
|
||||
await form.setInputValue('rollupIndexPattern', 'with space', true);
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Remove the spaces from your index pattern.');
|
||||
});
|
||||
|
||||
it('should not allow an unknown index pattern', async () => {
|
||||
mockIndexPatternValidityResponse({ doesMatchIndices: false });
|
||||
await form.setInputValue('rollupIndexPattern', 'unknown', true);
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Index pattern doesn\'t match any indices.');
|
||||
});
|
||||
|
||||
it('should not allow an index pattern without time fields', async () => {
|
||||
mockIndexPatternValidityResponse({ dateFields: [] });
|
||||
await form.setInputValue('rollupIndexPattern', 'abc', true);
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Index pattern must match indices that contain time fields.');
|
||||
});
|
||||
|
||||
it('should not allow an index pattern that matches a rollup index', async () => {
|
||||
mockIndexPatternValidityResponse({ doesMatchRollupIndices: true });
|
||||
await form.setInputValue('rollupIndexPattern', 'abc', true);
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Index pattern must not match rollup indices.');
|
||||
});
|
||||
|
||||
it('should not be the same as the rollup index name', async () => {
|
||||
await form.setInputValue('rollupIndexPattern', 'abc', true);
|
||||
await form.setInputValue('rollupIndexName', 'abc', true);
|
||||
|
||||
userActions.clickNextStep();
|
||||
|
||||
const errorMessages = getFormErrorsMessages();
|
||||
expect(errorMessages).toContain('Index pattern cannot have the same as the rollup index.');
|
||||
expect(errorMessages).toContain('Rollup index cannot have the same as the index pattern.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('rollup index name', () => {
|
||||
beforeEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow spaces', () => {
|
||||
form.setInputValue('rollupIndexName', 'with space');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Remove the spaces from your rollup index name.');
|
||||
});
|
||||
|
||||
it('should not allow invalid characters', () => {
|
||||
const expectInvalidChar = (char) => {
|
||||
form.setInputValue('rollupIndexName', `rollup_index_${char}`);
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain(`Remove the characters ${char} from your rollup index name.`);
|
||||
};
|
||||
|
||||
[...INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE, ','].reduce((promise, char) => {
|
||||
return promise.then(() => expectInvalidChar(char));
|
||||
}, Promise.resolve());
|
||||
});
|
||||
|
||||
it('should not allow a dot as first character', () => {
|
||||
form.setInputValue('rollupIndexName', '.kibana');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Index names cannot begin with periods.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('rollup cron', () => {
|
||||
const changeFrequency = (value) => {
|
||||
findTestSubject('rollupJobCreateFrequencySelect').simulate('change', { target: { value } });
|
||||
};
|
||||
|
||||
const generateStringSequenceOfNumbers = (total) => (
|
||||
new Array(total).fill('').map((_, i) => i < 10 ? `0${i}` : i.toString())
|
||||
);
|
||||
|
||||
describe('frequency', () => {
|
||||
it('should allow "minute", "hour", "day", "week", "month", "year"', () => {
|
||||
const frequencySelect = findTestSubject('rollupJobCreateFrequencySelect');
|
||||
const options = frequencySelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(['minute', 'hour', 'day', 'week', 'month', 'year']);
|
||||
});
|
||||
|
||||
describe('every minute', () => {
|
||||
it('should not have any additional configuration', () => {
|
||||
changeFrequency(MINUTE);
|
||||
expect(findTestSubject('rollupCronFrequencyConfiguration').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hourly', () => {
|
||||
beforeEach(() => {
|
||||
changeFrequency(HOUR);
|
||||
});
|
||||
|
||||
it('should have 1 additional configuration', () => {
|
||||
expect(findTestSubject('rollupCronFrequencyConfiguration').length).toBe(1);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyHourlyMinuteSelect')).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow to select any minute from 00 -> 59', () => {
|
||||
const minutSelect = findTestSubject('rollupJobCreateFrequencyHourlyMinuteSelect');
|
||||
const options = minutSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(60));
|
||||
});
|
||||
});
|
||||
|
||||
describe('daily', () => {
|
||||
beforeEach(() => {
|
||||
changeFrequency(DAY);
|
||||
});
|
||||
|
||||
it('should have 1 additional configuration with hour and minute selects', () => {
|
||||
expect(findTestSubject('rollupCronFrequencyConfiguration').length).toBe(1);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyDailyHourSelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyDailyMinuteSelect')).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow to select any hour from 00 -> 23', () => {
|
||||
const hourSelect = findTestSubject('rollupJobCreateFrequencyDailyHourSelect');
|
||||
const options = hourSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(24));
|
||||
});
|
||||
|
||||
it('should allow to select any miute from 00 -> 59', () => {
|
||||
const minutSelect = findTestSubject('rollupJobCreateFrequencyDailyMinuteSelect');
|
||||
const options = minutSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(60));
|
||||
});
|
||||
});
|
||||
|
||||
describe('weekly', () => {
|
||||
beforeEach(() => {
|
||||
changeFrequency(WEEK);
|
||||
});
|
||||
|
||||
it('should have 2 additional configurations with day, hour and minute selects', () => {
|
||||
expect(findTestSubject('rollupCronFrequencyConfiguration').length).toBe(2);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyWeeklyDaySelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyWeeklyHourSelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyWeeklyMinuteSelect')).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow to select any day of the week', () => {
|
||||
const hourSelect = findTestSubject('rollupJobCreateFrequencyWeeklyDaySelect');
|
||||
const options = hourSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual([
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should allow to select any hour from 00 -> 23', () => {
|
||||
const hourSelect = findTestSubject('rollupJobCreateFrequencyWeeklyHourSelect');
|
||||
const options = hourSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(24));
|
||||
});
|
||||
|
||||
it('should allow to select any miute from 00 -> 59', () => {
|
||||
const minutSelect = findTestSubject('rollupJobCreateFrequencyWeeklyMinuteSelect');
|
||||
const options = minutSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(60));
|
||||
});
|
||||
});
|
||||
|
||||
describe('monthly', () => {
|
||||
beforeEach(() => {
|
||||
changeFrequency(MONTH);
|
||||
});
|
||||
|
||||
it('should have 2 additional configurations with date, hour and minute selects', () => {
|
||||
expect(findTestSubject('rollupCronFrequencyConfiguration').length).toBe(2);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyMonthlyDateSelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyMonthlyHourSelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyMonthlyMinuteSelect')).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow to select any date of the month from 1st to 31st', () => {
|
||||
const dateSelect = findTestSubject('rollupJobCreateFrequencyMonthlyDateSelect');
|
||||
const options = dateSelect.find('option').map(option => option.text());
|
||||
expect(options.length).toEqual(31);
|
||||
});
|
||||
|
||||
it('should allow to select any hour from 00 -> 23', () => {
|
||||
const hourSelect = findTestSubject('rollupJobCreateFrequencyMonthlyHourSelect');
|
||||
const options = hourSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(24));
|
||||
});
|
||||
|
||||
it('should allow to select any miute from 00 -> 59', () => {
|
||||
const minutSelect = findTestSubject('rollupJobCreateFrequencyMonthlyMinuteSelect');
|
||||
const options = minutSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(60));
|
||||
});
|
||||
});
|
||||
|
||||
describe('yearly', () => {
|
||||
beforeEach(() => {
|
||||
changeFrequency(YEAR);
|
||||
});
|
||||
|
||||
it('should have 3 additional configurations with month, date, hour and minute selects', () => {
|
||||
expect(findTestSubject('rollupCronFrequencyConfiguration').length).toBe(3);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyYearlyMonthSelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyYearlyDateSelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyYearlyHourSelect')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobCreateFrequencyYearlyMinuteSelect')).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow to select any month of the year', () => {
|
||||
const monthSelect = findTestSubject('rollupJobCreateFrequencyYearlyMonthSelect');
|
||||
const options = monthSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual([
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should allow to select any date of the month from 1st to 31st', () => {
|
||||
const dateSelect = findTestSubject('rollupJobCreateFrequencyYearlyDateSelect');
|
||||
const options = dateSelect.find('option').map(option => option.text());
|
||||
expect(options.length).toEqual(31);
|
||||
});
|
||||
|
||||
it('should allow to select any hour from 00 -> 23', () => {
|
||||
const hourSelect = findTestSubject('rollupJobCreateFrequencyYearlyHourSelect');
|
||||
const options = hourSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(24));
|
||||
});
|
||||
|
||||
it('should allow to select any miute from 00 -> 59', () => {
|
||||
const minutSelect = findTestSubject('rollupJobCreateFrequencyYearlyMinuteSelect');
|
||||
const options = minutSelect.find('option').map(option => option.text());
|
||||
expect(options).toEqual(generateStringSequenceOfNumbers(60));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('advanced cron expression', () => {
|
||||
const activateAdvancedCronExpression = () => {
|
||||
findTestSubject('rollupShowAdvancedCronLink').simulate('click');
|
||||
};
|
||||
|
||||
it('should allow to create a cron expression', () => {
|
||||
expect(testSubjectExists('rollupAdvancedCron')).toBe(false);
|
||||
|
||||
activateAdvancedCronExpression();
|
||||
|
||||
expect(testSubjectExists('rollupAdvancedCron')).toBe(true);
|
||||
});
|
||||
|
||||
it('should not be empty', () => {
|
||||
activateAdvancedCronExpression();
|
||||
|
||||
form.setInputValue('rollupAdvancedCron', '');
|
||||
userActions.clickNextStep();
|
||||
|
||||
expect(getFormErrorsMessages()).toContain('Cron pattern or basic interval is required.');
|
||||
});
|
||||
|
||||
it('should not allow unvalid expression', () => {
|
||||
activateAdvancedCronExpression();
|
||||
|
||||
form.setInputValue('rollupAdvancedCron', 'invalid');
|
||||
userActions.clickNextStep();
|
||||
|
||||
expect(getFormErrorsMessages()).toContain('Expression has only 1 part. At least 5 parts are required.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('page size', () => {
|
||||
beforeEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should not be empty', () => {
|
||||
form.setInputValue('rollupPageSize', '');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Page size is required.');
|
||||
});
|
||||
|
||||
it('should be greater than 0', () => {
|
||||
form.setInputValue('rollupPageSize', '-1');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Page size must be greater than zero.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('delay', () => {
|
||||
beforeEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate the interval format', () => {
|
||||
form.setInputValue('rollupDelay', 'abc');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain('Invalid delay format.');
|
||||
});
|
||||
|
||||
it('should validate the calendar format', () => {
|
||||
form.setInputValue('rollupDelay', '3y');
|
||||
userActions.clickNextStep();
|
||||
expect(getFormErrorsMessages()).toContain(`The 'y' unit only allows values of 1. Try 1y.`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* 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 sinon from 'sinon';
|
||||
|
||||
import { initTestBed, mockServerResponses } from './job_create.test_helpers';
|
||||
|
||||
jest.mock('ui/index_patterns', () => {
|
||||
const { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } = require.requireActual('../../../../../src/legacy/ui/public/index_patterns/constants'); // eslint-disable-line max-len
|
||||
return { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE };
|
||||
});
|
||||
|
||||
jest.mock('ui/chrome', () => ({
|
||||
addBasePath: () => '/api/rollup',
|
||||
breadcrumbs: { set: () => {} },
|
||||
}));
|
||||
|
||||
jest.mock('lodash/function/debounce', () => fn => fn);
|
||||
|
||||
describe('Create Rollup Job, step 5: Metrics', () => {
|
||||
let server;
|
||||
let findTestSubject;
|
||||
let testSubjectExists;
|
||||
let userActions;
|
||||
let mockIndexPatternValidityResponse;
|
||||
let getEuiStepsHorizontalActive;
|
||||
let goToStep;
|
||||
let getMetadataFromEuiTable;
|
||||
|
||||
beforeEach(() => {
|
||||
server = sinon.fakeServer.create();
|
||||
server.respondImmediately = true;
|
||||
({ mockIndexPatternValidityResponse } = mockServerResponses(server));
|
||||
({
|
||||
findTestSubject,
|
||||
testSubjectExists,
|
||||
userActions,
|
||||
getEuiStepsHorizontalActive,
|
||||
goToStep,
|
||||
getMetadataFromEuiTable,
|
||||
} = initTestBed());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
const numericFields = ['a-numericField', 'c-numericField'];
|
||||
const dateFields = ['b-dateField', 'd-dateField'];
|
||||
|
||||
const goToStepAndOpenFieldChooser = async () => {
|
||||
await goToStep(5);
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
};
|
||||
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStep(5);
|
||||
});
|
||||
|
||||
it('should have the horizontal step active on "Metrics"', () => {
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Metrics');
|
||||
});
|
||||
|
||||
it('should have the title set to "Metrics"', () => {
|
||||
expect(testSubjectExists('rollupJobCreateMetricsTitle')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a link to the documentation', () => {
|
||||
expect(testSubjectExists('rollupJobCreateMetricsDocsButton')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have the "next" and "back" button visible', () => {
|
||||
expect(testSubjectExists('rollupJobBackButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobNextButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobSaveButton')).toBe(false);
|
||||
});
|
||||
|
||||
it('should go to the "Histogram" step when clicking the back button', async () => {
|
||||
userActions.clickPreviousStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Histogram');
|
||||
});
|
||||
|
||||
it('should go to the "Review" step when clicking the next button', async () => {
|
||||
userActions.clickNextStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Review');
|
||||
});
|
||||
|
||||
it('should have a button to display the list of metrics fields to chose from', () => {
|
||||
expect(testSubjectExists('rollupJobMetricsFieldChooser')).toBe(false);
|
||||
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
|
||||
expect(testSubjectExists('rollupJobMetricsFieldChooser')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('metrics field chooser (flyout)', () => {
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStepAndOpenFieldChooser();
|
||||
});
|
||||
|
||||
it('should have the title set to "Add metrics fields"', async () => {
|
||||
expect(findTestSubject('rollupJobCreateFlyoutTitle').text()).toEqual('Add metrics fields');
|
||||
});
|
||||
|
||||
it('should have a button to close the flyout', () => {
|
||||
expect(testSubjectExists('rollupJobMetricsFieldChooser')).toBe(true);
|
||||
|
||||
findTestSubject('euiFlyoutCloseButton').simulate('click');
|
||||
|
||||
expect(testSubjectExists('rollupJobMetricsFieldChooser')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('table', () => {
|
||||
beforeEach(async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields, dateFields });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
});
|
||||
|
||||
it('should display the fields with metrics and its type', async () => {
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobMetricsFieldChooser-table');
|
||||
|
||||
expect(tableCellsValues).toEqual([
|
||||
['a-numericField', 'numeric'],
|
||||
['b-dateField', 'date'],
|
||||
['c-numericField', 'numeric'],
|
||||
['d-dateField', 'date'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add metric field to the field list when clicking on a row', () => {
|
||||
let { tableCellsValues } = getMetadataFromEuiTable('rollupJobMetricsFieldList');
|
||||
expect(tableCellsValues).toEqual([['No metrics fields added']]); // make sure the field list is empty
|
||||
|
||||
const { rows } = getMetadataFromEuiTable('rollupJobMetricsFieldChooser-table');
|
||||
rows[0].reactWrapper.simulate('click'); // Select first row in field chooser
|
||||
|
||||
({ tableCellsValues } = getMetadataFromEuiTable('rollupJobMetricsFieldList'));
|
||||
const [firstRow] = tableCellsValues;
|
||||
const [field, type] = firstRow;
|
||||
expect(field).toEqual(rows[0].columns[0].value);
|
||||
expect(type).toEqual(rows[0].columns[1].value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fields list', () => {
|
||||
const addFieldToList = (type = 'numeric') => {
|
||||
if(!testSubjectExists('rollupJobMetricsFieldChooser-table')) {
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
}
|
||||
const { rows } = getMetadataFromEuiTable('rollupJobMetricsFieldChooser-table');
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
if (rows[i].columns[1].value === type) {
|
||||
rows[i].reactWrapper.simulate('click');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
it('should have an empty field list', async () => {
|
||||
await goToStep(5);
|
||||
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobMetricsFieldList');
|
||||
expect(tableCellsValues).toEqual([['No metrics fields added']]);
|
||||
});
|
||||
|
||||
describe('when fields are added', () => {
|
||||
beforeEach(async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields, dateFields });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
});
|
||||
|
||||
it('should have "avg", "max", "min", "sum" & "value count" metrics for *numeric* fields', () => {
|
||||
const numericTypeMetrics = ['avg', 'max', 'min', 'sum', 'value_count'];
|
||||
addFieldToList('numeric');
|
||||
numericTypeMetrics.forEach(type => {
|
||||
try {
|
||||
expect(testSubjectExists(`rollupJobMetricsCheckbox-${type}`)).toBe(true);
|
||||
} catch(e) {
|
||||
throw(new Error(`Test subject "rollupJobMetricsCheckbox-${type}" was not found.`));
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure there are no other checkboxes
|
||||
const { rows: [firstRow] } = getMetadataFromEuiTable('rollupJobMetricsFieldList');
|
||||
const columnWithMetricsCheckboxes = 2;
|
||||
const metricsCheckboxes = firstRow.columns[columnWithMetricsCheckboxes].reactWrapper.find('input');
|
||||
expect(metricsCheckboxes.length).toBe(numericTypeMetrics.length);
|
||||
});
|
||||
|
||||
it('should have "max", "min", & "value count" metrics for *date* fields', () => {
|
||||
const dateTypeMetrics = ['max', 'min', 'value_count'];
|
||||
addFieldToList('date');
|
||||
|
||||
dateTypeMetrics.forEach(type => {
|
||||
try {
|
||||
expect(testSubjectExists(`rollupJobMetricsCheckbox-${type}`)).toBe(true);
|
||||
} catch(e) {
|
||||
throw(new Error(`Test subject "rollupJobMetricsCheckbox-${type}" was not found.`));
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure there are no other checkboxes
|
||||
const { rows: [firstRow] } = getMetadataFromEuiTable('rollupJobMetricsFieldList');
|
||||
const columnWithMetricsCheckboxes = 2;
|
||||
const metricsCheckboxes = firstRow.columns[columnWithMetricsCheckboxes].reactWrapper.find('input');
|
||||
expect(metricsCheckboxes.length).toBe(dateTypeMetrics.length);
|
||||
});
|
||||
|
||||
it('should not allow to go to the next step if at least one metric type is not selected', () => {
|
||||
expect(testSubjectExists('rollupJobCreateStepError')).toBeFalsy();
|
||||
|
||||
addFieldToList('numeric');
|
||||
userActions.clickNextStep();
|
||||
|
||||
const stepError = findTestSubject('rollupJobCreateStepError');
|
||||
expect(stepError.length).toBeTruthy();
|
||||
expect(stepError.text()).toEqual('Select metrics types for these fields or remove them: a-numericField.');
|
||||
expect(findTestSubject('rollupJobNextButton').props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a delete button on each row to remove the metric field', async () => {
|
||||
const { rows: fieldChooserRows } = getMetadataFromEuiTable('rollupJobMetricsFieldChooser-table');
|
||||
fieldChooserRows[0].reactWrapper.simulate('click'); // select first item
|
||||
|
||||
// Make sure rows value has been set
|
||||
let { rows: fieldListRows } = getMetadataFromEuiTable('rollupJobMetricsFieldList');
|
||||
expect(fieldListRows[0].columns[0].value).not.toEqual('No metrics fields added');
|
||||
|
||||
const columnsFirstRow = fieldListRows[0].columns;
|
||||
// The last column is the eui "actions" column
|
||||
const deleteButton = columnsFirstRow[columnsFirstRow.length - 1].reactWrapper.find('button');
|
||||
deleteButton.simulate('click');
|
||||
|
||||
({ rows: fieldListRows } = getMetadataFromEuiTable('rollupJobMetricsFieldList'));
|
||||
expect(fieldListRows[0].columns[0].value).toEqual('No metrics fields added');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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 sinon from 'sinon';
|
||||
|
||||
import { initTestBed, mockServerResponses, nextTick } from './job_create.test_helpers';
|
||||
|
||||
jest.mock('ui/index_patterns', () => {
|
||||
const { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } = require.requireActual('../../../../../src/legacy/ui/public/index_patterns/constants'); // eslint-disable-line max-len
|
||||
return { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE };
|
||||
});
|
||||
|
||||
jest.mock('ui/chrome', () => ({
|
||||
addBasePath: (path) => path,
|
||||
breadcrumbs: { set: () => {} },
|
||||
}));
|
||||
|
||||
jest.mock('lodash/function/debounce', () => fn => fn);
|
||||
|
||||
|
||||
describe('Create Rollup Job, step 5: Metrics', () => {
|
||||
let server;
|
||||
let findTestSubject;
|
||||
let testSubjectExists;
|
||||
let userActions;
|
||||
let mockIndexPatternValidityResponse;
|
||||
let getEuiStepsHorizontalActive;
|
||||
let goToStep;
|
||||
let getMetadataFromEuiTable;
|
||||
let form;
|
||||
|
||||
beforeEach(() => {
|
||||
server = sinon.fakeServer.create();
|
||||
server.respondImmediately = true;
|
||||
({ mockIndexPatternValidityResponse } = mockServerResponses(server));
|
||||
({
|
||||
findTestSubject,
|
||||
testSubjectExists,
|
||||
userActions,
|
||||
getEuiStepsHorizontalActive,
|
||||
goToStep,
|
||||
getMetadataFromEuiTable,
|
||||
form
|
||||
} = initTestBed());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStep(6);
|
||||
});
|
||||
|
||||
it('should have the horizontal step active on "Review"', () => {
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Review');
|
||||
});
|
||||
|
||||
it('should have the title set to "Review"', () => {
|
||||
expect(testSubjectExists('rollupJobCreateReviewTitle')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have the "next" and "save" button visible', () => {
|
||||
expect(testSubjectExists('rollupJobBackButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobNextButton')).toBe(false);
|
||||
expect(testSubjectExists('rollupJobSaveButton')).toBe(true);
|
||||
});
|
||||
|
||||
it('should go to the "Metrics" step when clicking the back button', async () => {
|
||||
userActions.clickPreviousStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Metrics');
|
||||
});
|
||||
});
|
||||
|
||||
describe('tabs', () => {
|
||||
const getTabsText = () => findTestSubject('stepReviewTab').map(tab => tab.text());
|
||||
const selectFirstField = (step) => {
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
|
||||
// Select the first term field
|
||||
getMetadataFromEuiTable(`rollupJob${step}FieldChooser-table`)
|
||||
.rows[0]
|
||||
.reactWrapper
|
||||
.simulate('click');
|
||||
};
|
||||
|
||||
it('should have a "Summary" & "JSON" tabs to review the Job', async () => {
|
||||
await goToStep(6);
|
||||
expect(getTabsText()).toEqual(['Summary', 'JSON']);
|
||||
});
|
||||
|
||||
it('should have a "Summary", "Terms" & "JSON" tab if a term aggregation was added', async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields: ['my-field'] });
|
||||
await goToStep(3);
|
||||
selectFirstField('Terms');
|
||||
|
||||
userActions.clickNextStep(); // go to step 4
|
||||
userActions.clickNextStep(); // go to step 5
|
||||
userActions.clickNextStep(); // go to review
|
||||
|
||||
expect(getTabsText()).toEqual(['Summary', 'Terms', 'JSON']);
|
||||
});
|
||||
|
||||
it('should have a "Summary", "Histogram" & "JSON" tab if a histogram field was added', async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields: ['a-field'] });
|
||||
await goToStep(4);
|
||||
selectFirstField('Histogram');
|
||||
form.setInputValue('rollupJobCreateHistogramInterval', 3); // set an interval
|
||||
|
||||
userActions.clickNextStep(); // go to step 5
|
||||
userActions.clickNextStep(); // go to review
|
||||
|
||||
expect(getTabsText()).toEqual(['Summary', 'Histogram', 'JSON']);
|
||||
});
|
||||
|
||||
it('should have a "Summary", "Metrics" & "JSON" tab if a histogram field was added', async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields: ['a-field'], dateFields: ['b-field'] });
|
||||
await goToStep(5);
|
||||
selectFirstField('Metrics');
|
||||
form.selectCheckBox('rollupJobMetricsCheckbox-avg'); // select a metric
|
||||
|
||||
userActions.clickNextStep(); // go to review
|
||||
|
||||
expect(getTabsText()).toEqual(['Summary', 'Metrics', 'JSON']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('save()', () => {
|
||||
it('should call the "create" Api server endpoint', async () => {
|
||||
await goToStep(6);
|
||||
|
||||
const jobCreateApiPath = '/api/rollup/create';
|
||||
expect(server.requests.find(r => r.url === jobCreateApiPath)).toBe(undefined); // make sure it hasn't been called
|
||||
|
||||
userActions.clickSave();
|
||||
await nextTick();
|
||||
|
||||
expect(server.requests.find(r => r.url === jobCreateApiPath)).not.toBe(undefined); // It has been called!
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* 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 sinon from 'sinon';
|
||||
|
||||
import { initTestBed, mockServerResponses } from './job_create.test_helpers';
|
||||
|
||||
jest.mock('ui/index_patterns', () => {
|
||||
const { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } = require.requireActual('../../../../../src/legacy/ui/public/index_patterns/constants'); // eslint-disable-line max-len
|
||||
return { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE };
|
||||
});
|
||||
|
||||
jest.mock('ui/chrome', () => ({
|
||||
addBasePath: () => '/api/rollup',
|
||||
breadcrumbs: { set: () => {} },
|
||||
}));
|
||||
|
||||
jest.mock('lodash/function/debounce', () => fn => fn);
|
||||
|
||||
describe('Create Rollup Job, step 3: Terms', () => {
|
||||
let server;
|
||||
let findTestSubject;
|
||||
let testSubjectExists;
|
||||
let userActions;
|
||||
let mockIndexPatternValidityResponse;
|
||||
let getEuiStepsHorizontalActive;
|
||||
let goToStep;
|
||||
let getMetadataFromEuiTable;
|
||||
|
||||
beforeEach(() => {
|
||||
server = sinon.fakeServer.create();
|
||||
server.respondImmediately = true;
|
||||
({ mockIndexPatternValidityResponse } = mockServerResponses(server));
|
||||
({
|
||||
findTestSubject,
|
||||
testSubjectExists,
|
||||
userActions,
|
||||
getEuiStepsHorizontalActive,
|
||||
goToStep,
|
||||
getMetadataFromEuiTable,
|
||||
} = initTestBed());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.restore();
|
||||
});
|
||||
|
||||
const numericFields = ['a-numericField', 'c-numericField'];
|
||||
const keywordFields = ['b-keywordField', 'd-keywordField'];
|
||||
|
||||
const goToStepAndOpenFieldChooser = async () => {
|
||||
await goToStep(3);
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
};
|
||||
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStep(3);
|
||||
});
|
||||
|
||||
it('should have the horizontal step active on "Terms"', () => {
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Terms');
|
||||
});
|
||||
|
||||
it('should have the title set to "Terms"', () => {
|
||||
expect(testSubjectExists('rollupJobCreateTermsTitle')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a link to the documentation', () => {
|
||||
expect(testSubjectExists('rollupJobCreateTermsDocsButton')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have the "next" and "back" button visible', () => {
|
||||
expect(testSubjectExists('rollupJobBackButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobNextButton')).toBe(true);
|
||||
expect(testSubjectExists('rollupJobSaveButton')).toBe(false);
|
||||
});
|
||||
|
||||
it('should go to the "Date histogram" step when clicking the back button', async () => {
|
||||
userActions.clickPreviousStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Date histogram');
|
||||
});
|
||||
|
||||
it('should go to the "Histogram" step when clicking the next button', async () => {
|
||||
userActions.clickNextStep();
|
||||
expect(getEuiStepsHorizontalActive()).toContain('Histogram');
|
||||
});
|
||||
|
||||
it('should have a button to display the list of terms to chose from', () => {
|
||||
expect(testSubjectExists('rollupJobTermsFieldChooser')).toBe(false);
|
||||
|
||||
findTestSubject('rollupJobShowFieldChooserButton').simulate('click');
|
||||
|
||||
expect(testSubjectExists('rollupJobTermsFieldChooser')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('terms field chooser (flyout)', () => {
|
||||
describe('layout', () => {
|
||||
beforeEach(async () => {
|
||||
await goToStepAndOpenFieldChooser();
|
||||
});
|
||||
|
||||
it('should have the title set to "Add terms fields"', async () => {
|
||||
expect(findTestSubject('rollupJobCreateFlyoutTitle').text()).toEqual('Add terms fields');
|
||||
});
|
||||
|
||||
it('should have a button to close the flyout', () => {
|
||||
expect(testSubjectExists('rollupJobTermsFieldChooser')).toBe(true);
|
||||
|
||||
findTestSubject('euiFlyoutCloseButton').simulate('click');
|
||||
|
||||
expect(testSubjectExists('rollupJobTermsFieldChooser')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no terms are available', () => {
|
||||
it('should indicate it to the user', async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields: [], keywordFields: [] });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobTermsFieldChooser-table');
|
||||
|
||||
expect(tableCellsValues).toEqual([['No items found']]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when terms are available', () => {
|
||||
beforeEach(async () => {
|
||||
mockIndexPatternValidityResponse({ numericFields, keywordFields });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
});
|
||||
|
||||
it('should display the numeric & keyword fields available', async () => {
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobTermsFieldChooser-table');
|
||||
|
||||
expect(tableCellsValues).toEqual([
|
||||
['a-numericField', 'numeric'],
|
||||
['b-keywordField', 'keyword'],
|
||||
['c-numericField', 'numeric'],
|
||||
['d-keywordField', 'keyword'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add term to the field list when clicking on it', () => {
|
||||
let { tableCellsValues } = getMetadataFromEuiTable('rollupJobTermsFieldList');
|
||||
expect(tableCellsValues).toEqual([['No terms fields added']]); // make sure the field list is empty
|
||||
|
||||
const { rows } = getMetadataFromEuiTable('rollupJobTermsFieldChooser-table');
|
||||
rows[0].reactWrapper.simulate('click'); // Select first row
|
||||
|
||||
({ tableCellsValues } = getMetadataFromEuiTable('rollupJobTermsFieldList'));
|
||||
const [firstRow] = tableCellsValues;
|
||||
const [term, type] = firstRow;
|
||||
expect(term).toEqual('a-numericField');
|
||||
expect(type).toEqual('numeric');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fields list', () => {
|
||||
it('should have an empty field list', async () => {
|
||||
await goToStep(3);
|
||||
|
||||
const { tableCellsValues } = getMetadataFromEuiTable('rollupJobTermsFieldList');
|
||||
expect(tableCellsValues).toEqual([['No terms fields added']]);
|
||||
});
|
||||
|
||||
it('should have a delete button on each row to remove a term', async () => {
|
||||
// First let's add a term to the list
|
||||
mockIndexPatternValidityResponse({ numericFields, keywordFields });
|
||||
await goToStepAndOpenFieldChooser();
|
||||
const { rows: fieldChooserRows } = getMetadataFromEuiTable('rollupJobTermsFieldChooser-table');
|
||||
fieldChooserRows[0].reactWrapper.simulate('click');
|
||||
|
||||
// Make sure rows value has been set
|
||||
let { rows: fieldListRows } = getMetadataFromEuiTable('rollupJobTermsFieldList');
|
||||
expect(fieldListRows[0].columns[0].value).not.toEqual('No terms fields added');
|
||||
|
||||
const columnsFirstRow = fieldListRows[0].columns;
|
||||
// The last column is the eui "actions" column
|
||||
const deleteButton = columnsFirstRow[columnsFirstRow.length - 1].reactWrapper.find('button');
|
||||
deleteButton.simulate('click');
|
||||
|
||||
({ rows: fieldListRows } = getMetadataFromEuiTable('rollupJobTermsFieldList'));
|
||||
expect(fieldListRows[0].columns[0].value).toEqual('No terms fields added');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers'; // eslint-disable-line import/no-unresolved
|
||||
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
|
||||
import { findTestSubject as findTestSubjectHelper } from '@elastic/eui/lib/test';
|
||||
|
||||
const registerTestSubjExists = component => (testSubject, count = 1) => findTestSubjectHelper(component, testSubject).length === count;
|
||||
|
@ -33,5 +33,64 @@ export const registerTestBed = (Component, defaultProps, store = {}) => (props)
|
|||
const testSubjectExists = registerTestSubjExists(component);
|
||||
const findTestSubject = testSubject => findTestSubjectHelper(component, testSubject);
|
||||
|
||||
return { component, testSubjectExists, findTestSubject, setProps };
|
||||
const getFormErrorsMessages = () => {
|
||||
const errorMessagesWrappers = component.find('.euiFormErrorText');
|
||||
return errorMessagesWrappers.map(err => err.text());
|
||||
};
|
||||
|
||||
const setInputValue = (inputTestSubject, value, isAsync = false) => {
|
||||
const formInput = findTestSubject(inputTestSubject);
|
||||
formInput.simulate('change', { target: { value } });
|
||||
component.update();
|
||||
|
||||
// In some cases, changing an input value triggers an http request to validate
|
||||
// it. Even by returning immediately the response on the mock server we need
|
||||
// to wait until the next tick before the DOM updates.
|
||||
// Setting isAsync to "true" solves that problem.
|
||||
if (!isAsync) {
|
||||
return;
|
||||
}
|
||||
return new Promise((resolve) => setTimeout(resolve));
|
||||
};
|
||||
|
||||
const selectCheckBox = (checkboxTestSubject) => {
|
||||
findTestSubject(checkboxTestSubject).simulate('change', { target: { checked: true } });
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to parse an EUI table and return its rows and column reactWrapper
|
||||
*
|
||||
* @param {ReactWrapper} table enzyme react wrapper of the EuiBasicTable
|
||||
*/
|
||||
const getMetadataFromEuiTable = (tableTestSubject) => {
|
||||
const rows = findTestSubject(tableTestSubject)
|
||||
.find('tr')
|
||||
.slice(1) // we remove the first row as it is the table header
|
||||
.map(row => ({
|
||||
reactWrapper: row,
|
||||
columns: row.find('td').map(col => ({
|
||||
reactWrapper: col,
|
||||
// We can't access the td value with col.text() because
|
||||
// eui adds an extra div inside the table > tr > td on mobile => (".euiTableRowCell__mobileHeader")
|
||||
value: col.find('.euiTableCellContent').text()
|
||||
}))
|
||||
}));
|
||||
|
||||
// Also output the raw cell values, in the following format: [[td0, td1, td2], [td0, td1, td2]]
|
||||
const tableCellsValues = rows.map(({ columns }) => columns.map(col => col.value));
|
||||
return { rows, tableCellsValues };
|
||||
};
|
||||
|
||||
return {
|
||||
component,
|
||||
testSubjectExists,
|
||||
findTestSubject,
|
||||
setProps,
|
||||
getFormErrorsMessages,
|
||||
getMetadataFromEuiTable,
|
||||
form: {
|
||||
setInputValue,
|
||||
selectCheckBox
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ export const FieldList = ({
|
|||
onRemoveField,
|
||||
addButton,
|
||||
emptyMessage,
|
||||
dataTestSubj,
|
||||
}) => {
|
||||
let message;
|
||||
|
||||
|
@ -72,6 +73,7 @@ export const FieldList = ({
|
|||
pagination={pagination}
|
||||
sorting={true}
|
||||
message={message}
|
||||
data-test-subj={dataTestSubj}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -82,4 +84,5 @@ FieldList.propTypes = {
|
|||
onRemoveField: PropTypes.func,
|
||||
addButton: PropTypes.node,
|
||||
emptyMessage: PropTypes.node,
|
||||
dataTestSubj: PropTypes.string,
|
||||
};
|
||||
|
|
|
@ -45,66 +45,61 @@ const NavigationUi = ({
|
|||
);
|
||||
}
|
||||
|
||||
let previousStepButton;
|
||||
if (hasPreviousStep) {
|
||||
previousStepButton = (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
iconType="arrowLeft"
|
||||
onClick={goToPreviousStep}
|
||||
data-test-subj="rollupJobBackButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.backButton.label"
|
||||
defaultMessage="Back"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
}
|
||||
const previousStepButton = (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
iconType="arrowLeft"
|
||||
onClick={goToPreviousStep}
|
||||
data-test-subj="rollupJobBackButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.backButton.label"
|
||||
defaultMessage="Back"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
let nextStepButton;
|
||||
if (hasNextStep) {
|
||||
nextStepButton = (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
iconType="arrowRight"
|
||||
iconSide="right"
|
||||
onClick={goToNextStep}
|
||||
isDisabled={!canGoToNextStep}
|
||||
fill
|
||||
data-test-subj="rollupJobNextButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.nextButton.label"
|
||||
defaultMessage="Next"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
} else {
|
||||
nextStepButton = (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="secondary"
|
||||
iconType="check"
|
||||
onClick={save}
|
||||
fill
|
||||
data-test-subj="rollupJobSaveButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.saveButton.label"
|
||||
defaultMessage="Save"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
}
|
||||
const nextStepButton = (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
iconType="arrowRight"
|
||||
iconSide="right"
|
||||
onClick={goToNextStep}
|
||||
isDisabled={!canGoToNextStep}
|
||||
fill
|
||||
data-test-subj="rollupJobNextButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.nextButton.label"
|
||||
defaultMessage="Next"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
const saveButton = (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="secondary"
|
||||
iconType="check"
|
||||
onClick={save}
|
||||
fill
|
||||
data-test-subj="rollupJobSaveButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.saveButton.label"
|
||||
defaultMessage="Save"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup justifyContent="flexStart" gutterSize="m">
|
||||
{previousStepButton}
|
||||
{nextStepButton}
|
||||
{hasPreviousStep && previousStepButton}
|
||||
{hasNextStep && nextStepButton}
|
||||
{!hasNextStep && saveButton}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@ export const CronDaily = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -50,6 +51,7 @@ export const CronDaily = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyDailyHourSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
|
@ -66,6 +68,7 @@ export const CronDaily = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyDailyMinuteSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -352,6 +352,7 @@ export class CronEditor extends Component {
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencySelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ export const CronHourly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiSelect
|
||||
options={minuteOptions}
|
||||
|
@ -44,6 +45,7 @@ export const CronHourly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyHourlyMinuteSelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
|
|
|
@ -34,6 +34,7 @@ export const CronMonthly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiSelect
|
||||
options={dateOptions}
|
||||
|
@ -50,6 +51,7 @@ export const CronMonthly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyMonthlyDateSelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
|
@ -61,6 +63,7 @@ export const CronMonthly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -79,6 +82,7 @@ export const CronMonthly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyMonthlyHourSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
|
@ -95,6 +99,7 @@ export const CronMonthly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyMonthlyMinuteSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -34,6 +34,7 @@ export const CronWeekly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiSelect
|
||||
options={dayOptions}
|
||||
|
@ -50,6 +51,7 @@ export const CronWeekly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyWeeklyDaySelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
|
@ -61,6 +63,7 @@ export const CronWeekly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -79,6 +82,7 @@ export const CronWeekly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyWeeklyHourSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
|
@ -95,6 +99,7 @@ export const CronWeekly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyWeeklyMinuteSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -36,6 +36,7 @@ export const CronYearly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiSelect
|
||||
options={monthOptions}
|
||||
|
@ -52,6 +53,7 @@ export const CronYearly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyYearlyMonthSelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
|
@ -63,6 +65,7 @@ export const CronYearly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiSelect
|
||||
options={dateOptions}
|
||||
|
@ -79,6 +82,7 @@ export const CronYearly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyYearlyDateSelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
|
@ -90,6 +94,7 @@ export const CronYearly = ({
|
|||
/>
|
||||
)}
|
||||
fullWidth
|
||||
data-test-subj="rollupCronFrequencyConfiguration"
|
||||
>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -108,6 +113,7 @@ export const CronYearly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyYearlyHourSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
|
@ -124,6 +130,7 @@ export const CronYearly = ({
|
|||
</strong>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="rollupJobCreateFrequencyYearlyMinuteSelect"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -27,10 +27,12 @@ export class FieldChooser extends Component {
|
|||
onSelectField: PropTypes.func.isRequired,
|
||||
columns: PropTypes.array.isRequired,
|
||||
prompt: PropTypes.string,
|
||||
dataTestSubj: PropTypes.string,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
prompt: 'Search',
|
||||
dataTestSubj: 'rollupJobFieldChooser'
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -68,6 +70,7 @@ export class FieldChooser extends Component {
|
|||
selectedFields,
|
||||
prompt,
|
||||
onSelectField,
|
||||
dataTestSubj,
|
||||
} = this.props;
|
||||
|
||||
const { isOpen, searchValue } = this.state;
|
||||
|
@ -80,9 +83,7 @@ export class FieldChooser extends Component {
|
|||
};
|
||||
};
|
||||
|
||||
let flyout;
|
||||
|
||||
if (isOpen) {
|
||||
const renderFlyout = () => {
|
||||
// Derive the fields which the user can select.
|
||||
const selectedFieldNames = selectedFields.map(({ name }) => name);
|
||||
const unselectedFields = fields.filter(({ name }) => {
|
||||
|
@ -95,15 +96,20 @@ export class FieldChooser extends Component {
|
|||
item.type.toLowerCase().includes(normalizedSearchValue);
|
||||
}) : unselectedFields;
|
||||
|
||||
flyout = (
|
||||
return (
|
||||
<EuiFlyout
|
||||
onClose={this.close}
|
||||
aria-labelledby="fieldChooserFlyoutTitle"
|
||||
size="m"
|
||||
maxWidth={400}
|
||||
data-test-subj={dataTestSubj}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle size="m" id="fieldChooserFlyoutTitle">
|
||||
<EuiTitle
|
||||
size="m"
|
||||
id="fieldChooserFlyoutTitle"
|
||||
data-test-subj="rollupJobCreateFlyoutTitle"
|
||||
>
|
||||
<h2>{buttonLabel}</h2>
|
||||
</EuiTitle>
|
||||
|
||||
|
@ -124,21 +130,23 @@ export class FieldChooser extends Component {
|
|||
columns={columns}
|
||||
rowProps={getRowProps}
|
||||
responsive={false}
|
||||
data-test-subj={`${dataTestSubj}-table`}
|
||||
/>
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiButton
|
||||
onClick={this.onButtonClick}
|
||||
data-test-subj="rollupJobShowFieldChooserButton"
|
||||
>
|
||||
{buttonLabel}
|
||||
</EuiButton>
|
||||
|
||||
{flyout}
|
||||
{isOpen ? renderFlyout() : null}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ export function StepError({ title = (
|
|||
title={title}
|
||||
color="danger"
|
||||
iconType="cross"
|
||||
data-test-subj="rollupJobCreateStepError"
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -194,7 +194,7 @@ export class StepDateHistogramUi extends Component {
|
|||
<Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<EuiTitle data-test-subj="rollupJobCreateDateHistogramTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepDateHistogramTitle"
|
||||
|
@ -211,6 +211,7 @@ export class StepDateHistogramUi extends Component {
|
|||
href={dateHistogramDetailsUrl}
|
||||
target="_blank"
|
||||
iconType="help"
|
||||
data-test-subj="rollupJobCreateDateHistogramDocsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepDateHistogram.readDocsButtonLabel"
|
||||
|
@ -271,6 +272,7 @@ export class StepDateHistogramUi extends Component {
|
|||
value={dateHistogramField}
|
||||
onChange={e => onFieldsChange({ dateHistogramField: e.target.value })}
|
||||
fullWidth
|
||||
data-test-subj="rollupJobCreateDateFieldSelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
|
@ -311,6 +313,7 @@ export class StepDateHistogramUi extends Component {
|
|||
value={dateHistogramTimeZone}
|
||||
onChange={e => onFieldsChange({ dateHistogramTimeZone: e.target.value })}
|
||||
fullWidth
|
||||
data-test-subj="rollupJobCreateTimeZoneSelect"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
|
|
|
@ -81,7 +81,7 @@ export class StepHistogramUi extends Component {
|
|||
<Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<EuiTitle data-test-subj="rollupJobCreateHistogramTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepHistogramTitle"
|
||||
|
@ -109,6 +109,7 @@ export class StepHistogramUi extends Component {
|
|||
href={histogramDetailsUrl}
|
||||
target="_blank"
|
||||
iconType="help"
|
||||
data-test-subj="rollupJobCreateHistogramDocsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepHistogram.readDocsButtonLabel"
|
||||
|
@ -137,8 +138,10 @@ export class StepHistogramUi extends Component {
|
|||
fields={histogramFields}
|
||||
selectedFields={histogram}
|
||||
onSelectField={this.onSelectField}
|
||||
dataTestSubj="rollupJobHistogramFieldChooser"
|
||||
/>
|
||||
)}
|
||||
dataTestSubj="rollupJobHistogramFieldList"
|
||||
/>
|
||||
|
||||
{this.renderInterval()}
|
||||
|
@ -211,6 +214,7 @@ export class StepHistogramUi extends Component {
|
|||
onChange={e => onFieldsChange({ histogramInterval: e.target.value })}
|
||||
isInvalid={Boolean(areStepErrorsVisible && errorHistogramInterval)}
|
||||
fullWidth
|
||||
data-test-subj="rollupJobCreateHistogramInterval"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
|
|
|
@ -173,6 +173,7 @@ export class StepLogisticsUi extends Component {
|
|||
onChange={e => onFieldsChange({ rollupCron: e.target.value })}
|
||||
isInvalid={Boolean(areStepErrorsVisible && errorRollupCron)}
|
||||
fullWidth
|
||||
data-test-subj="rollupAdvancedCron"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
|
@ -207,7 +208,7 @@ export class StepLogisticsUi extends Component {
|
|||
/>
|
||||
|
||||
<EuiText size="s">
|
||||
<EuiLink onClick={this.showAdvancedCron}>
|
||||
<EuiLink onClick={this.showAdvancedCron} data-test-subj="rollupShowAdvancedCronLink">
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepLogistics.sectionSchedule.buttonAdvancedLabel"
|
||||
defaultMessage="Create cron expression"
|
||||
|
@ -248,7 +249,7 @@ export class StepLogisticsUi extends Component {
|
|||
<Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<EuiTitle data-test-subj="rollupJobCreateLogisticsTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepLogisticsTitle"
|
||||
|
@ -276,6 +277,7 @@ export class StepLogisticsUi extends Component {
|
|||
href={logisticalDetailsUrl}
|
||||
target="_blank"
|
||||
iconType="help"
|
||||
data-test-subj="rollupJobCreateLogisticsDocsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepLogistics.readDocsButtonLabel"
|
||||
|
@ -455,6 +457,7 @@ export class StepLogisticsUi extends Component {
|
|||
isInvalid={Boolean(areStepErrorsVisible && errorRollupPageSize)}
|
||||
fullWidth
|
||||
min={0}
|
||||
data-test-subj="rollupPageSize"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
|
@ -506,6 +509,7 @@ export class StepLogisticsUi extends Component {
|
|||
onChange={e => onFieldsChange({ rollupDelay: e.target.value })}
|
||||
isInvalid={Boolean(areStepErrorsVisible && errorRollupDelay)}
|
||||
fullWidth
|
||||
data-test-subj="rollupDelay"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
|
|
|
@ -136,6 +136,7 @@ export class StepMetricsUi extends Component {
|
|||
>
|
||||
<EuiCheckbox
|
||||
id={`${fieldName}-${type}-checkbox`}
|
||||
data-test-subj={`rollupJobMetricsCheckbox-${type}`}
|
||||
label={label}
|
||||
checked={isSelected}
|
||||
onChange={() => this.setMetric(fieldName, type, !isSelected)}
|
||||
|
@ -210,7 +211,7 @@ export class StepMetricsUi extends Component {
|
|||
<Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<EuiTitle data-test-subj="rollupJobCreateMetricsTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepMetricsTitle"
|
||||
|
@ -239,6 +240,7 @@ export class StepMetricsUi extends Component {
|
|||
href={metricsDetailsUrl}
|
||||
target="_blank"
|
||||
iconType="help"
|
||||
data-test-subj="rollupJobCreateMetricsDocsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepMetrics.readDocsButtonLabel"
|
||||
|
@ -267,8 +269,10 @@ export class StepMetricsUi extends Component {
|
|||
fields={metricsFields}
|
||||
selectedFields={metrics}
|
||||
onSelectField={this.onSelectField}
|
||||
dataTestSubj="rollupJobMetricsFieldChooser"
|
||||
/>
|
||||
)}
|
||||
dataTestSubj="rollupJobMetricsFieldList"
|
||||
/>
|
||||
|
||||
{this.renderErrors()}
|
||||
|
|
|
@ -80,7 +80,7 @@ export class StepReviewUi extends Component {
|
|||
<EuiTab
|
||||
onClick={() => this.selectTab(tab)}
|
||||
isSelected={isSelected}
|
||||
data-test-subj={`stepReviewTab${isSelected ? 'Selected' : ''}`}
|
||||
data-test-subj="stepReviewTab"
|
||||
key={index}
|
||||
>
|
||||
{tabToHumanizedMap[tab]}
|
||||
|
@ -109,7 +109,7 @@ export class StepReviewUi extends Component {
|
|||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiTitle>
|
||||
<EuiTitle data-test-subj="rollupJobCreateReviewTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepReviewTitle"
|
||||
|
|
|
@ -80,7 +80,7 @@ export class StepTermsUi extends Component {
|
|||
<Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<EuiTitle data-test-subj="rollupJobCreateTermsTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepTermsTitle"
|
||||
|
@ -110,6 +110,7 @@ export class StepTermsUi extends Component {
|
|||
href={termsDetailsUrl}
|
||||
target="_blank"
|
||||
iconType="help"
|
||||
data-test-subj="rollupJobCreateTermsDocsButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.rollupJobs.create.stepTerms.readDocsButtonLabel"
|
||||
|
@ -138,8 +139,10 @@ export class StepTermsUi extends Component {
|
|||
fields={termsFields}
|
||||
selectedFields={terms}
|
||||
onSelectField={this.onSelectField}
|
||||
dataTestSubj="rollupJobTermsFieldChooser"
|
||||
/>
|
||||
)}
|
||||
dataTestSubj="rollupJobTermsFieldList"
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue