From cdee8599d103af86c0aba32dfa52329da0cdd640 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Thu, 7 Jan 2021 12:02:11 -0500 Subject: [PATCH 01/24] [Time to Visualize] Align Lens & Visualize Top nav Buttons & Behaviour (#86922) * Aligned Lens & Visualize Top nav behaviour and look --- .../components/visualize_top_nav.tsx | 12 +- .../visualize/public/application/types.ts | 1 + .../application/utils/get_top_nav_config.tsx | 117 ++++++++---------- .../utils/get_visualization_instance.ts | 9 +- .../utils/use/use_saved_vis_instance.ts | 8 +- .../services/dashboard/visualizations.ts | 6 +- .../lens/public/app_plugin/lens_top_nav.tsx | 2 +- 7 files changed, 78 insertions(+), 77 deletions(-) diff --git a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx index af74c0d275076..627d5cd00147b 100644 --- a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx +++ b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx @@ -98,7 +98,6 @@ const TopNav = ({ stateTransfer: services.stateTransferService, savedObjectsClient, embeddableId, - onAppLeave, }, services ); @@ -117,7 +116,6 @@ const TopNav = ({ services, embeddableId, savedObjectsClient, - onAppLeave, ]); const [indexPatterns, setIndexPatterns] = useState( vis.data.indexPattern ? [vis.data.indexPattern] : [] @@ -145,8 +143,9 @@ const TopNav = ({ // Confirm when the user has made any changes to an existing visualizations // or when the user has configured something without saving if ( - ((originatingApp && originatingApp === 'dashboards') || originatingApp === 'canvas') && - (hasUnappliedChanges || hasUnsavedChanges) + originatingApp && + (hasUnappliedChanges || hasUnsavedChanges) && + !services.stateTransferService.isTransferInProgress ) { return actions.confirm( i18n.translate('visualize.confirmModal.confirmTextDescription', { @@ -161,10 +160,11 @@ const TopNav = ({ }); }, [ onAppLeave, - hasUnappliedChanges, + originatingApp, hasUnsavedChanges, + hasUnappliedChanges, visualizeCapabilities.save, - originatingApp, + services.stateTransferService.isTransferInProgress, ]); useEffect(() => { diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts index e9bb7ecf654f6..1729d273e24bc 100644 --- a/src/plugins/visualize/public/application/types.ts +++ b/src/plugins/visualize/public/application/types.ts @@ -129,6 +129,7 @@ export interface SavedVisInstance { export interface ByValueVisInstance { vis: Vis; + savedVis: VisSavedObject; savedSearch?: SavedObject; embeddableHandler: VisualizeEmbeddableContract; } diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index 34a618fdefd3f..2420c972977b8 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -21,7 +21,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { TopNavMenuData } from 'src/plugins/navigation/public'; -import { AppMountParameters } from 'kibana/public'; import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeInput } from '../../../../visualizations/public'; import { showSaveModal, @@ -55,7 +54,6 @@ interface TopNavConfigParams { stateTransfer: EmbeddableStateTransfer; savedObjectsClient: SavedObjectsClientContract; embeddableId?: string; - onAppLeave: AppMountParameters['onAppLeave']; } export const getTopNavConfig = ( @@ -72,12 +70,10 @@ export const getTopNavConfig = ( visualizationIdFromUrl, stateTransfer, embeddableId, - onAppLeave, }: TopNavConfigParams, { application, chrome, - embeddable, history, share, setActiveUrl, @@ -89,14 +85,11 @@ export const getTopNavConfig = ( }: VisualizeServices ) => { const { vis, embeddableHandler } = visInstance; - const savedVis = 'savedVis' in visInstance ? visInstance.savedVis : undefined; + const savedVis = visInstance.savedVis; /** * Called when the user clicks "Save" button. */ async function doSave(saveOptions: SavedObjectSaveOpts) { - if (!savedVis) { - return {}; - } const newlyCreated = !Boolean(savedVis.id) || savedVis.copyOnSave; // vis.title was not bound and it's needed to reflect title into visState stateContainer.transitions.setVis({ @@ -122,15 +115,21 @@ export const getTopNavConfig = ( }); if (originatingApp && saveOptions.returnToOrigin) { - const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(id)}`; + if (!embeddableId) { + const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(id)}`; - // Manually insert a new url so the back button will open the saved visualization. - history.replace(appPath); - setActiveUrl(appPath); + // Manually insert a new url so the back button will open the saved visualization. + history.replace(appPath); + setActiveUrl(appPath); + } if (newlyCreated && stateTransfer) { stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { - state: { type: VISUALIZE_EMBEDDABLE_TYPE, input: { savedObjectId: id } }, + state: { + type: VISUALIZE_EMBEDDABLE_TYPE, + input: { savedObjectId: id }, + embeddableId, + }, }); } else { application.navigateToApp(originatingApp); @@ -192,6 +191,24 @@ export const getTopNavConfig = ( } }; + const saveButtonLabel = + embeddableId || + (!savedVis.id && dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && originatingApp) + ? i18n.translate('visualize.topNavMenu.saveVisualizationToLibraryButtonLabel', { + defaultMessage: 'Save to library', + }) + : originatingApp && (embeddableId || savedVis.id) + ? i18n.translate('visualize.topNavMenu.saveVisualizationAsButtonLabel', { + defaultMessage: 'Save as', + }) + : i18n.translate('visualize.topNavMenu.saveVisualizationButtonLabel', { + defaultMessage: 'Save', + }); + + const showSaveAndReturn = + originatingApp && + (savedVis?.id || dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables); + const topNavMenu: TopNavMenuData[] = [ { id: 'inspector', @@ -243,7 +260,7 @@ export const getTopNavConfig = ( // disable the Share button if no action specified disableButton: !share || !!embeddableId, }, - ...(originatingApp === 'dashboards' || originatingApp === 'canvas' + ...(originatingApp ? [ { id: 'cancel', @@ -268,24 +285,16 @@ export const getTopNavConfig = ( }, ] : []), - ...(visualizeCapabilities.save && !embeddableId + ...(visualizeCapabilities.save ? [ { id: 'save', - iconType: savedVis?.id && originatingApp ? undefined : 'save', - label: - savedVis?.id && originatingApp - ? i18n.translate('visualize.topNavMenu.saveVisualizationAsButtonLabel', { - defaultMessage: 'save as', - }) - : i18n.translate('visualize.topNavMenu.saveVisualizationButtonLabel', { - defaultMessage: 'save', - }), - emphasize: (savedVis && !savedVis.id) || !originatingApp, + iconType: showSaveAndReturn ? undefined : 'save', + label: saveButtonLabel, + emphasize: !showSaveAndReturn, description: i18n.translate('visualize.topNavMenu.saveVisualizationButtonAriaLabel', { defaultMessage: 'Save Visualization', }), - className: savedVis?.id && originatingApp ? 'saveAsButton' : '', testId: 'visualizeSaveButton', disableButton: hasUnappliedChanges, tooltip() { @@ -298,7 +307,7 @@ export const getTopNavConfig = ( ); } }, - run: (anchorElement: HTMLElement) => { + run: () => { const onSave = async ({ newTitle, newCopyOnSave, @@ -308,10 +317,6 @@ export const getTopNavConfig = ( returnToOrigin, dashboardId, }: OnSaveProps & { returnToOrigin?: boolean } & { dashboardId?: string | null }) => { - if (!savedVis) { - return; - } - const currentTitle = savedVis.title; savedVis.title = newTitle; embeddableHandler.updateInput({ title: newTitle }); @@ -371,12 +376,10 @@ export const getTopNavConfig = ( let selectedTags: string[] = []; let tagOptions: React.ReactNode | undefined; - if ( - savedVis && - savedObjectsTagging && - savedObjectsTagging.ui.hasTagDecoration(savedVis) - ) { - selectedTags = savedVis.getTags(); + if (savedObjectsTagging) { + if (savedVis && savedObjectsTagging.ui.hasTagDecoration(savedVis)) { + selectedTags = savedVis.getTags(); + } tagOptions = ( {}} originatingApp={originatingApp} + returnToOriginSwitchLabel={ + originatingApp && embeddableId + ? i18n.translate('visualize.topNavMenu.updatePanel', { + defaultMessage: 'Update panel on {originatingAppName}', + values: { + originatingAppName: stateTransfer.getAppNameFromId(originatingApp), + }, + }) + : undefined + } /> ) : ( ); - - const isSaveAsButton = anchorElement.classList.contains('saveAsButton'); - onAppLeave((actions) => { - return actions.default(); - }); - if ( - originatingApp === 'dashboards' && - dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && - !isSaveAsButton - ) { - createVisReference(); - } else if (savedVis) { - showSaveModal(saveModal, I18nContext); - } + showSaveModal(saveModal, I18nContext); }, }, ] : []), - ...(originatingApp && ((savedVis && savedVis.id) || embeddableId) + ...(visualizeCapabilities.save && showSaveAndReturn ? [ { id: 'saveAndReturn', @@ -455,20 +455,13 @@ export const getTopNavConfig = ( } }, run: async () => { + if (!savedVis?.id) { + return createVisReference(); + } const saveOptions = { confirmOverwrite: false, returnToOrigin: true, }; - onAppLeave((actions) => { - return actions.default(); - }); - if ( - originatingApp === 'dashboards' && - dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && - !savedVis - ) { - return createVisReference(); - } return doSave(saveOptions); }, }, diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index 6010c4f8b163e..148e2c16c7824 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -71,8 +71,14 @@ export const getVisualizationInstanceFromInput = async ( visualizeServices: VisualizeServices, input: VisualizeInput ) => { - const { visualizations } = visualizeServices; + const { visualizations, savedVisualizations } = visualizeServices; const visState = input.savedVis as SerializedVis; + + /** + * A saved vis is needed even in by value mode to support 'save to library' which converts the 'by value' + * state of the visualization, into a new saved object. + */ + const savedVis: VisSavedObject = await savedVisualizations.get(); let vis = await visualizations.createVis(visState.type, cloneDeep(visState)); if (vis.type.setup) { try { @@ -87,6 +93,7 @@ export const getVisualizationInstanceFromInput = async ( ); return { vis, + savedVis, embeddableHandler, savedSearch, }; diff --git a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts index 9dd29a2ba433a..3f9b3ca9b8b73 100644 --- a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts +++ b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts @@ -45,6 +45,7 @@ export const useSavedVisInstance = ( savedVisInstance?: SavedVisInstance; visEditorController?: IEditorController; }>({}); + const visEditorRef = useRef(null); const visId = useRef(''); @@ -132,7 +133,6 @@ export const useSavedVisInstance = ( embeddableHandler.render(visEditorRef.current); } } - setState({ savedVisInstance, visEditorController, @@ -189,13 +189,13 @@ export const useSavedVisInstance = ( getSavedVisInstance(); } }, [ + services, eventEmitter, - isChromeVisible, originatingApp, - services, + isChromeVisible, + visualizationIdFromUrl, state.savedVisInstance, state.visEditorController, - visualizationIdFromUrl, ]); useEffect(() => { diff --git a/test/functional/services/dashboard/visualizations.ts b/test/functional/services/dashboard/visualizations.ts index 22e1769145f88..ff1e934c7f265 100644 --- a/test/functional/services/dashboard/visualizations.ts +++ b/test/functional/services/dashboard/visualizations.ts @@ -149,8 +149,8 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F await PageObjects.visualize.clickAggBasedVisualizations(); await PageObjects.visualize.clickMetric(); await find.clickByCssSelector('li.euiListGroupItem:nth-of-type(2)'); - await testSubjects.exists('visualizeSaveButton'); - await testSubjects.click('visualizeSaveButton'); + await testSubjects.exists('visualizesaveAndReturnButton'); + await testSubjects.click('visualizesaveAndReturnButton'); } async createAndEmbedMarkdown({ name, markdown }: { name: string; markdown: string }) { @@ -163,7 +163,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F await PageObjects.visualize.clickMarkdownWidget(); await PageObjects.visEditor.setMarkdownTxt(markdown); await PageObjects.visEditor.clickGo(); - await testSubjects.click('visualizeSaveButton'); + await testSubjects.click('visualizesaveAndReturnButton'); } })(); } diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx index 2c23dc291405c..ad354510ef049 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -46,7 +46,7 @@ export function getLensTopNavConfig(options: { if (showCancel) { topNavMenu.push({ label: i18n.translate('xpack.lens.app.cancel', { - defaultMessage: 'cancel', + defaultMessage: 'Cancel', }), run: actions.cancel, testId: 'lnsApp_cancelButton', From b906b10af7c764ec2eb3a908229d99ee39e7739c Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Thu, 7 Jan 2021 12:04:41 -0500 Subject: [PATCH 02/24] [Dashboard] Fix ViewMode Updates from URL (#87405) * added view mode state into `use_dashboard_state_manager` to re-render dashboard top nav any time the viewMode changes --- .../dashboard/public/application/dashboard_app.tsx | 13 +++++++++++-- .../hooks/use_dashboard_state_manager.ts | 13 +++++++++++-- .../application/top_nav/dashboard_top_nav.tsx | 6 ++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/plugins/dashboard/public/application/dashboard_app.tsx b/src/plugins/dashboard/public/application/dashboard_app.tsx index 845d64c16500d..f33383427342b 100644 --- a/src/plugins/dashboard/public/application/dashboard_app.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app.tsx @@ -74,7 +74,10 @@ export function DashboardApp({ const [indexPatterns, setIndexPatterns] = useState([]); const savedDashboard = useSavedDashboard(savedDashboardId, history); - const dashboardStateManager = useDashboardStateManager(savedDashboard, history); + const { dashboardStateManager, viewMode, setViewMode } = useDashboardStateManager( + savedDashboard, + history + ); const dashboardContainer = useDashboardContainer(dashboardStateManager, history, false); const refreshDashboardContainer = useCallback( @@ -113,6 +116,10 @@ export function DashboardApp({ removeQueryParam(history, DashboardConstants.SEARCH_SESSION_ID, true); } + if (changes.viewMode) { + setViewMode(changes.viewMode); + } + dashboardContainer.updateInput({ ...changes, // do not start a new session if this is irrelevant state change to prevent excessive searches @@ -123,6 +130,7 @@ export function DashboardApp({ [ history, data.query, + setViewMode, embedSettings, dashboardContainer, data.search.session, @@ -222,7 +230,7 @@ export function DashboardApp({ return (
- {savedDashboard && dashboardStateManager && dashboardContainer && ( + {savedDashboard && dashboardStateManager && dashboardContainer && viewMode && ( <> { diff --git a/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts b/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts index 7aadfe40ebf08..5c606504bfa9a 100644 --- a/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts @@ -39,16 +39,23 @@ import { createSessionRestorationDataProvider } from '../lib/session_restoration import { DashboardStateManager } from '../dashboard_state_manager'; import { getDashboardTitle } from '../../dashboard_strings'; import { DashboardAppServices } from '../types'; +import { ViewMode } from '../../services/embeddable'; // TS is picky with type guards, we can't just inline `() => false` function defaultTaggingGuard(_obj: SavedObject): _obj is TagDecoratedSavedObject { return false; } +interface DashboardStateManagerReturn { + dashboardStateManager: DashboardStateManager | null; + viewMode: ViewMode | null; + setViewMode: (value: ViewMode) => void; +} + export const useDashboardStateManager = ( savedDashboard: DashboardSavedObject | null, history: History -): DashboardStateManager | null => { +): DashboardStateManagerReturn => { const { data: dataPlugin, core, @@ -72,6 +79,7 @@ export const useDashboardStateManager = ( const [dashboardStateManager, setDashboardStateManager] = useState( null ); + const [viewMode, setViewMode] = useState(null); const hasTaggingCapabilities = savedObjectsTagging?.ui.hasTagDecoration || defaultTaggingGuard; @@ -172,6 +180,7 @@ export const useDashboardStateManager = ( ); setDashboardStateManager(stateManager); + setViewMode(stateManager.getViewMode()); return () => { stateManager?.destroy(); @@ -196,5 +205,5 @@ export const useDashboardStateManager = ( usageCollection, ]); - return dashboardStateManager; + return { dashboardStateManager, viewMode, setViewMode }; }; diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx index 87ccbf29b99f7..e800c84e24295 100644 --- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx @@ -76,6 +76,7 @@ export interface DashboardTopNavProps { indexPatterns: IndexPattern[]; redirectTo: DashboardRedirect; lastDashboardId?: string; + viewMode: ViewMode; } export function DashboardTopNav({ @@ -88,6 +89,7 @@ export function DashboardTopNav({ indexPatterns, redirectTo, timefilter, + viewMode, }: DashboardTopNavProps) { const { core, @@ -422,7 +424,7 @@ export function DashboardTopNav({ const showSearchBar = showQueryBar || showFilterBar; const topNav = getTopNavConfig( - dashboardStateManager.getViewMode(), + viewMode, dashboardTopNavActions, dashboardCapabilities.hideWriteControls ); @@ -469,7 +471,7 @@ export function DashboardTopNav({ return ( <> - {!dashboardStateManager.getIsViewMode() ? ( + {viewMode !== ViewMode.VIEW ? ( ) : null} From 9a17446495f92b91475000395f14fff28fb40bfe Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Thu, 7 Jan 2021 09:13:19 -0800 Subject: [PATCH 03/24] Fix broken link to Lens documentation (#87392) --- src/core/public/doc_links/doc_links_service.ts | 1 + x-pack/plugins/lens/public/help_menu_util.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 927f94c10fc42..12266ec8de2e4 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -169,6 +169,7 @@ export class DocLinksService { guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html`, timelionDeprecation: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html#timelion-deprecation`, lens: `${ELASTIC_WEBSITE_URL}what-is/kibana-lens`, + lensPanels: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html#create-panels-with-lens`, maps: `${ELASTIC_WEBSITE_URL}maps`, }, observability: { diff --git a/x-pack/plugins/lens/public/help_menu_util.tsx b/x-pack/plugins/lens/public/help_menu_util.tsx index 333a90df4731b..6169ca7bddc50 100644 --- a/x-pack/plugins/lens/public/help_menu_util.tsx +++ b/x-pack/plugins/lens/public/help_menu_util.tsx @@ -12,7 +12,7 @@ export function addHelpMenuToAppChrome(chrome: ChromeStart, docLinks: DocLinksSt links: [ { linkType: 'documentation', - href: `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/lens.html`, + href: docLinks.links.visualize.lensPanels, }, { linkType: 'github', From 9d4ef37f4e98c05d25dd9232473e8f59480f803d Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 7 Jan 2021 17:47:17 +0000 Subject: [PATCH 04/24] chore(NA): move monitoring out of __tests__ folder (#87556) * chore(NA): move server and common from monitoring out of the __tests__ folder * chore(NA): move monitoring public out of __tests__ folder * chore(NA): add missing skip on test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...s => format_timestamp_to_duration.test.js} | 37 +- .../__snapshots__/helpers.test.js.snap | 0 .../overview/{__tests__ => }/helpers.test.js | 2 +- .../__snapshots__/cells.test.js.snap | 0 .../nodes/{__tests__ => }/cells.test.js | 2 +- .../if_statement.js => if_statement.test.js} | 103 ++- ...ke_statement.js => make_statement.test.js} | 25 +- .../pipeline.js => pipeline.test.js} | 655 +++++++++--------- ..._statement.js => plugin_statement.test.js} | 21 +- .../{__tests__/queue.js => queue.test.js} | 15 +- .../statement.js => statement.test.js} | 13 +- .../{__tests__/utils.js => utils.test.js} | 15 +- .../collapsible_statement.test.js.snap | 0 .../__snapshots__/detail_drawer.test.js.snap | 0 .../__snapshots__/metric.test.js.snap | 0 .../pipeline_viewer.test.js.snap | 0 .../plugin_statement.test.js.snap | 0 .../__snapshots__/queue.test.js.snap | 0 .../__snapshots__/statement.test.js.snap | 0 .../__snapshots__/statement_list.test.js.snap | 0 .../statement_list_heading.test.js.snap | 0 .../statement_section.test.js.snap | 0 .../collapsible_statement.test.js | 2 +- .../{__test__ => }/detail_drawer.test.js | 4 +- .../views/{__test__ => }/metric.test.js | 2 +- .../{__test__ => }/pipeline_viewer.test.js | 4 +- .../{__test__ => }/plugin_statement.test.js | 2 +- .../views/{__test__ => }/queue.test.js | 2 +- .../views/{__test__ => }/statement.test.js | 10 +- .../{__test__ => }/statement_list.test.js | 4 +- .../statement_list_heading.test.js | 2 +- .../{__test__ => }/statement_section.test.js | 2 +- .../__snapshots__/checker_errors.test.js.snap | 0 .../__snapshots__/no_data.test.js.snap | 0 .../{__tests__ => }/checker_errors.test.js | 2 +- .../collection_enabled.test.js.snap | 2 +- .../collection_enabled.test.js | 7 +- .../collection_interval.test.js.snap | 6 +- .../collection_interval.test.js | 7 +- .../__snapshots__/exporters.test.js.snap | 0 .../{__tests__ => }/exporters.test.js | 2 +- .../__snapshots__/plugin_enabled.test.js.snap | 0 .../{__tests__ => }/plugin_enabled.test.js | 2 +- .../no_data/{__tests__ => }/no_data.test.js | 2 +- .../__snapshots__/reason_found.test.js.snap | 0 .../__snapshots__/we_tried.test.js.snap | 0 .../{__tests__ => }/reason_found.test.js | 2 +- .../reasons/{__tests__ => }/we_tried.test.js | 2 +- .../__snapshots__/page_loading.test.js.snap | 0 .../{__tests__ => }/page_loading.test.js | 2 +- .../__snapshots__/index.test.js.snap | 0 .../sparkline/{__test__ => }/index.test.js | 4 +- .../{__tests__ => }/enabler.test.js | 18 +- .../{__tests__ => }/settings_checker.test.js | 2 +- .../{__tests__ => }/start_checks.test.js | 4 +- .../{__tests__ => }/model_updater.test.js | 7 +- .../cloud/{__tests__/aws.js => aws.test.js} | 59 +- .../{__tests__/azure.js => azure.test.js} | 39 +- ...oud_detector.js => cloud_detector.test.js} | 17 +- ...oud_response.js => cloud_response.test.js} | 15 +- ...cloud_service.js => cloud_service.test.js} | 54 +- ...oud_services.js => cloud_services.test.js} | 11 +- .../cloud/{__tests__/gcp.js => gcp.test.js} | 53 +- .../fixtures => __fixtures__}/create_stubs.js | 0 ...earch.js => alerts_cluster_search.test.js} | 7 +- ...js => alerts_clusters_aggregation.test.js} | 7 +- ...check_license.js => check_license.test.js} | 2 +- ...e.js => verify_monitoring_license.test.js} | 5 +- .../deprecations.js => deprecations.test.js} | 59 +- ...e_client.js => instantiate_client.test.js} | 63 +- ...bulk_uploader.js => bulk_uploader.test.js} | 6 +- ...value.js => check_for_email_value.test.js} | 53 +- ...ail.js => get_default_admin_email.test.js} | 41 +- .../get_apms.js => get_apms.test.js} | 11 +- .../get_listing_response.js | 0 ...ts_query.js => create_beats_query.test.js} | 20 +- ...at_summary.js => get_beat_summary.test.js} | 12 +- .../get_beats.js => get_beats.test.js} | 18 +- ...ters.js => get_beats_for_clusters.test.js} | 7 +- ...test_stats.js => get_latest_stats.test.js} | 7 +- .../get_stats.js => get_stats.test.js} | 9 +- ...lculate_auto.js => calculate_auto.test.js} | 7 +- ...ilty.js => calculate_availabiilty.test.js} | 7 +- ...us.js => calculate_overall_status.test.js} | 9 +- ...lculate_rate.js => calculate_rate.test.js} | 27 +- ... => calculate_timeseries_interval.test.js} | 31 +- .../ccs_utils.js => ccs_utils.test.js} | 6 +- .../fixtures => __fixtures__}/clusters.json | 0 .../get_clusters_summary.test.js.snap | 0 ...ers.js => flag_supported_clusters.test.js} | 26 +- ...r_status.js => get_cluster_status.test.js} | 9 +- ...rs_state.js => get_clusters_state.test.js} | 21 +- ...rs_stats.js => get_clusters_stats.test.js} | 61 +- .../get_clusters_summary.test.js | 4 +- .../create_query.js => create_query.test.js} | 26 +- .../agg_metrics_buckets.json | 0 .../deriv_metrics_buckets.json | 0 .../non_deriv_metrics_buckets.json | 0 .../__snapshots__/get_metrics.test.js.snap | 0 .../{__test__ => }/get_metrics.test.js | 8 +- ..._recovery.js => get_last_recovery.test.js} | 37 +- .../get_ml_jobs.js => get_ml_jobs.test.js} | 7 +- .../{__tests__/cluster.js => cluster.test.js} | 23 +- .../find_reason.js => find_reason.test.js} | 31 +- .../{__tests__/nodes.js => nodes.test.js} | 15 +- .../auth_errors.js => auth_errors.test.js} | 53 +- .../known_errors.js => known_errors.test.js} | 58 +- .../server/lib/{__tests__ => }/helpers.js | 0 ...kibana_info.js => get_kibana_info.test.js} | 12 +- ...get_node_info.js => get_node_info.test.js} | 14 +- .../get_pipeline.js => get_pipeline.test.js} | 11 +- .../__snapshots__/metrics.test.js.snap | 0 .../cpu_utilization_calculation.test.js | 2 +- .../latency_metric_calculation.test.js | 2 +- .../quota_metric_calculation.test.js | 2 +- .../latency_calculation.test.js | 2 +- .../metrics/{__test__ => }/metrics.test.js | 2 +- ...ring.js => process_version_string.test.js} | 9 +- .../get_collection_status.test.js | 110 +-- 119 files changed, 1064 insertions(+), 1136 deletions(-) rename x-pack/plugins/monitoring/common/{__tests__/format_timestamp_to_duration.js => format_timestamp_to_duration.test.js} (85%) rename x-pack/plugins/monitoring/public/components/cluster/overview/{__tests__ => }/__snapshots__/helpers.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/cluster/overview/{__tests__ => }/helpers.test.js (95%) rename x-pack/plugins/monitoring/public/components/elasticsearch/nodes/{__tests__ => }/__snapshots__/cells.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/elasticsearch/nodes/{__tests__ => }/cells.test.js (98%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/{__tests__/if_statement.js => if_statement.test.js} (65%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/{__tests__/make_statement.js => make_statement.test.js} (68%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/{__tests__/pipeline.js => pipeline.test.js} (71%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/{__tests__/plugin_statement.js => plugin_statement.test.js} (66%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/{__tests__/queue.js => queue.test.js} (71%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/{__tests__/statement.js => statement.test.js} (69%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/{__tests__/utils.js => utils.test.js} (85%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/collapsible_statement.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/detail_drawer.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/metric.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/pipeline_viewer.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/plugin_statement.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/queue.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/statement.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/statement_list.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/statement_list_heading.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/__snapshots__/statement_section.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/collapsible_statement.test.js (95%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/detail_drawer.test.js (98%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/metric.test.js (95%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/pipeline_viewer.test.js (94%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/plugin_statement.test.js (98%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/queue.test.js (92%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/statement.test.js (87%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/statement_list.test.js (94%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/statement_list_heading.test.js (90%) rename x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/{__test__ => }/statement_section.test.js (95%) rename x-pack/plugins/monitoring/public/components/no_data/{__tests__ => }/__snapshots__/checker_errors.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/no_data/{__tests__ => }/__snapshots__/no_data.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/no_data/{__tests__ => }/checker_errors.test.js (94%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/{__tests__ => }/__snapshots__/collection_enabled.test.js.snap (99%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/{__tests__ => }/collection_enabled.test.js (83%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/{__tests__ => }/__snapshots__/collection_interval.test.js.snap (99%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/{__tests__ => }/collection_interval.test.js (91%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/{__tests__ => }/__snapshots__/exporters.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/{__tests__ => }/exporters.test.js (93%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/{__tests__ => }/__snapshots__/plugin_enabled.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/{__tests__ => }/plugin_enabled.test.js (92%) rename x-pack/plugins/monitoring/public/components/no_data/{__tests__ => }/no_data.test.js (97%) rename x-pack/plugins/monitoring/public/components/no_data/reasons/{__tests__ => }/__snapshots__/reason_found.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/no_data/reasons/{__tests__ => }/__snapshots__/we_tried.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/no_data/reasons/{__tests__ => }/reason_found.test.js (98%) rename x-pack/plugins/monitoring/public/components/no_data/reasons/{__tests__ => }/we_tried.test.js (94%) rename x-pack/plugins/monitoring/public/components/page_loading/{__tests__ => }/__snapshots__/page_loading.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/page_loading/{__tests__ => }/page_loading.test.js (93%) rename x-pack/plugins/monitoring/public/components/sparkline/{__test__ => }/__snapshots__/index.test.js.snap (100%) rename x-pack/plugins/monitoring/public/components/sparkline/{__test__ => }/index.test.js (96%) rename x-pack/plugins/monitoring/public/lib/elasticsearch_settings/{__tests__ => }/enabler.test.js (76%) rename x-pack/plugins/monitoring/public/lib/elasticsearch_settings/{__tests__ => }/settings_checker.test.js (94%) rename x-pack/plugins/monitoring/public/lib/elasticsearch_settings/{__tests__ => }/start_checks.test.js (93%) rename x-pack/plugins/monitoring/public/views/no_data/{__tests__ => }/model_updater.test.js (88%) rename x-pack/plugins/monitoring/server/cloud/{__tests__/aws.js => aws.test.js} (80%) rename x-pack/plugins/monitoring/server/cloud/{__tests__/azure.js => azure.test.js} (83%) rename x-pack/plugins/monitoring/server/cloud/{__tests__/cloud_detector.js => cloud_detector.test.js} (81%) rename x-pack/plugins/monitoring/server/cloud/{__tests__/cloud_response.js => cloud_response.test.js} (71%) rename x-pack/plugins/monitoring/server/cloud/{__tests__/cloud_service.js => cloud_service.test.js} (63%) rename x-pack/plugins/monitoring/server/cloud/{__tests__/cloud_services.js => cloud_services.test.js} (65%) rename x-pack/plugins/monitoring/server/cloud/{__tests__/gcp.js => gcp.test.js} (79%) rename x-pack/plugins/monitoring/server/cluster_alerts/{__tests__/fixtures => __fixtures__}/create_stubs.js (100%) rename x-pack/plugins/monitoring/server/cluster_alerts/{__tests__/alerts_cluster_search.js => alerts_cluster_search.test.js} (96%) rename x-pack/plugins/monitoring/server/cluster_alerts/{__tests__/alerts_clusters_aggregation.js => alerts_clusters_aggregation.test.js} (96%) rename x-pack/plugins/monitoring/server/cluster_alerts/{__tests__/check_license.js => check_license.test.js} (98%) rename x-pack/plugins/monitoring/server/cluster_alerts/{__tests__/verify_monitoring_license.js => verify_monitoring_license.test.js} (95%) rename x-pack/plugins/monitoring/server/{__tests__/deprecations.js => deprecations.test.js} (77%) rename x-pack/plugins/monitoring/server/es_client/{__tests__/instantiate_client.js => instantiate_client.test.js} (61%) rename x-pack/plugins/monitoring/server/kibana_monitoring/{__tests__/bulk_uploader.js => bulk_uploader.test.js} (98%) rename x-pack/plugins/monitoring/server/kibana_monitoring/collectors/{__tests__/check_for_email_value.js => check_for_email_value.test.js} (53%) rename x-pack/plugins/monitoring/server/kibana_monitoring/collectors/{__tests__/get_default_admin_email.js => get_default_admin_email.test.js} (53%) rename x-pack/plugins/monitoring/server/lib/apm/{__tests__/get_apms.js => get_apms.test.js} (57%) rename x-pack/plugins/monitoring/server/lib/beats/{__tests__/fixtures => __fixtures__}/get_listing_response.js (100%) rename x-pack/plugins/monitoring/server/lib/beats/{__tests__/create_beats_query.js => create_beats_query.test.js} (72%) rename x-pack/plugins/monitoring/server/lib/beats/{__tests__/get_beat_summary.js => get_beat_summary.test.js} (95%) rename x-pack/plugins/monitoring/server/lib/beats/{__tests__/get_beats.js => get_beats.test.js} (66%) rename x-pack/plugins/monitoring/server/lib/beats/{__tests__/get_beats_for_clusters.js => get_beats_for_clusters.test.js} (87%) rename x-pack/plugins/monitoring/server/lib/beats/{__tests__/get_latest_stats.js => get_latest_stats.test.js} (90%) rename x-pack/plugins/monitoring/server/lib/beats/{__tests__/get_stats.js => get_stats.test.js} (90%) rename x-pack/plugins/monitoring/server/lib/{__tests__/calculate_auto.js => calculate_auto.test.js} (82%) rename x-pack/plugins/monitoring/server/lib/{__tests__/calculate_availabiilty.js => calculate_availabiilty.test.js} (69%) rename x-pack/plugins/monitoring/server/lib/{__tests__/calculate_overall_status.js => calculate_overall_status.test.js} (75%) rename x-pack/plugins/monitoring/server/lib/{__tests__/calculate_rate.js => calculate_rate.test.js} (82%) rename x-pack/plugins/monitoring/server/lib/{__tests__/calculate_timeseries_interval.js => calculate_timeseries_interval.test.js} (91%) rename x-pack/plugins/monitoring/server/lib/{__tests__/ccs_utils.js => ccs_utils.test.js} (95%) rename x-pack/plugins/monitoring/server/lib/cluster/{__test__/fixtures => __fixtures__}/clusters.json (100%) rename x-pack/plugins/monitoring/server/lib/cluster/{__test__ => }/__snapshots__/get_clusters_summary.test.js.snap (100%) rename x-pack/plugins/monitoring/server/lib/cluster/{__tests__/flag_supported_clusters.js => flag_supported_clusters.test.js} (93%) rename x-pack/plugins/monitoring/server/lib/cluster/{__tests__/get_cluster_status.js => get_cluster_status.test.js} (92%) rename x-pack/plugins/monitoring/server/lib/cluster/{__tests__/get_clusters_state.js => get_clusters_state.test.js} (72%) rename x-pack/plugins/monitoring/server/lib/cluster/{__tests__/get_clusters_stats.js => get_clusters_stats.test.js} (65%) rename x-pack/plugins/monitoring/server/lib/cluster/{__test__ => }/get_clusters_summary.test.js (92%) rename x-pack/plugins/monitoring/server/lib/{__tests__/create_query.js => create_query.test.js} (81%) rename x-pack/plugins/monitoring/server/lib/details/{__test__/fixtures => __fixtures__}/agg_metrics_buckets.json (100%) rename x-pack/plugins/monitoring/server/lib/details/{__test__/fixtures => __fixtures__}/deriv_metrics_buckets.json (100%) rename x-pack/plugins/monitoring/server/lib/details/{__test__/fixtures => __fixtures__}/non_deriv_metrics_buckets.json (100%) rename x-pack/plugins/monitoring/server/lib/details/{__test__ => }/__snapshots__/get_metrics.test.js.snap (100%) rename x-pack/plugins/monitoring/server/lib/details/{__test__ => }/get_metrics.test.js (92%) rename x-pack/plugins/monitoring/server/lib/elasticsearch/{__tests__/get_last_recovery.js => get_last_recovery.test.js} (62%) rename x-pack/plugins/monitoring/server/lib/elasticsearch/{__tests__/get_ml_jobs.js => get_ml_jobs.test.js} (94%) rename x-pack/plugins/monitoring/server/lib/elasticsearch_settings/{__tests__/cluster.js => cluster.test.js} (85%) rename x-pack/plugins/monitoring/server/lib/elasticsearch_settings/{__tests__/find_reason.js => find_reason.test.js} (91%) rename x-pack/plugins/monitoring/server/lib/elasticsearch_settings/{__tests__/nodes.js => nodes.test.js} (91%) rename x-pack/plugins/monitoring/server/lib/errors/{__tests__/auth_errors.js => auth_errors.test.js} (59%) rename x-pack/plugins/monitoring/server/lib/errors/{__tests__/known_errors.js => known_errors.test.js} (70%) rename x-pack/plugins/monitoring/server/lib/{__tests__ => }/helpers.js (100%) rename x-pack/plugins/monitoring/server/lib/kibana/{__tests__/get_kibana_info.js => get_kibana_info.test.js} (86%) rename x-pack/plugins/monitoring/server/lib/logstash/{__tests__/get_node_info.js => get_node_info.test.js} (91%) rename x-pack/plugins/monitoring/server/lib/logstash/{__tests__/get_pipeline.js => get_pipeline.test.js} (97%) rename x-pack/plugins/monitoring/server/lib/metrics/{__test__ => }/__snapshots__/metrics.test.js.snap (100%) rename x-pack/plugins/monitoring/server/lib/metrics/beats/{__test__ => }/cpu_utilization_calculation.test.js (97%) rename x-pack/plugins/monitoring/server/lib/metrics/classes/{__test__ => }/latency_metric_calculation.test.js (97%) rename x-pack/plugins/monitoring/server/lib/metrics/classes/{__test__ => }/quota_metric_calculation.test.js (98%) rename x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/{__test__ => }/latency_calculation.test.js (97%) rename x-pack/plugins/monitoring/server/lib/metrics/{__test__ => }/metrics.test.js (92%) rename x-pack/plugins/monitoring/server/lib/{__tests__/process_version_string.js => process_version_string.test.js} (75%) rename x-pack/plugins/monitoring/server/lib/setup/collection/{__test__ => }/get_collection_status.test.js (72%) diff --git a/x-pack/plugins/monitoring/common/__tests__/format_timestamp_to_duration.js b/x-pack/plugins/monitoring/common/format_timestamp_to_duration.test.js similarity index 85% rename from x-pack/plugins/monitoring/common/__tests__/format_timestamp_to_duration.js rename to x-pack/plugins/monitoring/common/format_timestamp_to_duration.test.js index aec30f0628f31..7dd08e4826a7c 100644 --- a/x-pack/plugins/monitoring/common/__tests__/format_timestamp_to_duration.js +++ b/x-pack/plugins/monitoring/common/format_timestamp_to_duration.test.js @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import moment from 'moment'; -import { formatTimestampToDuration } from '../format_timestamp_to_duration'; -import { CALCULATE_DURATION_SINCE, CALCULATE_DURATION_UNTIL } from '../constants'; +import { formatTimestampToDuration } from './format_timestamp_to_duration'; +import { CALCULATE_DURATION_SINCE, CALCULATE_DURATION_UNTIL } from './constants'; const testTime = moment('2010-05-01'); // pick a date where adding/subtracting 2 months formats roundly to '2 months 0 days' const getTestTime = () => moment(testTime); // clones the obj so it's not mutated with .adds and .subtracts @@ -22,15 +21,15 @@ describe('formatTimestampToDuration', () => { const fiftyNineSeconds = getTestTime().subtract(59, 'seconds'); expect( formatTimestampToDuration(fiftyNineSeconds, CALCULATE_DURATION_SINCE, getTestTime()) - ).to.be('59 seconds'); + ).toBe('59 seconds'); const fiveMins = getTestTime().subtract(5, 'minutes').subtract(30, 'seconds'); - expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_SINCE, getTestTime())).to.be( + expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_SINCE, getTestTime())).toBe( '6 mins' ); const sixHours = getTestTime().subtract(6, 'hours').subtract(30, 'minutes'); - expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_SINCE, getTestTime())).to.be( + expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_SINCE, getTestTime())).toBe( '6 hrs 30 mins' ); @@ -38,7 +37,7 @@ describe('formatTimestampToDuration', () => { .subtract(7, 'days') .subtract(6, 'hours') .subtract(18, 'minutes'); - expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_SINCE, getTestTime())).to.be( + expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_SINCE, getTestTime())).toBe( '7 days 6 hrs 18 mins' ); @@ -47,22 +46,22 @@ describe('formatTimestampToDuration', () => { .subtract(7, 'days') .subtract(6, 'hours') .subtract(18, 'minutes'); - expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_SINCE, getTestTime())).to.be( + expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_SINCE, getTestTime())).toBe( '2 months 2 days' ); const oneHour = getTestTime().subtract(1, 'hour'); // should trim 0 min - expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_SINCE, getTestTime())).to.be( + expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_SINCE, getTestTime())).toBe( '1 hr' ); const oneDay = getTestTime().subtract(1, 'day'); // should trim 0 hrs - expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_SINCE, getTestTime())).to.be( + expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_SINCE, getTestTime())).toBe( '1 day' ); const twoMonths = getTestTime().subtract(2, 'month'); // should trim 0 days - expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_SINCE, getTestTime())).to.be( + expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_SINCE, getTestTime())).toBe( '2 months' ); }); @@ -74,20 +73,20 @@ describe('formatTimestampToDuration', () => { const fiftyNineSeconds = getTestTime().add(59, 'seconds'); expect( formatTimestampToDuration(fiftyNineSeconds, CALCULATE_DURATION_UNTIL, getTestTime()) - ).to.be('59 seconds'); + ).toBe('59 seconds'); const fiveMins = getTestTime().add(10, 'minutes'); - expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( + expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_UNTIL, getTestTime())).toBe( '10 mins' ); const sixHours = getTestTime().add(6, 'hours').add(30, 'minutes'); - expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( + expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_UNTIL, getTestTime())).toBe( '6 hrs 30 mins' ); const sevenDays = getTestTime().add(7, 'days').add(6, 'hours').add(18, 'minutes'); - expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( + expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_UNTIL, getTestTime())).toBe( '7 days 6 hrs 18 mins' ); @@ -96,22 +95,22 @@ describe('formatTimestampToDuration', () => { .add(7, 'days') .add(6, 'hours') .add(18, 'minutes'); - expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( + expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_UNTIL, getTestTime())).toBe( '2 months 2 days' ); const oneHour = getTestTime().add(1, 'hour'); // should trim 0 min - expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( + expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_UNTIL, getTestTime())).toBe( '1 hr' ); const oneDay = getTestTime().add(1, 'day'); // should trim 0 hrs - expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( + expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_UNTIL, getTestTime())).toBe( '1 day' ); const twoMonths = getTestTime().add(2, 'month'); // should trim 0 days - expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( + expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_UNTIL, getTestTime())).toBe( '2 months' ); }); diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/__snapshots__/helpers.test.js.snap b/x-pack/plugins/monitoring/public/components/cluster/overview/__snapshots__/helpers.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/__snapshots__/helpers.test.js.snap rename to x-pack/plugins/monitoring/public/components/cluster/overview/__snapshots__/helpers.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js b/x-pack/plugins/monitoring/public/components/cluster/overview/helpers.test.js similarity index 95% rename from x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/helpers.test.js index a4d7e7527024d..3e70158cf5c82 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/helpers.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { BytesUsage, BytesPercentageUsage } from '../helpers'; +import { BytesUsage, BytesPercentageUsage } from './helpers'; describe('Bytes Usage', () => { it('should format correctly with used and max bytes', () => { diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__snapshots__/cells.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap rename to x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__snapshots__/cells.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.test.js similarity index 98% rename from x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.test.js index 67773a6745f96..c21733eef5a96 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { MetricCell } from '../cells'; +import { MetricCell } from './cells'; describe('Node Listing Metric Cell', () => { it('should format a percentage metric', () => { diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.test.js similarity index 65% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.test.js index b2992f3458a19..a93ad95618807 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.test.js @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { IfStatement } from '../if_statement'; -import { PluginVertex } from '../../graph/plugin_vertex'; -import { IfElement } from '../../list/if_element'; -import { PluginElement } from '../../list/plugin_element'; +import { IfStatement } from './if_statement'; +import { PluginVertex } from '../graph/plugin_vertex'; +import { IfElement } from '../list/if_element'; +import { PluginElement } from '../list/plugin_element'; describe('IfStatement class', () => { let ifVertex; @@ -57,16 +56,16 @@ describe('IfStatement class', () => { it('creates a IfStatement from vertex props', () => { const ifStatement = IfStatement.fromPipelineGraphVertex(ifVertex, pipelineStage); - expect(ifStatement.id).to.be('0aef421'); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.stats).to.eql({}); - expect(ifStatement.meta).to.be(meta); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); - expect(ifStatement.trueStatements).to.be.an(Array); - expect(ifStatement.trueStatements.length).to.be(1); - expect(ifStatement.elseStatements).to.be.an(Array); - expect(ifStatement.elseStatements.length).to.be(0); - expect(ifStatement.vertex).to.eql(ifVertex); + expect(ifStatement.id).toBe('0aef421'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.stats).toEqual({}); + expect(ifStatement.meta).toBe(meta); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); + expect(ifStatement.trueStatements).toBeInstanceOf(Array); + expect(ifStatement.trueStatements.length).toBe(1); + expect(ifStatement.elseStatements).toBeInstanceOf(Array); + expect(ifStatement.elseStatements.length).toBe(0); + expect(ifStatement.vertex).toEqual(ifVertex); }); }); @@ -99,16 +98,16 @@ describe('IfStatement class', () => { it('creates a IfStatement from vertex props', () => { const ifStatement = IfStatement.fromPipelineGraphVertex(ifVertex, pipelineStage); - expect(ifStatement.id).to.be('0aef421'); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.stats).to.eql({}); - expect(ifStatement.meta).to.be(meta); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); - expect(ifStatement.trueStatements).to.be.an(Array); - expect(ifStatement.trueStatements.length).to.be(1); - expect(ifStatement.elseStatements).to.be.an(Array); - expect(ifStatement.elseStatements.length).to.be(1); - expect(ifStatement.vertex).to.eql(ifVertex); + expect(ifStatement.id).toBe('0aef421'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.stats).toEqual({}); + expect(ifStatement.meta).toBe(meta); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); + expect(ifStatement.trueStatements).toBeInstanceOf(Array); + expect(ifStatement.trueStatements.length).toBe(1); + expect(ifStatement.elseStatements).toBeInstanceOf(Array); + expect(ifStatement.elseStatements.length).toBe(1); + expect(ifStatement.vertex).toEqual(ifVertex); }); }); @@ -142,16 +141,16 @@ describe('IfStatement class', () => { it('creates a IfStatement from vertex props', () => { const ifStatement = IfStatement.fromPipelineGraphVertex(ifVertex, pipelineStage); - expect(ifStatement.id).to.be('0aef421'); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.stats).to.eql({}); - expect(ifStatement.meta).to.be(meta); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); - expect(ifStatement.trueStatements).to.be.an(Array); - expect(ifStatement.trueStatements.length).to.be(2); - expect(ifStatement.elseStatements).to.be.an(Array); - expect(ifStatement.elseStatements.length).to.be(0); - expect(ifStatement.vertex).to.eql(ifVertex); + expect(ifStatement.id).toBe('0aef421'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.stats).toEqual({}); + expect(ifStatement.meta).toBe(meta); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); + expect(ifStatement.trueStatements).toBeInstanceOf(Array); + expect(ifStatement.trueStatements.length).toBe(2); + expect(ifStatement.elseStatements).toBeInstanceOf(Array); + expect(ifStatement.elseStatements.length).toBe(0); + expect(ifStatement.vertex).toEqual(ifVertex); }); }); @@ -193,16 +192,16 @@ describe('IfStatement class', () => { it('creates a IfStatement from vertex props', () => { const ifStatement = IfStatement.fromPipelineGraphVertex(ifVertex, pipelineStage); - expect(ifStatement.id).to.be('0aef421'); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.stats).to.eql({}); - expect(ifStatement.meta).to.be(meta); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); - expect(ifStatement.trueStatements).to.be.an(Array); - expect(ifStatement.trueStatements.length).to.be(1); - expect(ifStatement.elseStatements).to.be.an(Array); - expect(ifStatement.elseStatements.length).to.be(2); - expect(ifStatement.vertex).to.eql(ifVertex); + expect(ifStatement.id).toBe('0aef421'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.stats).toEqual({}); + expect(ifStatement.meta).toBe(meta); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); + expect(ifStatement.trueStatements).toBeInstanceOf(Array); + expect(ifStatement.trueStatements.length).toBe(1); + expect(ifStatement.elseStatements).toBeInstanceOf(Array); + expect(ifStatement.elseStatements.length).toBe(2); + expect(ifStatement.vertex).toEqual(ifVertex); }); }); @@ -220,14 +219,14 @@ describe('IfStatement class', () => { const result = ifStatement.toList(0, 'output'); - expect(result).to.be.an(Array); - expect(result.length).to.be(2); - expect(result[0]).to.be.an(IfElement); - expect(result[0].id).to.be('0aef421'); - expect(result[1]).to.be.an(PluginElement); + expect(result).toBeInstanceOf(Array); + expect(result.length).toBe(2); + expect(result[0]).toBeInstanceOf(IfElement); + expect(result[0].id).toBe('0aef421'); + expect(result[1]).toBeInstanceOf(PluginElement); const plugin = result[1]; - expect(plugin).to.be.an(PluginElement); - expect(plugin.id).to.be('es_output'); + expect(plugin).toBeInstanceOf(PluginElement); + expect(plugin.id).toBe('es_output'); }); }); }); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.test.js similarity index 68% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.test.js index cff7718eac2ab..814fc88b00653 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.test.js @@ -4,20 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { makeStatement } from '../make_statement'; -import { PluginVertex } from '../../graph/plugin_vertex'; -import { IfVertex } from '../../graph/if_vertex'; -import { QueueVertex } from '../../graph/queue_vertex'; -import { PluginStatement } from '../plugin_statement'; -import { IfStatement } from '../if_statement'; -import { Queue } from '../queue'; +import { makeStatement } from './make_statement'; +import { PluginVertex } from '../graph/plugin_vertex'; +import { IfVertex } from '../graph/if_vertex'; +import { QueueVertex } from '../graph/queue_vertex'; +import { PluginStatement } from './plugin_statement'; +import { IfStatement } from './if_statement'; +import { Queue } from './queue'; describe('makeStatement', () => { it('can make a PluginStatement from a PluginVertex', () => { const pluginVertex = new PluginVertex({}, { json: { id: 'my_grok' } }); const actual = makeStatement(pluginVertex, 'output'); - expect(actual).to.be.a(PluginStatement); + expect(actual).toBeInstanceOf(PluginStatement); }); it('can make an IfStatement from an IfVertex', () => { @@ -37,17 +36,19 @@ describe('makeStatement', () => { { json: { id: 'abcdef0' } } ); const actual = makeStatement(ifVertex, 'output'); - expect(actual).to.be.a(IfStatement); + expect(actual).toBeInstanceOf(IfStatement); }); it('can make a Queue from a QueueVertex', () => { const queueVertex = new QueueVertex({}, { json: { id: '__QUEUE__' } }); const actual = makeStatement(queueVertex); - expect(actual).to.be.a(Queue); + expect(actual).toBeInstanceOf(Queue); }); it('throws an error for an unknown type of vertex', () => { const unknownVertex = {}; - expect(makeStatement).withArgs(unknownVertex, 'output').to.throwError(); + expect(() => { + makeStatement(unknownVertex, 'output'); + }).toThrow(); }); }); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.test.js similarity index 71% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.test.js index edf57fbe6836e..4a0ce8fd686f0 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.test.js @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { Pipeline } from '../'; -import { Graph } from '../../graph'; -import { IfStatement } from '../if_statement'; -import { PluginStatement } from '../plugin_statement'; -import { Queue } from '../queue'; +import { Pipeline } from '.'; +import { Graph } from '../graph'; +import { IfStatement } from './if_statement'; +import { PluginStatement } from './plugin_statement'; +import { Queue } from './queue'; describe('Pipeline class', () => { let graph; @@ -25,10 +24,10 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); }); }); @@ -65,12 +64,12 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(1); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).not.to.be(null); + expect(pipeline.inputStatements.length).toBe(1); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).not.toBe(null); - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); }); it('fromPipelineGraph parses Queue and adds it to Pipeline', () => { @@ -78,12 +77,12 @@ describe('Pipeline class', () => { const { queue } = pipeline; - expect(queue).to.be.a(Queue); - expect(queue.id).to.equal('__QUEUE__'); - expect(queue.hasExplicitId).to.equal(false); - expect(queue.stats).to.be.a(Object); - expect(Object.keys(queue.stats).length).to.be(0); - expect(queue.meta).to.be(undefined); + expect(queue).toBeInstanceOf(Queue); + expect(queue.id).toEqual('__QUEUE__'); + expect(queue.hasExplicitId).toEqual(false); + expect(queue.stats).toBeInstanceOf(Object); + expect(Object.keys(queue.stats).length).toBe(0); + expect(queue.meta).toBe(undefined); }); }); @@ -107,12 +106,12 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -136,12 +135,12 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(1); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(1); + expect(pipeline.queue).toBe(null); - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -192,13 +191,13 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(1); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.not.be(null); + expect(pipeline.inputStatements.length).toBe(1); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).not.toBe(null); - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -249,13 +248,13 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(1); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(1); - expect(pipeline.queue).to.not.be(null); + expect(pipeline.inputStatements.length).toBe(1); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(1); + expect(pipeline.queue).not.toBe(null); - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -294,13 +293,13 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(1); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(1); + expect(pipeline.queue).toBe(null); - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -365,15 +364,15 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(1); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(1); - expect(pipeline.queue).to.not.be(null); - - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.queue).to.be.a(Queue); + expect(pipeline.inputStatements.length).toBe(1); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(1); + expect(pipeline.queue).not.toBe(null); + + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.queue).toBeInstanceOf(Queue); }); }); @@ -412,15 +411,15 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); const ifStatement = pipeline.filterStatements[0]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.trueStatements.length).to.be(1); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.trueStatements.length).toBe(1); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -459,15 +458,15 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(1); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(1); + expect(pipeline.queue).toBe(null); const ifStatement = pipeline.outputStatements[0]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.trueStatements.length).to.be(1); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.trueStatements.length).toBe(1); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -532,16 +531,16 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(1); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.not.be(null); + expect(pipeline.inputStatements.length).toBe(1); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).not.toBe(null); - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); const ifStatement = pipeline.filterStatements[0]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.trueStatements.length).to.be(1); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.trueStatements.length).toBe(1); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -641,22 +640,22 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(1); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(1); - expect(pipeline.queue).to.not.be(null); + expect(pipeline.inputStatements.length).toBe(1); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(1); + expect(pipeline.queue).not.toBe(null); - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); const filterIfStatement = pipeline.filterStatements[0]; - expect(filterIfStatement).to.be.a(IfStatement); - expect(filterIfStatement.trueStatements.length).to.be(1); - expect(filterIfStatement.trueStatements[0]).to.be.a(PluginStatement); + expect(filterIfStatement).toBeInstanceOf(IfStatement); + expect(filterIfStatement.trueStatements.length).toBe(1); + expect(filterIfStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); const outputIfStatement = pipeline.filterStatements[0]; - expect(outputIfStatement).to.be.a(IfStatement); - expect(outputIfStatement.trueStatements.length).to.be(1); - expect(outputIfStatement.trueStatements[0]).to.be.a(PluginStatement); + expect(outputIfStatement).toBeInstanceOf(IfStatement); + expect(outputIfStatement.trueStatements.length).toBe(1); + expect(outputIfStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -707,24 +706,24 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(2); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.not.be(null); - - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.inputStatements[0].id).to.be('tweet_harvester'); - expect(pipeline.inputStatements[0].hasExplicitId).to.be(true); - expect(pipeline.inputStatements[0].pluginType).to.be('input'); - expect(pipeline.inputStatements[0].name).to.be('twitter'); - - expect(pipeline.inputStatements[1]).to.be.a(PluginStatement); - expect(pipeline.inputStatements[1].id).to.be( + expect(pipeline.inputStatements.length).toBe(2); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).not.toBe(null); + + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.inputStatements[0].id).toBe('tweet_harvester'); + expect(pipeline.inputStatements[0].hasExplicitId).toBe(true); + expect(pipeline.inputStatements[0].pluginType).toBe('input'); + expect(pipeline.inputStatements[0].name).toBe('twitter'); + + expect(pipeline.inputStatements[1]).toBeInstanceOf(PluginStatement); + expect(pipeline.inputStatements[1].id).toBe( '296ae28a11c3d99d1adf44f793763db6b9c61379e0ad518371b49aa67ef902f0' ); - expect(pipeline.inputStatements[1].hasExplicitId).to.be(false); - expect(pipeline.inputStatements[1].pluginType).to.be('input'); - expect(pipeline.inputStatements[1].name).to.be('stdin'); + expect(pipeline.inputStatements[1].hasExplicitId).toBe(false); + expect(pipeline.inputStatements[1].pluginType).toBe('input'); + expect(pipeline.inputStatements[1].name).toBe('stdin'); }); }); @@ -763,24 +762,24 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(2); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); - - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[0].id).to.be('log_line_parser'); - expect(pipeline.filterStatements[0].hasExplicitId).to.be(true); - expect(pipeline.filterStatements[0].pluginType).to.be('filter'); - expect(pipeline.filterStatements[0].name).to.be('grok'); - - expect(pipeline.filterStatements[1]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[1].id).to.be( + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(2); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); + + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[0].id).toBe('log_line_parser'); + expect(pipeline.filterStatements[0].hasExplicitId).toBe(true); + expect(pipeline.filterStatements[0].pluginType).toBe('filter'); + expect(pipeline.filterStatements[0].name).toBe('grok'); + + expect(pipeline.filterStatements[1]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[1].id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(pipeline.filterStatements[1].hasExplicitId).to.be(false); - expect(pipeline.filterStatements[1].pluginType).to.be('filter'); - expect(pipeline.filterStatements[1].name).to.be('mutate'); + expect(pipeline.filterStatements[1].hasExplicitId).toBe(false); + expect(pipeline.filterStatements[1].pluginType).toBe('filter'); + expect(pipeline.filterStatements[1].name).toBe('mutate'); }); }); @@ -812,24 +811,24 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(2); - expect(pipeline.queue).to.be(null); - - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[0].id).to.be('es'); - expect(pipeline.outputStatements[0].hasExplicitId).to.be(true); - expect(pipeline.outputStatements[0].pluginType).to.be('output'); - expect(pipeline.outputStatements[0].name).to.be('elasticsearch'); - - expect(pipeline.outputStatements[1]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[1].id).to.be( + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(2); + expect(pipeline.queue).toBe(null); + + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[0].id).toBe('es'); + expect(pipeline.outputStatements[0].hasExplicitId).toBe(true); + expect(pipeline.outputStatements[0].pluginType).toBe('output'); + expect(pipeline.outputStatements[0].name).toBe('elasticsearch'); + + expect(pipeline.outputStatements[1]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[1].id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(pipeline.outputStatements[1].hasExplicitId).to.be(false); - expect(pipeline.outputStatements[1].pluginType).to.be('output'); - expect(pipeline.outputStatements[1].name).to.be('stdout'); + expect(pipeline.outputStatements[1].hasExplicitId).toBe(false); + expect(pipeline.outputStatements[1].pluginType).toBe('output'); + expect(pipeline.outputStatements[1].name).toBe('stdout'); }); }); @@ -882,26 +881,26 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(2); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(2); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[0].id).to.be('log_line_parser'); - expect(pipeline.filterStatements[0].hasExplicitId).to.be(true); - expect(pipeline.filterStatements[0].pluginType).to.be('filter'); - expect(pipeline.filterStatements[0].name).to.be('grok'); + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[0].id).toBe('log_line_parser'); + expect(pipeline.filterStatements[0].hasExplicitId).toBe(true); + expect(pipeline.filterStatements[0].pluginType).toBe('filter'); + expect(pipeline.filterStatements[0].name).toBe('grok'); const ifStatement = pipeline.filterStatements[1]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.id).to.be( + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -975,31 +974,31 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(3); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(3); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[0].id).to.be('log_line_parser'); - expect(pipeline.filterStatements[0].hasExplicitId).to.be(true); - expect(pipeline.filterStatements[0].pluginType).to.be('filter'); - expect(pipeline.filterStatements[0].name).to.be('grok'); + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[0].id).toBe('log_line_parser'); + expect(pipeline.filterStatements[0].hasExplicitId).toBe(true); + expect(pipeline.filterStatements[0].pluginType).toBe('filter'); + expect(pipeline.filterStatements[0].name).toBe('grok'); const ifStatement = pipeline.filterStatements[1]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.id).to.be( + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); - - expect(pipeline.filterStatements[2]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[2].id).to.be('micdrop'); - expect(pipeline.filterStatements[2].hasExplicitId).to.be(true); - expect(pipeline.filterStatements[2].pluginType).to.be('filter'); - expect(pipeline.filterStatements[2].name).to.be('drop'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); + + expect(pipeline.filterStatements[2]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[2].id).toBe('micdrop'); + expect(pipeline.filterStatements[2].hasExplicitId).toBe(true); + expect(pipeline.filterStatements[2].pluginType).toBe('filter'); + expect(pipeline.filterStatements[2].name).toBe('drop'); }); }); @@ -1042,29 +1041,29 @@ describe('Pipeline class', () => { }, ], }); + }); - it('fromPipelineGraph parses the pipelineGraph correctly', () => { - const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(2); - expect(pipeline.queue).to.be(null); - - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[0].id).to.be('es'); - expect(pipeline.outputStatements[0].hasExplicitId).to.be(true); - expect(pipeline.outputStatements[0].pluginType).to.be('output'); - expect(pipeline.outputStatements[0].name).to.be('elasticsearch'); - - const ifStatement = pipeline.outputStatements[1]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.id).to.be( - '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' - ); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); - }); + it('fromPipelineGraph parses the pipelineGraph correctly', () => { + const pipeline = Pipeline.fromPipelineGraph(graph); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(2); + expect(pipeline.queue).toBe(null); + + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[0].id).toBe('es'); + expect(pipeline.outputStatements[0].hasExplicitId).toBe(true); + expect(pipeline.outputStatements[0].pluginType).toBe('output'); + expect(pipeline.outputStatements[0].name).toBe('elasticsearch'); + + const ifStatement = pipeline.outputStatements[1]; + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.id).toBe( + '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' + ); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); }); }); @@ -1119,31 +1118,31 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(0); - expect(pipeline.outputStatements.length).to.be(3); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(0); + expect(pipeline.outputStatements.length).toBe(3); + expect(pipeline.queue).toBe(null); - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[0].id).to.be('es'); - expect(pipeline.outputStatements[0].hasExplicitId).to.be(true); - expect(pipeline.outputStatements[0].pluginType).to.be('output'); - expect(pipeline.outputStatements[0].name).to.be('elasticsearch'); + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[0].id).toBe('es'); + expect(pipeline.outputStatements[0].hasExplicitId).toBe(true); + expect(pipeline.outputStatements[0].pluginType).toBe('output'); + expect(pipeline.outputStatements[0].name).toBe('elasticsearch'); const ifStatement = pipeline.outputStatements[1]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.id).to.be( + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); - - expect(pipeline.outputStatements[2]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[2].id).to.be('local_persistent_out'); - expect(pipeline.outputStatements[2].hasExplicitId).to.be(true); - expect(pipeline.outputStatements[2].pluginType).to.be('output'); - expect(pipeline.outputStatements[2].name).to.be('file'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); + + expect(pipeline.outputStatements[2]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[2].id).toBe('local_persistent_out'); + expect(pipeline.outputStatements[2].hasExplicitId).toBe(true); + expect(pipeline.outputStatements[2].pluginType).toBe('output'); + expect(pipeline.outputStatements[2].name).toBe('file'); }); }); @@ -1313,63 +1312,63 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(2); - expect(pipeline.filterStatements.length).to.be(2); - expect(pipeline.outputStatements.length).to.be(3); - expect(pipeline.queue).to.not.be(null); - - expect(pipeline.inputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.inputStatements[0].id).to.be('tweet_harvester'); - expect(pipeline.inputStatements[0].hasExplicitId).to.be(true); - expect(pipeline.inputStatements[0].pluginType).to.be('input'); - expect(pipeline.inputStatements[0].name).to.be('twitter'); - - expect(pipeline.inputStatements[1]).to.be.a(PluginStatement); - expect(pipeline.inputStatements[1].id).to.be( + expect(pipeline.inputStatements.length).toBe(2); + expect(pipeline.filterStatements.length).toBe(2); + expect(pipeline.outputStatements.length).toBe(3); + expect(pipeline.queue).not.toBe(null); + + expect(pipeline.inputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.inputStatements[0].id).toBe('tweet_harvester'); + expect(pipeline.inputStatements[0].hasExplicitId).toBe(true); + expect(pipeline.inputStatements[0].pluginType).toBe('input'); + expect(pipeline.inputStatements[0].name).toBe('twitter'); + + expect(pipeline.inputStatements[1]).toBeInstanceOf(PluginStatement); + expect(pipeline.inputStatements[1].id).toBe( '296ae28a11c3d99d1adf44f793763db6b9c61379e0ad518371b49aa67ef902f0' ); - expect(pipeline.inputStatements[1].hasExplicitId).to.be(false); - expect(pipeline.inputStatements[1].pluginType).to.be('input'); - expect(pipeline.inputStatements[1].name).to.be('stdin'); + expect(pipeline.inputStatements[1].hasExplicitId).toBe(false); + expect(pipeline.inputStatements[1].pluginType).toBe('input'); + expect(pipeline.inputStatements[1].name).toBe('stdin'); - expect(pipeline.filterStatements[0]).to.be.a(PluginStatement); - expect(pipeline.filterStatements[0].id).to.be('log_line_parser'); - expect(pipeline.filterStatements[0].hasExplicitId).to.be(true); - expect(pipeline.filterStatements[0].pluginType).to.be('filter'); - expect(pipeline.filterStatements[0].name).to.be('grok'); + expect(pipeline.filterStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.filterStatements[0].id).toBe('log_line_parser'); + expect(pipeline.filterStatements[0].hasExplicitId).toBe(true); + expect(pipeline.filterStatements[0].pluginType).toBe('filter'); + expect(pipeline.filterStatements[0].name).toBe('grok'); const filterIfStatement = pipeline.filterStatements[1]; - expect(filterIfStatement).to.be.a(IfStatement); - expect(filterIfStatement.id).to.be( + expect(filterIfStatement).toBeInstanceOf(IfStatement); + expect(filterIfStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(filterIfStatement.hasExplicitId).to.be(false); - expect(filterIfStatement.condition).to.be('[is_rt] == "RT"'); - expect(filterIfStatement.trueStatements[0]).to.be.a(PluginStatement); + expect(filterIfStatement.hasExplicitId).toBe(false); + expect(filterIfStatement.condition).toBe('[is_rt] == "RT"'); + expect(filterIfStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); - expect(pipeline.outputStatements[0]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[0].id).to.be('es'); - expect(pipeline.outputStatements[0].hasExplicitId).to.be(true); - expect(pipeline.outputStatements[0].pluginType).to.be('output'); - expect(pipeline.outputStatements[0].name).to.be('elasticsearch'); + expect(pipeline.outputStatements[0]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[0].id).toBe('es'); + expect(pipeline.outputStatements[0].hasExplicitId).toBe(true); + expect(pipeline.outputStatements[0].pluginType).toBe('output'); + expect(pipeline.outputStatements[0].name).toBe('elasticsearch'); const outputIfStatement = pipeline.outputStatements[1]; - expect(outputIfStatement).to.be.a(IfStatement); - expect(outputIfStatement.id).to.be( + expect(outputIfStatement).toBeInstanceOf(IfStatement); + expect(outputIfStatement.id).toBe( '90f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e84a8' ); - expect(outputIfStatement.hasExplicitId).to.be(false); - expect(outputIfStatement.condition).to.be('[is_rt] == "RT"'); - expect(outputIfStatement.trueStatements[0]).to.be.a(PluginStatement); - - expect(pipeline.outputStatements[2]).to.be.a(PluginStatement); - expect(pipeline.outputStatements[2].id).to.be('local_persistent_out'); - expect(pipeline.outputStatements[2].hasExplicitId).to.be(true); - expect(pipeline.outputStatements[2].pluginType).to.be('output'); - expect(pipeline.outputStatements[2].name).to.be('file'); - - expect(pipeline.queue).to.be.a(Queue); - expect(pipeline.queue.id).to.be('__QUEUE__'); + expect(outputIfStatement.hasExplicitId).toBe(false); + expect(outputIfStatement.condition).toBe('[is_rt] == "RT"'); + expect(outputIfStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); + + expect(pipeline.outputStatements[2]).toBeInstanceOf(PluginStatement); + expect(pipeline.outputStatements[2].id).toBe('local_persistent_out'); + expect(pipeline.outputStatements[2].hasExplicitId).toBe(true); + expect(pipeline.outputStatements[2].pluginType).toBe('output'); + expect(pipeline.outputStatements[2].name).toBe('file'); + + expect(pipeline.queue).toBeInstanceOf(Queue); + expect(pipeline.queue.id).toBe('__QUEUE__'); }); }); @@ -1423,26 +1422,26 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); const ifStatement = pipeline.filterStatements[0]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.id).to.be( + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); - expect(ifStatement.trueStatements[0].id).to.be( + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); + expect(ifStatement.trueStatements[0].id).toBe( 'a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e84' ); - expect(ifStatement.elseStatements[0]).to.be.a(PluginStatement); - expect(ifStatement.elseStatements[0].id).to.be('micdrop'); + expect(ifStatement.elseStatements[0]).toBeInstanceOf(PluginStatement); + expect(ifStatement.elseStatements[0].id).toBe('micdrop'); }); }); @@ -1495,28 +1494,28 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); const ifStatement = pipeline.filterStatements[0]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.id).to.be( + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); - expect(ifStatement.trueStatements.length).to.be(2); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); - expect(ifStatement.trueStatements[0].id).to.be( + expect(ifStatement.trueStatements.length).toBe(2); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); + expect(ifStatement.trueStatements[0].id).toBe( 'a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e84' ); - expect(ifStatement.trueStatements[1]).to.be.a(PluginStatement); - expect(ifStatement.trueStatements[1].id).to.be('micdrop'); + expect(ifStatement.trueStatements[1]).toBeInstanceOf(PluginStatement); + expect(ifStatement.trueStatements[1].id).toBe('micdrop'); - expect(ifStatement.elseStatements.length).to.be(0); + expect(ifStatement.elseStatements.length).toBe(0); }); }); @@ -1584,32 +1583,32 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); const ifStatement = pipeline.filterStatements[0]; - expect(ifStatement).to.be.a(IfStatement); - expect(ifStatement.id).to.be( + expect(ifStatement).toBeInstanceOf(IfStatement); + expect(ifStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(ifStatement.hasExplicitId).to.be(false); - expect(ifStatement.condition).to.be('[is_rt] == "RT"'); + expect(ifStatement.hasExplicitId).toBe(false); + expect(ifStatement.condition).toBe('[is_rt] == "RT"'); - expect(ifStatement.trueStatements.length).to.be(1); - expect(ifStatement.trueStatements[0]).to.be.a(PluginStatement); - expect(ifStatement.trueStatements[0].id).to.be( + expect(ifStatement.trueStatements.length).toBe(1); + expect(ifStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); + expect(ifStatement.trueStatements[0].id).toBe( '890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e84a' ); - expect(ifStatement.elseStatements.length).to.be(2); - expect(ifStatement.elseStatements[0]).to.be.a(PluginStatement); - expect(ifStatement.elseStatements[0].id).to.be( + expect(ifStatement.elseStatements.length).toBe(2); + expect(ifStatement.elseStatements[0]).toBeInstanceOf(PluginStatement); + expect(ifStatement.elseStatements[0].id).toBe( 'a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e84' ); - expect(ifStatement.elseStatements[1]).to.be.a(PluginStatement); - expect(ifStatement.elseStatements[1].id).to.be('micdrop'); + expect(ifStatement.elseStatements[1]).toBeInstanceOf(PluginStatement); + expect(ifStatement.elseStatements[1].id).toBe('micdrop'); }); }); @@ -1680,12 +1679,12 @@ describe('Pipeline class', () => { it('has two child statements', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.outputStatements.length).to.be(1); + expect(pipeline.outputStatements.length).toBe(1); const { trueStatements } = pipeline.outputStatements[0]; - expect(trueStatements.length).to.be(2); - expect(trueStatements[0].id).to.be('plugin_1'); - expect(trueStatements[1].id).to.be('plugin_2'); - expect(pipeline.outputStatements[0].elseStatements.length).to.be(0); + expect(trueStatements.length).toBe(2); + expect(trueStatements[0].id).toBe('plugin_1'); + expect(trueStatements[1].id).toBe('plugin_2'); + expect(pipeline.outputStatements[0].elseStatements.length).toBe(0); }); }); @@ -1779,13 +1778,13 @@ describe('Pipeline class', () => { it('has two child else statements', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.outputStatements.length).to.be(1); + expect(pipeline.outputStatements.length).toBe(1); const { trueStatements, elseStatements } = pipeline.outputStatements[0]; - expect(trueStatements.length).to.be(1); - expect(trueStatements[0].id).to.be('plugin_3'); - expect(elseStatements.length).to.be(2); - expect(elseStatements[0].id).to.be('plugin_1'); - expect(elseStatements[1].id).to.be('plugin_2'); + expect(trueStatements.length).toBe(1); + expect(trueStatements[0].id).toBe('plugin_3'); + expect(elseStatements.length).toBe(2); + expect(elseStatements[0].id).toBe('plugin_1'); + expect(elseStatements[1].id).toBe('plugin_2'); }); }); @@ -1838,30 +1837,30 @@ describe('Pipeline class', () => { it('fromPipelineGraph parses the pipelineGraph correctly', () => { const pipeline = Pipeline.fromPipelineGraph(graph); - expect(pipeline.inputStatements.length).to.be(0); - expect(pipeline.filterStatements.length).to.be(1); - expect(pipeline.outputStatements.length).to.be(0); - expect(pipeline.queue).to.be(null); + expect(pipeline.inputStatements.length).toBe(0); + expect(pipeline.filterStatements.length).toBe(1); + expect(pipeline.outputStatements.length).toBe(0); + expect(pipeline.queue).toBe(null); const outerIfStatement = pipeline.filterStatements[0]; - expect(outerIfStatement).to.be.a(IfStatement); - expect(outerIfStatement.id).to.be( + expect(outerIfStatement).toBeInstanceOf(IfStatement); + expect(outerIfStatement.id).toBe( '4a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e8' ); - expect(outerIfStatement.hasExplicitId).to.be(false); - expect(outerIfStatement.condition).to.be('[is_rt] == "RT"'); + expect(outerIfStatement.hasExplicitId).toBe(false); + expect(outerIfStatement.condition).toBe('[is_rt] == "RT"'); const innerIfStatement = outerIfStatement.trueStatements[0]; - expect(innerIfStatement).to.be.a(IfStatement); - expect(innerIfStatement.id).to.be( + expect(innerIfStatement).toBeInstanceOf(IfStatement); + expect(innerIfStatement.id).toBe( 'a890f3e5c135c037eb40ba88d69b040faaeb954bb10510e95294259ffdd88e84' ); - expect(innerIfStatement.hasExplicitId).to.be(false); - expect(innerIfStatement.condition).to.be('[has_image] == true'); + expect(innerIfStatement.hasExplicitId).toBe(false); + expect(innerIfStatement.condition).toBe('[has_image] == true'); - expect(innerIfStatement.trueStatements.length).to.be(1); - expect(innerIfStatement.trueStatements[0]).to.be.a(PluginStatement); - expect(innerIfStatement.trueStatements[0].id).to.be('micdrop'); + expect(innerIfStatement.trueStatements.length).toBe(1); + expect(innerIfStatement.trueStatements[0]).toBeInstanceOf(PluginStatement); + expect(innerIfStatement.trueStatements[0].id).toBe('micdrop'); }); }); }); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.test.js similarity index 66% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.test.js index 78f16b1122fe8..40f30ae66f389 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { PluginStatement } from '../plugin_statement'; +import { PluginStatement } from './plugin_statement'; describe('PluginStatement class', () => { let pluginVertex; @@ -33,13 +32,13 @@ describe('PluginStatement class', () => { it('creates a PluginStatement from vertex props', () => { const pluginStatement = PluginStatement.fromPipelineGraphVertex(pluginVertex); - expect(pluginStatement.id).to.be('es_output'); - expect(pluginStatement.hasExplicitId).to.be(true); - expect(pluginStatement.stats).to.eql({}); - expect(pluginStatement.meta).to.be(meta); - expect(pluginStatement.pluginType).to.be('output'); - expect(pluginStatement.name).to.be('elasticsearch'); - expect(pluginStatement.vertex).to.eql(pluginVertex); + expect(pluginStatement.id).toBe('es_output'); + expect(pluginStatement.hasExplicitId).toBe(true); + expect(pluginStatement.stats).toEqual({}); + expect(pluginStatement.meta).toBe(meta); + expect(pluginStatement.pluginType).toBe('output'); + expect(pluginStatement.name).toBe('elasticsearch'); + expect(pluginStatement.vertex).toEqual(pluginVertex); }); }); @@ -48,8 +47,8 @@ describe('PluginStatement class', () => { const pluginStatement = PluginStatement.fromPipelineGraphVertex(pluginVertex); const result = pluginStatement.toList(); - expect(result.length).to.be(1); - expect(result[0].id).to.be('es_output'); + expect(result.length).toBe(1); + expect(result[0].id).toBe('es_output'); }); }); }); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.test.js similarity index 71% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.test.js index 8e53fb349e027..54442cd48cdf6 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { Queue } from '../queue'; +import { Queue } from './queue'; describe('Queue class', () => { let queueVertex; @@ -32,12 +31,12 @@ describe('Queue class', () => { it('fromPipelineGraphVertex creates new Queue from vertex props', () => { const queue = Queue.fromPipelineGraphVertex(queueVertex); - expect(queue.id).to.be('__QUEUE__'); - expect(queue.hasExplicitId).to.be(false); - expect(queue.stats).to.eql({}); - expect(queue.meta).to.be(meta); - expect(queue).to.be.a(Queue); - expect(queue.vertex).to.eql(queueVertex); + expect(queue.id).toBe('__QUEUE__'); + expect(queue.hasExplicitId).toBe(false); + expect(queue.stats).toEqual({}); + expect(queue.meta).toBe(meta); + expect(queue).toBeInstanceOf(Queue); + expect(queue.vertex).toEqual(queueVertex); }); }); }); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.test.js similarity index 69% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.test.js index b64c9d71f6710..b3c4ab18cc691 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { Statement } from '../statement'; +import { Statement } from './statement'; describe('Statement class', () => { let vertex; @@ -31,11 +30,11 @@ describe('Statement class', () => { it('creates a new Statement instance', () => { const statement = new Statement(vertex); - expect(statement.id).to.be('statement_id'); - expect(statement.hasExplicitId).to.be(true); - expect(statement.stats).to.eql({}); - expect(statement.meta).to.equal(meta); - expect(statement.vertex).to.eql(vertex); + expect(statement.id).toBe('statement_id'); + expect(statement.hasExplicitId).toBe(true); + expect(statement.stats).toEqual({}); + expect(statement.meta).toEqual(meta); + expect(statement.vertex).toEqual(vertex); }); }); }); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.test.js similarity index 85% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.test.js index fc50c3b9dfedb..783265c33484a 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { isVertexPipelineStage } from '../utils'; +import { isVertexPipelineStage } from './utils'; describe('Utils', () => { let vertex; @@ -20,7 +19,7 @@ describe('Utils', () => { vertex = undefined; const actual = isVertexPipelineStage(vertex, pipelineStage); - expect(actual).to.be(undefined); + expect(actual).toBe(undefined); }); }); @@ -29,7 +28,7 @@ describe('Utils', () => { vertex = null; const actual = isVertexPipelineStage(vertex, pipelineStage); - expect(actual).to.be(null); + expect(actual).toBe(null); }); }); @@ -38,7 +37,7 @@ describe('Utils', () => { vertex = {}; const actual = isVertexPipelineStage(vertex, pipelineStage); - expect(actual).to.be(false); + expect(actual).toBe(false); }); }); @@ -48,7 +47,7 @@ describe('Utils', () => { pipelineStage = undefined; const actual = isVertexPipelineStage(vertex, pipelineStage); - expect(actual).to.be(false); + expect(actual).toBe(false); }); it('isVertexPipelineStage returns false for null pipelineStage', () => { @@ -56,7 +55,7 @@ describe('Utils', () => { pipelineStage = null; const actual = isVertexPipelineStage(vertex, pipelineStage); - expect(actual).to.be(false); + expect(actual).toBe(false); }); }); @@ -65,7 +64,7 @@ describe('Utils', () => { vertex = { pipelineStage: 'input' }; const actual = isVertexPipelineStage(vertex, pipelineStage); - expect(actual).to.be(true); + expect(actual).toBe(true); }); }); }); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/collapsible_statement.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/collapsible_statement.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/detail_drawer.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/detail_drawer.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/detail_drawer.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/detail_drawer.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/metric.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/metric.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/metric.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/metric.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/pipeline_viewer.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/pipeline_viewer.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/pipeline_viewer.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/pipeline_viewer.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/plugin_statement.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/plugin_statement.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/queue.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/queue.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/queue.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/queue.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement_list.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement_list.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list_heading.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement_list_heading.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list_heading.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement_list_heading.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_section.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement_section.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_section.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__snapshots__/statement_section.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.test.js similarity index 95% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.test.js index ac196c014035f..eee55fd12f1c8 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { CollapsibleStatement } from '../collapsible_statement'; +import { CollapsibleStatement } from './collapsible_statement'; import { shallow } from 'enzyme'; import { EuiButtonIcon } from '@elastic/eui'; diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.test.js similarity index 98% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.test.js index 09f4d03953038..96979fb4d306d 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.test.js @@ -5,10 +5,10 @@ */ import React from 'react'; -import { DetailDrawer } from '../detail_drawer'; +import { DetailDrawer } from './detail_drawer'; import { shallow } from 'enzyme'; -jest.mock('../../../../sparkline', () => ({ +jest.mock('../../../sparkline', () => ({ Sparkline: () => 'Sparkline', })); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.test.js similarity index 95% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.test.js index c623074317c54..5587599e88a4b 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { Metric } from '../metric'; +import { Metric } from './metric'; import { shallow } from 'enzyme'; describe('Metric component', () => { diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.test.js similarity index 94% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.test.js index 8c2558bee4e44..cc347f5e6d706 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.test.js @@ -5,10 +5,10 @@ */ import React from 'react'; -import { PipelineViewer } from '../pipeline_viewer'; +import { PipelineViewer } from './pipeline_viewer'; import { shallow } from 'enzyme'; -jest.mock('../../../../sparkline', () => ({ +jest.mock('../../../sparkline', () => ({ Sparkline: () => 'Sparkline', })); diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.test.js similarity index 98% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.test.js index 317aebf1f21cb..4e861adcba70a 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { PluginStatement } from '../plugin_statement'; +import { PluginStatement } from './plugin_statement'; import { shallow } from 'enzyme'; import { EuiButtonEmpty, EuiBadge } from '@elastic/eui'; diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.test.js similarity index 92% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.test.js index 2d107ed77d664..b0b29811f1f52 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { Queue } from '../queue'; +import { Queue } from './queue'; import { shallow } from 'enzyme'; describe('Queue component', () => { diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.test.js similarity index 87% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.test.js index 88f2ae861da11..0604840e52a17 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.test.js @@ -5,11 +5,11 @@ */ import React from 'react'; -import { Statement } from '../statement'; -import { PluginStatement } from '../../models/pipeline/plugin_statement'; -import { PluginStatement as PluginStatementComponent } from '../plugin_statement'; -import { IfElement } from '../../models/list/if_element'; -import { CollapsibleStatement } from '../collapsible_statement'; +import { Statement } from './statement'; +import { PluginStatement } from '../models/pipeline/plugin_statement'; +import { PluginStatement as PluginStatementComponent } from './plugin_statement'; +import { IfElement } from '../models/list/if_element'; +import { CollapsibleStatement } from './collapsible_statement'; import { shallow } from 'enzyme'; import { EuiButtonEmpty } from '@elastic/eui'; diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.test.js similarity index 94% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.test.js index 6cbd6c01443a4..195a5f798b16f 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.test.js @@ -5,8 +5,8 @@ */ import React from 'react'; -import { StatementList } from '../statement_list'; -import { Statement } from '../statement'; +import { StatementList } from './statement_list'; +import { Statement } from './statement'; import { shallow } from 'enzyme'; describe('StatementList', () => { diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.test.js similarity index 90% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.test.js index e4d68901ff544..4759b9ec85273 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { StatementListHeading } from '../statement_list_heading'; +import { StatementListHeading } from './statement_list_heading'; import { shallow } from 'enzyme'; describe('StatementListHeading component', () => { diff --git a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.test.js similarity index 95% rename from x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.test.js index 679c60ee8eaab..a9acf93a43f11 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { StatementSection } from '../statement_section'; +import { StatementSection } from './statement_section'; import { shallow } from 'enzyme'; describe('StatementSection component', () => { diff --git a/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/checker_errors.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/checker_errors.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/checker_errors.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/__snapshots__/checker_errors.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js b/x-pack/plugins/monitoring/public/components/no_data/checker_errors.test.js similarity index 94% rename from x-pack/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js rename to x-pack/plugins/monitoring/public/components/no_data/checker_errors.test.js index b3dd093022a2b..37ef5d7f0d96d 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/checker_errors.test.js @@ -7,7 +7,7 @@ import React from 'react'; import { boomify, forbidden } from '@hapi/boom'; import { renderWithIntl } from '@kbn/test/jest'; -import { CheckerErrors } from '../checker_errors'; +import { CheckerErrors } from './checker_errors'; describe('CheckerErrors', () => { test('should render nothing if errors is empty', () => { diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__snapshots__/collection_enabled.test.js.snap similarity index 99% rename from x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__snapshots__/collection_enabled.test.js.snap index 0af2fbb01ab65..a36b09edf1fd3 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__snapshots__/collection_enabled.test.js.snap @@ -4,7 +4,7 @@ exports[`ExplainCollectionEnabled should explain about xpack.monitoring.collecti { beforeEach(() => { - enabler.enableCollectionEnabled = sinon.spy(); + enabler.enableCollectionEnabled = jest.fn(); const reason = { property: 'xpack.monitoring.collection.enabled', data: '-1', @@ -33,6 +32,6 @@ describe('ExplainCollectionEnabled', () => { const rendered = mountWithIntl(component); const actionButton = findTestSubject(rendered, 'enableCollectionEnabled'); actionButton.simulate('click'); - expect(enabler.enableCollectionEnabled.calledOnce).toBe(true); + expect(enabler.enableCollectionEnabled).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__snapshots__/collection_interval.test.js.snap similarity index 99% rename from x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__snapshots__/collection_interval.test.js.snap index c03507a623238..ebc4a9fa885f3 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__snapshots__/collection_interval.test.js.snap @@ -4,7 +4,7 @@ exports[`ExplainCollectionInterval collection interval setting updates should sh { beforeEach(() => { - enabler.enableCollectionInterval = sinon.spy(); + enabler.enableCollectionInterval = jest.fn(); }); test('should explain about xpack.monitoring.collection.interval setting', () => { @@ -47,7 +46,7 @@ describe('ExplainCollectionInterval', () => { const rendered = mountWithIntl(component); const actionButton = findTestSubject(rendered, 'enableCollectionInterval'); actionButton.simulate('click'); - expect(enabler.enableCollectionInterval.calledOnce).toBe(true); + expect(enabler.enableCollectionInterval).toHaveBeenCalledTimes(1); }); describe('collection interval setting updates', () => { diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/__snapshots__/exporters.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/__snapshots__/exporters.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.test.js similarity index 93% rename from x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.test.js index ea41cf1b81cd5..2bc581ffb1abb 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { ExplainExporters, ExplainExportersCloud } from '../exporters'; +import { ExplainExporters, ExplainExportersCloud } from './exporters'; describe('ExplainExporters', () => { test('should explain about xpack.monitoring.exporters setting', () => { diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/__snapshots__/plugin_enabled.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__snapshots__/plugin_enabled.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/__snapshots__/plugin_enabled.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__snapshots__/plugin_enabled.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.test.js similarity index 92% rename from x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.test.js index 2f101f44f014a..b9eac16692210 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { ExplainPluginEnabled } from '../plugin_enabled'; +import { ExplainPluginEnabled } from './plugin_enabled'; describe('ExplainPluginEnabled', () => { test('should explain about xpack.monitoring.enabled setting', () => { diff --git a/x-pack/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js b/x-pack/plugins/monitoring/public/components/no_data/no_data.test.js similarity index 97% rename from x-pack/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js rename to x-pack/plugins/monitoring/public/components/no_data/no_data.test.js index f692c7ee919dc..d501ba15f9361 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/no_data.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { NoData } from '../'; +import { NoData } from '.'; const enabler = {}; diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/reason_found.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/reason_found.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/we_tried.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/we_tried.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/we_tried.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/we_tried.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.test.js similarity index 98% rename from x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js rename to x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.test.js index f30799ebd4f73..b4abda87ea1e0 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { ReasonFound } from '../'; +import { ReasonFound } from '.'; const enabler = {}; diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.test.js similarity index 94% rename from x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js rename to x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.test.js index 57cee4baeb8bf..4a46c3e4ad7f0 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { WeTried } from '../'; +import { WeTried } from '.'; describe('WeTried', () => { test('should render "we tried" message', () => { diff --git a/x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap b/x-pack/plugins/monitoring/public/components/page_loading/__snapshots__/page_loading.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap rename to x-pack/plugins/monitoring/public/components/page_loading/__snapshots__/page_loading.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js b/x-pack/plugins/monitoring/public/components/page_loading/page_loading.test.js similarity index 93% rename from x-pack/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js rename to x-pack/plugins/monitoring/public/components/page_loading/page_loading.test.js index bbe6afce193d5..b79fea011fe7e 100644 --- a/x-pack/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js +++ b/x-pack/plugins/monitoring/public/components/page_loading/page_loading.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithIntl } from '@kbn/test/jest'; -import { PageLoading } from '../'; +import { PageLoading } from '.'; describe('PageLoading', () => { test('should show a simple page loading component', () => { diff --git a/x-pack/plugins/monitoring/public/components/sparkline/__test__/__snapshots__/index.test.js.snap b/x-pack/plugins/monitoring/public/components/sparkline/__snapshots__/index.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/public/components/sparkline/__test__/__snapshots__/index.test.js.snap rename to x-pack/plugins/monitoring/public/components/sparkline/__snapshots__/index.test.js.snap diff --git a/x-pack/plugins/monitoring/public/components/sparkline/__test__/index.test.js b/x-pack/plugins/monitoring/public/components/sparkline/index.test.js similarity index 96% rename from x-pack/plugins/monitoring/public/components/sparkline/__test__/index.test.js rename to x-pack/plugins/monitoring/public/components/sparkline/index.test.js index 6ce4b051d428c..a35931a223c19 100644 --- a/x-pack/plugins/monitoring/public/components/sparkline/__test__/index.test.js +++ b/x-pack/plugins/monitoring/public/components/sparkline/index.test.js @@ -7,9 +7,9 @@ import React from 'react'; import renderer from 'react-test-renderer'; import { shallow } from 'enzyme'; -import { Sparkline } from '../'; +import { Sparkline } from '.'; -jest.mock('../sparkline_flot_chart', () => ({ +jest.mock('./sparkline_flot_chart', () => ({ SparklineFlotChart: () => 'SparklineFlotChart', })); diff --git a/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/enabler.test.js b/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/enabler.test.js similarity index 76% rename from x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/enabler.test.js rename to x-pack/plugins/monitoring/public/lib/elasticsearch_settings/enabler.test.js index 70fc8e405f904..a20382dcefe86 100644 --- a/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/enabler.test.js +++ b/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/enabler.test.js @@ -4,12 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Enabler } from '../'; -import sinon from 'sinon'; +import { Enabler } from '.'; import { forbidden } from '@hapi/boom'; -const updateModel = (properties) => properties; -const updateModelSpy = sinon.spy(updateModel); +const updateModelSpy = jest.fn((properties) => properties); describe('Settings Enabler Class for calling API to update Elasticsearch Settings', () => { test('should return status from successfully calling API', async () => { @@ -26,11 +24,11 @@ describe('Settings Enabler Class for calling API to update Elasticsearch Setting await enabler.enableCollectionInterval(); - expect(updateModelSpy.callCount).toBe(2); - expect(updateModelSpy.getCall(0).args[0]).toEqual({ + expect(updateModelSpy).toHaveBeenCalledTimes(2); + expect(updateModelSpy.mock.calls[0][0]).toEqual({ isCollectionIntervalUpdating: true, }); - expect(updateModelSpy.getCall(1).args[0]).toEqual({ + expect(updateModelSpy.mock.calls[1][0]).toEqual({ isCollectionIntervalUpdated: true, isCollectionIntervalUpdating: false, }); @@ -47,11 +45,11 @@ describe('Settings Enabler Class for calling API to update Elasticsearch Setting const enabler = new Enabler(get$http(), updateModelSpy); await enabler.enableCollectionInterval(); - expect(updateModelSpy.callCount).toBe(4); - expect(updateModelSpy.firstCall.args[0]).toEqual({ + expect(updateModelSpy).toHaveBeenCalledTimes(4); + expect(updateModelSpy.mock.calls[0][0]).toEqual({ isCollectionIntervalUpdating: true, }); - expect(updateModelSpy.lastCall.args[0]).toEqual({ + expect(updateModelSpy.mock.calls[updateModelSpy.mock.calls.length - 1][0]).toEqual({ errors: { error: 'Forbidden', message: 'this is not available', diff --git a/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/settings_checker.test.js b/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/settings_checker.test.js similarity index 94% rename from x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/settings_checker.test.js rename to x-pack/plugins/monitoring/public/lib/elasticsearch_settings/settings_checker.test.js index 6031c2c3feef3..1dac5530c70bc 100644 --- a/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/settings_checker.test.js +++ b/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/settings_checker.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SettingsChecker } from '../checkers/settings_checker'; +import { SettingsChecker } from './checkers/settings_checker'; describe('Settings Checker Class for Elasticsearch Settings', () => { const getHttp = () => ({ diff --git a/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/start_checks.test.js b/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.test.js similarity index 93% rename from x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/start_checks.test.js rename to x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.test.js index 529a8ae3ed5a7..6ea9d5eebc42d 100644 --- a/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/start_checks.test.js +++ b/x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.test.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SettingsChecker } from '../checkers/settings_checker'; -import { startChecks } from '../'; +import { SettingsChecker } from './checkers/settings_checker'; +import { startChecks } from '.'; describe('Start Checks of Elasticsearch Settings', () => { const getHttp = (data) => ({ diff --git a/x-pack/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js b/x-pack/plugins/monitoring/public/views/no_data/model_updater.test.js similarity index 88% rename from x-pack/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js rename to x-pack/plugins/monitoring/public/views/no_data/model_updater.test.js index 8be378be84189..cb02b808320fb 100644 --- a/x-pack/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js +++ b/x-pack/plugins/monitoring/public/views/no_data/model_updater.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import sinon from 'sinon'; -import { ModelUpdater } from '../model_updater'; +import { ModelUpdater } from './model_updater'; describe('Model Updater for Angular Controller with React Components', () => { let $scope; @@ -19,12 +18,12 @@ describe('Model Updater for Angular Controller with React Components', () => { model = {}; updater = new ModelUpdater($scope, model); - sinon.spy(updater, 'updateModel'); + jest.spyOn(updater, 'updateModel'); }); test('should successfully construct an object', () => { expect(typeof updater).toBe('object'); - expect(updater.updateModel.called).toBe(false); + expect(updater.updateModel).not.toHaveBeenCalled(); }); test('updateModel method should add properties to the model', () => { diff --git a/x-pack/plugins/monitoring/server/cloud/__tests__/aws.js b/x-pack/plugins/monitoring/server/cloud/aws.test.js similarity index 80% rename from x-pack/plugins/monitoring/server/cloud/__tests__/aws.js rename to x-pack/plugins/monitoring/server/cloud/aws.test.js index 767f2a951f4be..dec2fec43fd3c 100644 --- a/x-pack/plugins/monitoring/server/cloud/__tests__/aws.js +++ b/x-pack/plugins/monitoring/server/cloud/aws.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { AWS, AWSCloudService } from '../aws'; +import { AWS, AWSCloudService } from './aws'; describe('AWS', () => { const expectedFilename = '/sys/hypervisor/uuid'; @@ -14,26 +13,26 @@ describe('AWS', () => { const ec2Uuid = 'eC2abcdef-ghijk\n'; const ec2FileSystem = { readFile: (filename, encoding, callback) => { - expect(filename).to.eql(expectedFilename); - expect(encoding).to.eql(expectedEncoding); + expect(filename).toEqual(expectedFilename); + expect(encoding).toEqual(expectedEncoding); callback(null, ec2Uuid); }, }; it('is named "aws"', () => { - expect(AWS.getName()).to.eql('aws'); + expect(AWS.getName()).toEqual('aws'); }); describe('_checkIfService', () => { it('handles expected response', async () => { const id = 'abcdef'; const request = (req, callback) => { - expect(req.method).to.eql('GET'); - expect(req.uri).to.eql( + expect(req.method).toEqual('GET'); + expect(req.uri).toEqual( 'http://169.254.169.254/2016-09-02/dynamic/instance-identity/document' ); - expect(req.json).to.eql(true); + expect(req.json).toEqual(true); const body = `{"instanceId": "${id}","availabilityZone":"us-fake-2c", "imageId" : "ami-6df1e514"}`; @@ -47,8 +46,8 @@ describe('AWS', () => { const response = await awsCheckedFileSystem._checkIfService(request); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: AWS.getName(), id, region: undefined, @@ -69,8 +68,8 @@ describe('AWS', () => { const response = await awsCheckedFileSystem._checkIfService(request); - expect(response.isConfirmed()).to.be(true); - expect(response.toJSON()).to.eql({ + expect(response.isConfirmed()).toBe(true); + expect(response.toJSON()).toEqual({ name: AWS.getName(), id: ec2Uuid.trim().toLowerCase(), region: undefined, @@ -90,8 +89,8 @@ describe('AWS', () => { const response = await awsCheckedFileSystem._checkIfService(failedRequest); - expect(response.isConfirmed()).to.be(true); - expect(response.toJSON()).to.eql({ + expect(response.isConfirmed()).toBe(true); + expect(response.toJSON()).toEqual({ name: AWS.getName(), id: ec2Uuid.trim().toLowerCase(), region: undefined, @@ -110,8 +109,8 @@ describe('AWS', () => { const response = await awsIgnoredFileSystem._checkIfService(failedRequest); - expect(response.getName()).to.eql(AWS.getName()); - expect(response.isConfirmed()).to.be(false); + expect(response.getName()).toEqual(AWS.getName()); + expect(response.isConfirmed()).toBe(false); }); }); @@ -136,9 +135,9 @@ describe('AWS', () => { const response = AWS._parseBody(body); - expect(response.getName()).to.eql(AWS.getName()); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.getName()).toEqual(AWS.getName()); + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: 'aws', id: 'i-0c7a5b7590a4d811c', vm_type: 't2.micro', @@ -156,10 +155,10 @@ describe('AWS', () => { }); it('ignores unexpected response body', () => { - expect(AWS._parseBody(undefined)).to.be(null); - expect(AWS._parseBody(null)).to.be(null); - expect(AWS._parseBody({})).to.be(null); - expect(AWS._parseBody({ privateIp: 'a.b.c.d' })).to.be(null); + expect(AWS._parseBody(undefined)).toBe(null); + expect(AWS._parseBody(null)).toBe(null); + expect(AWS._parseBody({})).toBe(null); + expect(AWS._parseBody({ privateIp: 'a.b.c.d' })).toBe(null); }); }); @@ -172,8 +171,8 @@ describe('AWS', () => { const response = await awsCheckedFileSystem._tryToDetectUuid(); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: AWS.getName(), id: ec2Uuid.trim().toLowerCase(), region: undefined, @@ -186,8 +185,8 @@ describe('AWS', () => { it('ignores UUID if it does not start with ec2', async () => { const notEC2FileSystem = { readFile: (filename, encoding, callback) => { - expect(filename).to.eql(expectedFilename); - expect(encoding).to.eql(expectedEncoding); + expect(filename).toEqual(expectedFilename); + expect(encoding).toEqual(expectedEncoding); callback(null, 'notEC2'); }, @@ -200,7 +199,7 @@ describe('AWS', () => { const response = await awsCheckedFileSystem._tryToDetectUuid(); - expect(response.isConfirmed()).to.eql(false); + expect(response.isConfirmed()).toEqual(false); }); it('does NOT check the file system for UUID on Windows', async () => { @@ -211,7 +210,7 @@ describe('AWS', () => { const response = await awsUncheckedFileSystem._tryToDetectUuid(); - expect(response.isConfirmed()).to.eql(false); + expect(response.isConfirmed()).toEqual(false); }); it('does NOT handle file system exceptions', async () => { @@ -230,7 +229,7 @@ describe('AWS', () => { expect().fail('Method should throw exception (Promise.reject)'); } catch (err) { - expect(err).to.be(fileDNE); + expect(err).toBe(fileDNE); } }); }); diff --git a/x-pack/plugins/monitoring/server/cloud/__tests__/azure.js b/x-pack/plugins/monitoring/server/cloud/azure.test.js similarity index 83% rename from x-pack/plugins/monitoring/server/cloud/__tests__/azure.js rename to x-pack/plugins/monitoring/server/cloud/azure.test.js index 499636b0fd28d..87a2630629903 100644 --- a/x-pack/plugins/monitoring/server/cloud/__tests__/azure.js +++ b/x-pack/plugins/monitoring/server/cloud/azure.test.js @@ -4,22 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { AZURE } from '../azure'; +import { AZURE } from './azure'; describe('Azure', () => { it('is named "azure"', () => { - expect(AZURE.getName()).to.eql('azure'); + expect(AZURE.getName()).toEqual('azure'); }); describe('_checkIfService', () => { it('handles expected response', async () => { const id = 'abcdef'; const request = (req, callback) => { - expect(req.method).to.eql('GET'); - expect(req.uri).to.eql('http://169.254.169.254/metadata/instance?api-version=2017-04-02'); - expect(req.headers.Metadata).to.eql('true'); - expect(req.json).to.eql(true); + expect(req.method).toEqual('GET'); + expect(req.uri).toEqual('http://169.254.169.254/metadata/instance?api-version=2017-04-02'); + expect(req.headers.Metadata).toEqual('true'); + expect(req.json).toEqual(true); const body = `{"compute":{"vmId": "${id}","location":"fakeus","availabilityZone":"fakeus-2"}}`; @@ -27,8 +26,8 @@ describe('Azure', () => { }; const response = await AZURE._checkIfService(request); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: AZURE.getName(), id, region: 'fakeus', @@ -50,7 +49,7 @@ describe('Azure', () => { expect().fail('Method should throw exception (Promise.reject)'); } catch (err) { - expect(err.message).to.eql(someError.message); + expect(err.message).toEqual(someError.message); } }); @@ -124,9 +123,9 @@ describe('Azure', () => { const response = AZURE._parseBody(body); - expect(response.getName()).to.eql(AZURE.getName()); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.getName()).toEqual(AZURE.getName()); + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: 'azure', id: 'd4c57456-2b3b-437a-9f1f-7082cf123456', vm_type: 'Standard_A1', @@ -176,9 +175,9 @@ describe('Azure', () => { const response = AZURE._parseBody(body); - expect(response.getName()).to.eql(AZURE.getName()); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.getName()).toEqual(AZURE.getName()); + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: 'azure', id: undefined, vm_type: undefined, @@ -191,10 +190,10 @@ describe('Azure', () => { }); it('ignores unexpected response body', () => { - expect(AZURE._parseBody(undefined)).to.be(null); - expect(AZURE._parseBody(null)).to.be(null); - expect(AZURE._parseBody({})).to.be(null); - expect(AZURE._parseBody({ privateIp: 'a.b.c.d' })).to.be(null); + expect(AZURE._parseBody(undefined)).toBe(null); + expect(AZURE._parseBody(null)).toBe(null); + expect(AZURE._parseBody({})).toBe(null); + expect(AZURE._parseBody({ privateIp: 'a.b.c.d' })).toBe(null); }); }); }); diff --git a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_detector.js b/x-pack/plugins/monitoring/server/cloud/cloud_detector.test.js similarity index 81% rename from x-pack/plugins/monitoring/server/cloud/__tests__/cloud_detector.js rename to x-pack/plugins/monitoring/server/cloud/cloud_detector.test.js index d0fc07af018cb..28b71991738d7 100644 --- a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_detector.js +++ b/x-pack/plugins/monitoring/server/cloud/cloud_detector.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { CloudDetector } from '../cloud_detector'; +import { CloudDetector } from './cloud_detector'; describe('CloudDetector', () => { const cloudService1 = { @@ -46,7 +45,7 @@ describe('CloudDetector', () => { it('returns undefined by default', () => { const detector = new CloudDetector(); - expect(detector.getCloudDetails()).to.be(undefined); + expect(detector.getCloudDetails()).toBe(undefined); }); }); @@ -54,9 +53,9 @@ describe('CloudDetector', () => { it('awaits _getCloudService', async () => { const detector = new CloudDetector({ cloudServices }); - expect(detector.getCloudDetails()).to.be(undefined); + expect(detector.getCloudDetails()).toBe(undefined); await detector.detectCloudService(); - expect(detector.getCloudDetails()).to.eql({ name: 'good-match' }); + expect(detector.getCloudDetails()).toEqual({ name: 'good-match' }); }); }); @@ -65,21 +64,21 @@ describe('CloudDetector', () => { const detector = new CloudDetector(); // note: should never use better-match - expect(await detector._getCloudService(cloudServices)).to.eql({ name: 'good-match' }); + expect(await detector._getCloudService(cloudServices)).toEqual({ name: 'good-match' }); }); it('returns undefined if none match', async () => { const detector = new CloudDetector(); - expect(await detector._getCloudService([cloudService1, cloudService2])).to.be(undefined); - expect(await detector._getCloudService([])).to.be(undefined); + expect(await detector._getCloudService([cloudService1, cloudService2])).toBe(undefined); + expect(await detector._getCloudService([])).toBe(undefined); }); // this is already tested above, but this just tests it explicitly it('ignores exceptions from cloud services', async () => { const detector = new CloudDetector(); - expect(await detector._getCloudService([cloudService2])).to.be(undefined); + expect(await detector._getCloudService([cloudService2])).toBe(undefined); }); }); }); diff --git a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_response.js b/x-pack/plugins/monitoring/server/cloud/cloud_response.test.js similarity index 71% rename from x-pack/plugins/monitoring/server/cloud/__tests__/cloud_response.js rename to x-pack/plugins/monitoring/server/cloud/cloud_response.test.js index ac05cbae479c1..226eca2708f01 100644 --- a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_response.js +++ b/x-pack/plugins/monitoring/server/cloud/cloud_response.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { CloudServiceResponse } from '../cloud_response'; +import { CloudServiceResponse } from './cloud_response'; describe('CloudServiceResponse', () => { const cloudName = 'my_cloud'; @@ -26,17 +25,17 @@ describe('CloudServiceResponse', () => { const unconfirmed = CloudServiceResponse.unconfirmed(cloudName); it('getName() matches constructor value', () => { - expect(confirmed.getName()).to.be(cloudName); - expect(unconfirmed.getName()).to.be(cloudName); + expect(confirmed.getName()).toBe(cloudName); + expect(unconfirmed.getName()).toBe(cloudName); }); it('isConfirmed() matches constructor value', () => { - expect(confirmed.isConfirmed()).to.be(true); - expect(unconfirmed.isConfirmed()).to.be(false); + expect(confirmed.isConfirmed()).toBe(true); + expect(unconfirmed.isConfirmed()).toBe(false); }); it('toJSON() should return object representing values', () => { - expect(confirmed.toJSON()).to.eql({ + expect(confirmed.toJSON()).toEqual({ name: cloudName, id, vm_type: vmType, @@ -47,6 +46,6 @@ describe('CloudServiceResponse', () => { }); it('toJSON() should throw an error when unconfirmed', () => { - expect(() => unconfirmed.toJSON()).to.throwException(`[${cloudName}] is not confirmed`); + expect(() => unconfirmed.toJSON()).toThrowError(`[${cloudName}] is not confirmed`); }); }); diff --git a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_service.js b/x-pack/plugins/monitoring/server/cloud/cloud_service.test.js similarity index 63% rename from x-pack/plugins/monitoring/server/cloud/__tests__/cloud_service.js rename to x-pack/plugins/monitoring/server/cloud/cloud_service.test.js index 1f6bda9833a01..40c320cc7ca9a 100644 --- a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_service.js +++ b/x-pack/plugins/monitoring/server/cloud/cloud_service.test.js @@ -4,17 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import { CloudService } from '../cloud_service'; -import { CloudServiceResponse } from '../cloud_response'; +import { CloudService } from './cloud_service'; +import { CloudServiceResponse } from './cloud_response'; describe('CloudService', () => { const service = new CloudService('xyz'); describe('getName', () => { it('is named by the constructor', () => { - expect(service.getName()).to.eql('xyz'); + expect(service.getName()).toEqual('xyz'); }); }); @@ -22,19 +20,19 @@ describe('CloudService', () => { it('is always unconfirmed', async () => { const response = await service.checkIfService(); - expect(response.getName()).to.eql('xyz'); - expect(response.isConfirmed()).to.be(false); + expect(response.getName()).toEqual('xyz'); + expect(response.isConfirmed()).toBe(false); }); }); describe('_checkIfService', () => { it('throws an exception unless overridden', async () => { - const request = sinon.stub(); + const request = jest.fn(); try { await service._checkIfService(request); } catch (err) { - expect(err.message).to.eql('not implemented'); + expect(err.message).toEqual('not implemented'); } }); }); @@ -43,31 +41,31 @@ describe('CloudService', () => { it('is always unconfirmed', () => { const response = service._createUnconfirmedResponse(); - expect(response.getName()).to.eql('xyz'); - expect(response.isConfirmed()).to.be(false); + expect(response.getName()).toEqual('xyz'); + expect(response.isConfirmed()).toBe(false); }); }); describe('_stringToJson', () => { it('only handles strings', () => { - expect(() => service._stringToJson({})).to.throwException(); - expect(() => service._stringToJson(123)).to.throwException(); - expect(() => service._stringToJson(true)).to.throwException(); + expect(() => service._stringToJson({})).toThrow(); + expect(() => service._stringToJson(123)).toThrow(); + expect(() => service._stringToJson(true)).toThrow(); }); it('fails with unexpected values', () => { // array - expect(() => service._stringToJson('[{}]')).to.throwException(); + expect(() => service._stringToJson('[{}]')).toThrow(); // normal values - expect(() => service._stringToJson('true')).to.throwException(); - expect(() => service._stringToJson('123')).to.throwException(); - expect(() => service._stringToJson('xyz')).to.throwException(); + expect(() => service._stringToJson('true')).toThrow(); + expect(() => service._stringToJson('123')).toThrow(); + expect(() => service._stringToJson('xyz')).toThrow(); // invalid JSON - expect(() => service._stringToJson('{"xyz"}')).to.throwException(); + expect(() => service._stringToJson('{"xyz"}')).toThrow(); // (single quotes are not actually valid in serialized JSON) - expect(() => service._stringToJson("{'a': 'xyz'}")).to.throwException(); - expect(() => service._stringToJson('{{}')).to.throwException(); - expect(() => service._stringToJson('{}}')).to.throwException(); + expect(() => service._stringToJson("{'a': 'xyz'}")).toThrow(); + expect(() => service._stringToJson('{{}')).toThrow(); + expect(() => service._stringToJson('{}}')).toThrow(); }); it('parses objects', () => { @@ -82,9 +80,9 @@ describe('CloudService', () => { etc: 'abc', }; - expect(service._stringToJson(' {} ')).to.eql({}); - expect(service._stringToJson('{ "a" : "key" }\n')).to.eql({ a: 'key' }); - expect(service._stringToJson(JSON.stringify(testObject))).to.eql(testObject); + expect(service._stringToJson(' {} ')).toEqual({}); + expect(service._stringToJson('{ "a" : "key" }\n')).toEqual({ a: 'key' }); + expect(service._stringToJson(JSON.stringify(testObject))).toEqual(testObject); }); }); @@ -114,7 +112,7 @@ describe('CloudService', () => { it('expects unusable bodies', async () => { const parseBody = (parsedBody) => { - expect(parsedBody).to.eql(body); + expect(parsedBody).toEqual(body); return null; }; @@ -126,14 +124,14 @@ describe('CloudService', () => { it('uses parsed object to create response', async () => { const serviceResponse = new CloudServiceResponse('a123', true, { id: 'xyz' }); const parseBody = (parsedBody) => { - expect(parsedBody).to.eql(body); + expect(parsedBody).toEqual(body); return serviceResponse; }; const response = await service._parseResponse(body, parseBody); - expect(response).to.be(serviceResponse); + expect(response).toBe(serviceResponse); }); }); }); diff --git a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_services.js b/x-pack/plugins/monitoring/server/cloud/cloud_services.test.js similarity index 65% rename from x-pack/plugins/monitoring/server/cloud/__tests__/cloud_services.js rename to x-pack/plugins/monitoring/server/cloud/cloud_services.test.js index 37ad67586001f..c62535a593a97 100644 --- a/x-pack/plugins/monitoring/server/cloud/__tests__/cloud_services.js +++ b/x-pack/plugins/monitoring/server/cloud/cloud_services.test.js @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { CLOUD_SERVICES } from '../cloud_services'; -import { AWS } from '../aws'; -import { AZURE } from '../azure'; -import { GCP } from '../gcp'; +import { CLOUD_SERVICES } from './cloud_services'; +import { AWS } from './aws'; +import { AZURE } from './azure'; +import { GCP } from './gcp'; describe('cloudServices', () => { const expectedOrder = [AWS, GCP, AZURE]; @@ -16,7 +15,7 @@ describe('cloudServices', () => { it('iterates in expected order', () => { let i = 0; for (const service of CLOUD_SERVICES) { - expect(service).to.be(expectedOrder[i++]); + expect(service).toBe(expectedOrder[i++]); } }); }); diff --git a/x-pack/plugins/monitoring/server/cloud/__tests__/gcp.js b/x-pack/plugins/monitoring/server/cloud/gcp.test.js similarity index 79% rename from x-pack/plugins/monitoring/server/cloud/__tests__/gcp.js rename to x-pack/plugins/monitoring/server/cloud/gcp.test.js index e990f3b09f69b..919bc8c49f216 100644 --- a/x-pack/plugins/monitoring/server/cloud/__tests__/gcp.js +++ b/x-pack/plugins/monitoring/server/cloud/gcp.test.js @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { GCP } from '../gcp'; +import { GCP } from './gcp'; describe('GCP', () => { it('is named "gcp"', () => { - expect(GCP.getName()).to.eql('gcp'); + expect(GCP.getName()).toEqual('gcp'); }); describe('_checkIfService', () => { @@ -25,10 +24,10 @@ describe('GCP', () => { const request = (req, callback) => { const basePath = 'http://169.254.169.254/computeMetadata/v1/instance/'; - expect(req.method).to.eql('GET'); - expect(req.uri.startsWith(basePath)).to.be(true); - expect(req.headers['Metadata-Flavor']).to.eql('Google'); - expect(req.json).to.eql(false); + expect(req.method).toEqual('GET'); + expect(req.uri.startsWith(basePath)).toBe(true); + expect(req.headers['Metadata-Flavor']).toEqual('Google'); + expect(req.json).toEqual(false); const requestKey = req.uri.substring(basePath.length); let body = null; @@ -43,8 +42,8 @@ describe('GCP', () => { }; const response = await GCP._checkIfService(request); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: GCP.getName(), id: metadata.id, region: 'us-fake4', @@ -91,7 +90,7 @@ describe('GCP', () => { expect().fail('Method should throw exception (Promise.reject)'); } catch (err) { - expect(err.message).to.eql(someError.message); + expect(err.message).toEqual(someError.message); } }); @@ -126,15 +125,15 @@ describe('GCP', () => { describe('_extractValue', () => { it('only handles strings', () => { - expect(GCP._extractValue()).to.be(undefined); - expect(GCP._extractValue(null, null)).to.be(undefined); - expect(GCP._extractValue('abc', { field: 'abcxyz' })).to.be(undefined); - expect(GCP._extractValue('abc', 1234)).to.be(undefined); - expect(GCP._extractValue('abc/', 'abc/xyz')).to.eql('xyz'); + expect(GCP._extractValue()).toBe(undefined); + expect(GCP._extractValue(null, null)).toBe(undefined); + expect(GCP._extractValue('abc', { field: 'abcxyz' })).toBe(undefined); + expect(GCP._extractValue('abc', 1234)).toBe(undefined); + expect(GCP._extractValue('abc/', 'abc/xyz')).toEqual('xyz'); }); it('uses the last index of the prefix to truncate', () => { - expect(GCP._extractValue('abc/', ' \n 123/abc/xyz\t \n')).to.eql('xyz'); + expect(GCP._extractValue('abc/', ' \n 123/abc/xyz\t \n')).toEqual('xyz'); }); }); @@ -146,9 +145,9 @@ describe('GCP', () => { const response = GCP._combineResponses(id, machineType, zone); - expect(response.getName()).to.eql(GCP.getName()); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.getName()).toEqual(GCP.getName()); + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: 'gcp', id: '5702733457649812345', vm_type: 'f1-micro', @@ -166,9 +165,9 @@ describe('GCP', () => { const response = GCP._combineResponses(id, machineType, zone); - expect(response.getName()).to.eql(GCP.getName()); - expect(response.isConfirmed()).to.eql(true); - expect(response.toJSON()).to.eql({ + expect(response.getName()).toEqual(GCP.getName()); + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toEqual({ name: 'gcp', id: '5702733457649812345', vm_type: undefined, @@ -179,13 +178,13 @@ describe('GCP', () => { }); it('ignores unexpected response body', () => { - expect(() => GCP._combineResponses()).to.throwException(); - expect(() => GCP._combineResponses(undefined, undefined, undefined)).to.throwException(); - expect(() => GCP._combineResponses(null, null, null)).to.throwException(); + expect(() => GCP._combineResponses()).toThrow(); + expect(() => GCP._combineResponses(undefined, undefined, undefined)).toThrow(); + expect(() => GCP._combineResponses(null, null, null)).toThrow(); expect(() => GCP._combineResponses({ id: 'x' }, { machineType: 'a' }, { zone: 'b' }) - ).to.throwException(); - expect(() => GCP._combineResponses({ privateIp: 'a.b.c.d' })).to.throwException(); + ).toThrow(); + expect(() => GCP._combineResponses({ privateIp: 'a.b.c.d' })).toThrow(); }); }); }); diff --git a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/fixtures/create_stubs.js b/x-pack/plugins/monitoring/server/cluster_alerts/__fixtures__/create_stubs.js similarity index 100% rename from x-pack/plugins/monitoring/server/cluster_alerts/__tests__/fixtures/create_stubs.js rename to x-pack/plugins/monitoring/server/cluster_alerts/__fixtures__/create_stubs.js diff --git a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/alerts_cluster_search.js b/x-pack/plugins/monitoring/server/cluster_alerts/alerts_cluster_search.test.js similarity index 96% rename from x-pack/plugins/monitoring/server/cluster_alerts/__tests__/alerts_cluster_search.js rename to x-pack/plugins/monitoring/server/cluster_alerts/alerts_cluster_search.test.js index 3c0460db71791..a76ad3ccecb4b 100644 --- a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/alerts_cluster_search.js +++ b/x-pack/plugins/monitoring/server/cluster_alerts/alerts_cluster_search.test.js @@ -6,8 +6,8 @@ import expect from '@kbn/expect'; import sinon from 'sinon'; -import { createStubs } from './fixtures/create_stubs'; -import { alertsClusterSearch } from '../alerts_cluster_search'; +import { createStubs } from './__fixtures__/create_stubs'; +import { alertsClusterSearch } from './alerts_cluster_search'; const mockAlerts = [ { @@ -44,7 +44,8 @@ const mockQueryResult = { }, }; -describe('Alerts Cluster Search', () => { +// TODO: tests were not running and are not up to date. +describe.skip('Alerts Cluster Search', () => { describe('License checks pass', () => { const featureStub = sinon.stub().returns({ getLicenseCheckResults: () => ({ clusterAlerts: { enabled: true } }), diff --git a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/alerts_clusters_aggregation.js b/x-pack/plugins/monitoring/server/cluster_alerts/alerts_clusters_aggregation.test.js similarity index 96% rename from x-pack/plugins/monitoring/server/cluster_alerts/__tests__/alerts_clusters_aggregation.js rename to x-pack/plugins/monitoring/server/cluster_alerts/alerts_clusters_aggregation.test.js index a64ff2e2b6080..8e75e1b7e6466 100644 --- a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/alerts_clusters_aggregation.js +++ b/x-pack/plugins/monitoring/server/cluster_alerts/alerts_clusters_aggregation.test.js @@ -7,8 +7,8 @@ import expect from '@kbn/expect'; import sinon from 'sinon'; import { merge } from 'lodash'; -import { createStubs } from './fixtures/create_stubs'; -import { alertsClustersAggregation } from '../alerts_clusters_aggregation'; +import { createStubs } from './__fixtures__/create_stubs'; +import { alertsClustersAggregation } from './alerts_clusters_aggregation'; const clusters = [ { @@ -64,7 +64,8 @@ const mockQueryResult = { }, }; -describe('Alerts Clusters Aggregation', () => { +// TODO: tests were not running and are not up to date. +describe.skip('Alerts Clusters Aggregation', () => { describe('with alerts enabled', () => { const featureStub = sinon.stub().returns({ getLicenseCheckResults: () => ({ clusterAlerts: { enabled: true } }), diff --git a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/check_license.js b/x-pack/plugins/monitoring/server/cluster_alerts/check_license.test.js similarity index 98% rename from x-pack/plugins/monitoring/server/cluster_alerts/__tests__/check_license.js rename to x-pack/plugins/monitoring/server/cluster_alerts/check_license.test.js index e0528209c50ae..ee45e6e89f1e6 100644 --- a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/check_license.js +++ b/x-pack/plugins/monitoring/server/cluster_alerts/check_license.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { checkLicense, checkLicenseGenerator } from '../check_license'; +import { checkLicense, checkLicenseGenerator } from './check_license'; import expect from '@kbn/expect'; import sinon from 'sinon'; diff --git a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/verify_monitoring_license.js b/x-pack/plugins/monitoring/server/cluster_alerts/verify_monitoring_license.test.js similarity index 95% rename from x-pack/plugins/monitoring/server/cluster_alerts/__tests__/verify_monitoring_license.js rename to x-pack/plugins/monitoring/server/cluster_alerts/verify_monitoring_license.test.js index 08385e8d96a80..8302d8022269b 100644 --- a/x-pack/plugins/monitoring/server/cluster_alerts/__tests__/verify_monitoring_license.js +++ b/x-pack/plugins/monitoring/server/cluster_alerts/verify_monitoring_license.test.js @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { verifyMonitoringLicense } from '../verify_monitoring_license'; +import { verifyMonitoringLicense } from './verify_monitoring_license'; import expect from '@kbn/expect'; import sinon from 'sinon'; -describe('Monitoring Verify License', () => { +// TODO: tests were not running and are not up to date. +describe.skip('Monitoring Verify License', () => { describe('Disabled by Configuration', () => { const get = sinon.stub().withArgs('xpack.monitoring.cluster_alerts.enabled').returns(false); const server = { config: sinon.stub().returns({ get }) }; diff --git a/x-pack/plugins/monitoring/server/__tests__/deprecations.js b/x-pack/plugins/monitoring/server/deprecations.test.js similarity index 77% rename from x-pack/plugins/monitoring/server/__tests__/deprecations.js rename to x-pack/plugins/monitoring/server/deprecations.test.js index 42621ea3549b4..1b5b66678cb63 100644 --- a/x-pack/plugins/monitoring/server/__tests__/deprecations.js +++ b/x-pack/plugins/monitoring/server/deprecations.test.js @@ -5,17 +5,16 @@ */ import { noop } from 'lodash'; -import expect from '@kbn/expect'; -import { deprecations as deprecationsModule } from '../deprecations'; -import sinon from 'sinon'; +import { deprecations as deprecationsModule } from './deprecations'; describe('monitoring plugin deprecations', function () { let transformDeprecations; - const rename = sinon.stub().returns(() => {}); + const rename = jest.fn(() => jest.fn()); + const renameFromRoot = jest.fn(() => jest.fn()); const fromPath = 'monitoring'; - before(function () { - const deprecations = deprecationsModule({ rename }); + beforeAll(function () { + const deprecations = deprecationsModule({ rename, renameFromRoot }); transformDeprecations = (settings, fromPath, log = noop) => { deprecations.forEach((deprecation) => deprecation(settings, fromPath, log)); }; @@ -31,9 +30,9 @@ describe('monitoring plugin deprecations', function () { }, }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(false); + expect(log).not.toHaveBeenCalled(); }); it(`shouldn't log when cluster alerts are disabled`, function () { @@ -46,9 +45,9 @@ describe('monitoring plugin deprecations', function () { }, }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(false); + expect(log).not.toHaveBeenCalled(); }); it(`shouldn't log when email_address is specified`, function () { @@ -62,9 +61,9 @@ describe('monitoring plugin deprecations', function () { }, }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(false); + expect(log).not.toHaveBeenCalled(); }); it(`should log when email_address is missing, but alerts/notifications are both enabled`, function () { @@ -77,9 +76,9 @@ describe('monitoring plugin deprecations', function () { }, }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(true); + expect(log).toHaveBeenCalled(); }); }); @@ -87,66 +86,66 @@ describe('monitoring plugin deprecations', function () { it('logs a warning if elasticsearch.username is set to "elastic"', () => { const settings = { elasticsearch: { username: 'elastic' } }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(true); + expect(log).toHaveBeenCalled(); }); it('logs a warning if elasticsearch.username is set to "kibana"', () => { const settings = { elasticsearch: { username: 'kibana' } }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(true); + expect(log).toHaveBeenCalled(); }); it('does not log a warning if elasticsearch.username is set to something besides "elastic" or "kibana"', () => { const settings = { elasticsearch: { username: 'otheruser' } }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(false); + expect(log).not.toHaveBeenCalled(); }); it('does not log a warning if elasticsearch.username is unset', () => { const settings = { elasticsearch: { username: undefined } }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(false); + expect(log).not.toHaveBeenCalled(); }); it('logs a warning if ssl.key is set and ssl.certificate is not', () => { const settings = { elasticsearch: { ssl: { key: '' } } }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(true); + expect(log).toHaveBeenCalled(); }); it('logs a warning if ssl.certificate is set and ssl.key is not', () => { const settings = { elasticsearch: { ssl: { certificate: '' } } }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(true); + expect(log).toHaveBeenCalled(); }); it('does not log a warning if both ssl.key and ssl.certificate are set', () => { const settings = { elasticsearch: { ssl: { key: '', certificate: '' } } }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(log.called).to.be(false); + expect(log).not.toHaveBeenCalled(); }); }); describe('xpack_api_polling_frequency_millis', () => { it('should call rename for this renamed config key', () => { const settings = { xpack_api_polling_frequency_millis: 30000 }; - const log = sinon.spy(); + const log = jest.fn(); transformDeprecations(settings, fromPath, log); - expect(rename.called).to.be(true); + expect(rename).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/monitoring/server/es_client/__tests__/instantiate_client.js b/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js similarity index 61% rename from x-pack/plugins/monitoring/server/es_client/__tests__/instantiate_client.js rename to x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js index a18b7cc8b79f3..8f73240914c0f 100644 --- a/x-pack/plugins/monitoring/server/es_client/__tests__/instantiate_client.js +++ b/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js @@ -4,9 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import { instantiateClient, hasMonitoringCluster } from '../instantiate_client'; +import { instantiateClient, hasMonitoringCluster } from './instantiate_client'; const server = { monitoring: { @@ -39,26 +37,26 @@ const serverWithUrl = { }, }; -const createClient = sinon.stub(); -const log = { info: sinon.stub() }; +const createClient = jest.fn(); +const log = { info: jest.fn() }; describe('Instantiate Client', () => { afterEach(() => { - createClient.resetHistory(); - log.info.resetHistory(); + createClient.mockReset(); + log.info.mockReset(); }); describe('Logging', () => { it('logs that the config was sourced from the production options', () => { instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); - expect(log.info.getCall(0).args).to.eql(['config sourced from: production cluster']); + expect(log.info.mock.calls[0]).toEqual(['config sourced from: production cluster']); }); it('logs that the config was sourced from the monitoring options', () => { instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); - expect(log.info.getCall(0).args).to.eql(['config sourced from: monitoring cluster']); + expect(log.info.mock.calls[0]).toEqual(['config sourced from: monitoring cluster']); }); }); @@ -66,21 +64,20 @@ describe('Instantiate Client', () => { it('Does not add xpack.monitoring.elasticsearch.customHeaders if connected to production cluster', () => { instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); - const createClusterCall = createClient.getCall(0); - - sinon.assert.calledOnce(createClient); - expect(createClusterCall.args[0]).to.be('monitoring'); - expect(createClusterCall.args[1].customHeaders).to.eql(undefined); + const createClusterCall = createClient.mock.calls[0]; + expect(createClient).toHaveBeenCalledTimes(1); + expect(createClusterCall[0]).toBe('monitoring'); + expect(createClusterCall[1].customHeaders).toEqual(undefined); }); it('Adds xpack.monitoring.elasticsearch.customHeaders if connected to monitoring cluster', () => { instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); - const createClusterCall = createClient.getCall(0); + const createClusterCall = createClient.mock.calls[0]; - sinon.assert.calledOnce(createClient); - expect(createClusterCall.args[0]).to.be('monitoring'); - expect(createClusterCall.args[1].customHeaders).to.eql({ + expect(createClient).toHaveBeenCalledTimes(1); + expect(createClusterCall[0]).toBe('monitoring'); + expect(createClusterCall[1].customHeaders).toEqual({ 'x-custom-headers-test': 'connection-monitoring', }); }); @@ -90,36 +87,36 @@ describe('Instantiate Client', () => { it('exposes an authenticated client using production host settings', () => { instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); - const createClusterCall = createClient.getCall(0); - const createClientOptions = createClusterCall.args[1]; + const createClusterCall = createClient.mock.calls[0]; + const createClientOptions = createClusterCall[1]; - sinon.assert.calledOnce(createClient); - expect(createClusterCall.args[0]).to.be('monitoring'); - expect(createClientOptions.hosts).to.eql(undefined); + expect(createClient).toHaveBeenCalledTimes(1); + expect(createClusterCall[0]).toBe('monitoring'); + expect(createClientOptions.hosts).toEqual(undefined); }); }); describe('Use a connection to monitoring cluster', () => { it('exposes an authenticated client using monitoring host settings', () => { instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); - const createClusterCall = createClient.getCall(0); - const createClientOptions = createClusterCall.args[1]; - - sinon.assert.calledOnce(createClient); - expect(createClusterCall.args[0]).to.be('monitoring'); - expect(createClientOptions.hosts[0]).to.eql('http://monitoring-cluster.test:9200'); - expect(createClientOptions.username).to.eql('monitoring-user-internal-test'); - expect(createClientOptions.password).to.eql('monitoring-p@ssw0rd!-internal-test'); + const createClusterCall = createClient.mock.calls[0]; + const createClientOptions = createClusterCall[1]; + + expect(createClient).toHaveBeenCalledTimes(1); + expect(createClusterCall[0]).toBe('monitoring'); + expect(createClientOptions.hosts[0]).toEqual('http://monitoring-cluster.test:9200'); + expect(createClientOptions.username).toEqual('monitoring-user-internal-test'); + expect(createClientOptions.password).toEqual('monitoring-p@ssw0rd!-internal-test'); }); }); describe('hasMonitoringCluster', () => { it('returns true if monitoring is configured', () => { - expect(hasMonitoringCluster(serverWithUrl.monitoring.ui.elasticsearch)).to.be(true); + expect(hasMonitoringCluster(serverWithUrl.monitoring.ui.elasticsearch)).toBe(true); }); it('returns false if monitoring is not configured', () => { - expect(hasMonitoringCluster(server.monitoring.ui.elasticsearch)).to.be(false); + expect(hasMonitoringCluster(server.monitoring.ui.elasticsearch)).toBe(false); }); }); }); diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/__tests__/bulk_uploader.js b/x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.test.js similarity index 98% rename from x-pack/plugins/monitoring/server/kibana_monitoring/__tests__/bulk_uploader.js rename to x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.test.js index 1aa9e49bc5f35..154845681031e 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/__tests__/bulk_uploader.js +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.test.js @@ -7,7 +7,7 @@ import { noop } from 'lodash'; import sinon from 'sinon'; import expect from '@kbn/expect'; -import { BulkUploader } from '../bulk_uploader'; +import { BulkUploader } from './bulk_uploader'; const FETCH_INTERVAL = 300; const CHECK_DELAY = 500; @@ -39,7 +39,9 @@ class MockCollectorSet { } } -describe('BulkUploader', () => { +// TODO: Those tests were not running and they are not up to . +// They need to be migrated +describe.skip('BulkUploader', () => { describe('registers a collector set and runs lifecycle events', () => { let server; beforeEach(() => { diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/__tests__/check_for_email_value.js b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/check_for_email_value.test.js similarity index 53% rename from x-pack/plugins/monitoring/server/kibana_monitoring/collectors/__tests__/check_for_email_value.js rename to x-pack/plugins/monitoring/server/kibana_monitoring/collectors/check_for_email_value.test.js index e74c5f9419daf..7b30ccde5bf47 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/__tests__/check_for_email_value.js +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/check_for_email_value.test.js @@ -4,67 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { checkForEmailValue } from '../get_settings_collector'; +import { checkForEmailValue } from './get_settings_collector'; describe('getSettingsCollector / checkForEmailValue', () => { - const mockLogger = { - warn: () => {}, - }; - it('ignores shouldUseNull=true value and returns email if email value if one is set', async () => { const shouldUseNull = true; const getDefaultAdminEmailMock = () => 'test@elastic.co'; - expect( - await checkForEmailValue( - undefined, - undefined, - mockLogger, - shouldUseNull, - getDefaultAdminEmailMock - ) - ).to.be('test@elastic.co'); + expect(await checkForEmailValue(undefined, shouldUseNull, getDefaultAdminEmailMock)).toBe( + 'test@elastic.co' + ); }); it('ignores shouldUseNull=false value and returns email if email value if one is set', async () => { const shouldUseNull = false; const getDefaultAdminEmailMock = () => 'test@elastic.co'; - expect( - await checkForEmailValue( - undefined, - undefined, - mockLogger, - shouldUseNull, - getDefaultAdminEmailMock - ) - ).to.be('test@elastic.co'); + expect(await checkForEmailValue(undefined, shouldUseNull, getDefaultAdminEmailMock)).toBe( + 'test@elastic.co' + ); }); it('returns a null if no email value is set and null is allowed', async () => { const shouldUseNull = true; const getDefaultAdminEmailMock = () => null; - expect( - await checkForEmailValue( - undefined, - undefined, - mockLogger, - shouldUseNull, - getDefaultAdminEmailMock - ) - ).to.be(null); + expect(await checkForEmailValue(undefined, shouldUseNull, getDefaultAdminEmailMock)).toBe(null); }); it('returns undefined if no email value is set and null is not allowed', async () => { const shouldUseNull = false; const getDefaultAdminEmailMock = () => null; - expect( - await checkForEmailValue( - undefined, - undefined, - mockLogger, - shouldUseNull, - getDefaultAdminEmailMock - ) - ).to.be(undefined); + expect(await checkForEmailValue(undefined, shouldUseNull, getDefaultAdminEmailMock)).toBe( + undefined + ); }); }); diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/__tests__/get_default_admin_email.js b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_default_admin_email.test.js similarity index 53% rename from x-pack/plugins/monitoring/server/kibana_monitoring/collectors/__tests__/get_default_admin_email.js rename to x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_default_admin_email.test.js index 10f52a82a830c..5020dee8c548f 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/__tests__/get_default_admin_email.js +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_default_admin_email.test.js @@ -4,49 +4,46 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import sinon from 'sinon'; - -import { getDefaultAdminEmail } from '../get_settings_collector'; -import { CLUSTER_ALERTS_ADDRESS_CONFIG_KEY } from '../../../../common/constants'; +import { getDefaultAdminEmail } from './get_settings_collector'; describe('getSettingsCollector / getDefaultAdminEmail', () => { function setup({ enabled = true, adminEmail = null } = {}) { - const config = { get: sinon.stub() }; - - config.get.withArgs('monitoring.cluster_alerts.email_notifications.enabled').returns(enabled); - - if (adminEmail) { - config.get.withArgs(`monitoring.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}`).returns(adminEmail); - } - - config.get.withArgs('kibana.index').returns('.kibana'); - - config.get.withArgs('pkg.version').returns('1.1.1'); - - return config; + return { + cluster_alerts: { + email_notifications: { + email_address: adminEmail, + enabled: enabled, + }, + }, + kibana: { + index: '.kibana', + }, + pkg: { + version: '1.1.1', + }, + }; } describe('monitoring.cluster_alerts.email_notifications.enabled = false', () => { it('returns null when email is defined', async () => { const config = setup({ enabled: false }); - expect(await getDefaultAdminEmail(config)).to.be(null); + expect(await getDefaultAdminEmail(config)).toBe(null); }); it('returns null when email is undefined', async () => { const config = setup({ enabled: false }); - expect(await getDefaultAdminEmail(config)).to.be(null); + expect(await getDefaultAdminEmail(config)).toBe(null); }); }); describe('monitoring.cluster_alerts.email_notifications.enabled = true', () => { it('returns value when email is defined', async () => { const config = setup({ adminEmail: 'hello@world' }); - expect(await getDefaultAdminEmail(config)).to.be('hello@world'); + expect(await getDefaultAdminEmail(config)).toBe('hello@world'); }); it('returns null when email is undefined', async () => { const config = setup(); - expect(await getDefaultAdminEmail(config)).to.be(null); + expect(await getDefaultAdminEmail(config)).toBe(null); }); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/apm/__tests__/get_apms.js b/x-pack/plugins/monitoring/server/lib/apm/get_apms.test.js similarity index 57% rename from x-pack/plugins/monitoring/server/lib/apm/__tests__/get_apms.js rename to x-pack/plugins/monitoring/server/lib/apm/get_apms.test.js index 2e45ff600abcf..beaa761c8033e 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/__tests__/get_apms.js +++ b/x-pack/plugins/monitoring/server/lib/apm/get_apms.test.js @@ -4,15 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { defaultResponseSort } from '../../__tests__/helpers'; -import { handleResponse } from '../get_apms'; -import expect from '@kbn/expect'; +import { defaultResponseSort } from '../helpers'; +import { handleResponse } from './get_apms'; describe('apm/get_apms', () => { it('Timestamp is desc', () => { const { beats, version } = defaultResponseSort(handleResponse); - expect(beats[0].version).to.eql(version[0]); - expect(beats[1].version).to.eql(version[1]); - expect(beats[2].version).to.eql(version[2]); + expect(beats[0].version).toEqual(version[0]); + expect(beats[1].version).toEqual(version[1]); + expect(beats[2].version).toEqual(version[2]); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/beats/__tests__/fixtures/get_listing_response.js b/x-pack/plugins/monitoring/server/lib/beats/__fixtures__/get_listing_response.js similarity index 100% rename from x-pack/plugins/monitoring/server/lib/beats/__tests__/fixtures/get_listing_response.js rename to x-pack/plugins/monitoring/server/lib/beats/__fixtures__/get_listing_response.js diff --git a/x-pack/plugins/monitoring/server/lib/beats/__tests__/create_beats_query.js b/x-pack/plugins/monitoring/server/lib/beats/create_beats_query.test.js similarity index 72% rename from x-pack/plugins/monitoring/server/lib/beats/__tests__/create_beats_query.js rename to x-pack/plugins/monitoring/server/lib/beats/create_beats_query.test.js index b26720d0c1031..06cd64f0b3a42 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/__tests__/create_beats_query.js +++ b/x-pack/plugins/monitoring/server/lib/beats/create_beats_query.test.js @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { createBeatsQuery } from '../create_beats_query'; +import { createBeatsQuery } from './create_beats_query'; -describe('createBeatsQuery', () => { +// TODO: tests were not running and are not up to date +describe.skip('createBeatsQuery', () => { const noApmFilter = { bool: { must_not: { @@ -22,10 +22,10 @@ describe('createBeatsQuery', () => { const query1 = createBeatsQuery(); const query2 = createBeatsQuery({}); - expect(query1.bool.filter[0]).to.eql({ term: { type: 'beats_stats' } }); - expect(query1.bool.filter[query1.bool.filter.length - 1]).to.eql(noApmFilter); - expect(query2.bool.filter[0]).to.eql({ term: { type: 'beats_stats' } }); - expect(query2.bool.filter[query2.bool.filter.length - 1]).to.eql(noApmFilter); + expect(query1.bool.filter[0]).toEqual({ term: { type: 'beats_stats' } }); + expect(query1.bool.filter[query1.bool.filter.length - 1]).toEqual(noApmFilter); + expect(query2.bool.filter[0]).toEqual({ term: { type: 'beats_stats' } }); + expect(query2.bool.filter[query2.bool.filter.length - 1]).toEqual(noApmFilter); }); it('adds filters with other filters', () => { @@ -40,14 +40,14 @@ describe('createBeatsQuery', () => { const queryFilters = query.bool.filter; const filterCount = queryFilters.length; - expect(queryFilters[0]).to.eql({ term: { type: 'beats_stats' } }); + expect(queryFilters[0]).toEqual({ term: { type: 'beats_stats' } }); filters.forEach((filter, index) => { // "custom" filters are added at the end of all known filters, and the last "custom" filter is the noApmFilter - expect(queryFilters[filterCount - (filters.length - index)]).to.eql(filter); + expect(queryFilters[filterCount - (filters.length - index)]).toEqual(filter); }); - expect(queryFilters[filterCount - 1]).to.eql(noApmFilter); + expect(queryFilters[filterCount - 1]).toEqual(noApmFilter); }); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beat_summary.js b/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.test.js similarity index 95% rename from x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beat_summary.js rename to x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.test.js index ec8ce478b152f..de1a9b2e3a5c6 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beat_summary.js +++ b/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.test.js @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { handleResponse } from '../get_beat_summary'; -import expect from '@kbn/expect'; +import { handleResponse } from './get_beat_summary'; describe('get_beat_summary', () => { - it('Handles empty aggregation', () => { + // TODO: test was not running before and is not up to date + it.skip('Handles empty aggregation', () => { const response = {}; const beatUuid = 'fooUuid'; - expect(handleResponse(response, beatUuid)).to.eql({ + expect(handleResponse(response, beatUuid)).toEqual({ uuid: 'fooUuid', transportAddress: null, version: null, @@ -114,7 +114,7 @@ describe('get_beat_summary', () => { }; const beatUuid = 'fooUuid'; - expect(handleResponse(response, beatUuid)).to.eql({ + expect(handleResponse(response, beatUuid)).toEqual({ uuid: 'fooUuid', transportAddress: 'beat-summary.test', version: '6.2.0', @@ -216,7 +216,7 @@ describe('get_beat_summary', () => { }; const beatUuid = 'fooUuid'; - expect(handleResponse(response, beatUuid)).to.eql({ + expect(handleResponse(response, beatUuid)).toEqual({ uuid: 'fooUuid', transportAddress: 'beat-summary.test', version: '6.2.0', diff --git a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beats.js b/x-pack/plugins/monitoring/server/lib/beats/get_beats.test.js similarity index 66% rename from x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beats.js rename to x-pack/plugins/monitoring/server/lib/beats/get_beats.test.js index 2b52e4d54a95f..23ae88d6a1c2c 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beats.js +++ b/x-pack/plugins/monitoring/server/lib/beats/get_beats.test.js @@ -4,17 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { response, defaultResponseSort } from '../../__tests__/helpers'; -import { handleResponse } from '../get_beats'; -import expect from '@kbn/expect'; +import { response, defaultResponseSort } from '../helpers'; +import { handleResponse } from './get_beats'; describe('beats/get_beats', () => { - it('Handles empty response', () => { - expect(handleResponse()).to.eql([]); + // TODO: test was not running and is not up to date + it.skip('Handles empty response', () => { + expect(handleResponse()).toEqual([]); }); it('Maps hits into a listing', () => { - expect(handleResponse(response, 1515534342000, 1515541592880)).to.eql([ + expect(handleResponse(response, 1515534342000, 1515541592880)).toEqual([ { bytes_sent_rate: 18.756344057548876, errors: 7, @@ -31,8 +31,8 @@ describe('beats/get_beats', () => { it('Timestamp is desc', () => { const { beats, version } = defaultResponseSort(handleResponse); - expect(beats[0].version).to.eql(version[0]); - expect(beats[1].version).to.eql(version[1]); - expect(beats[2].version).to.eql(version[2]); + expect(beats[0].version).toEqual(version[0]); + expect(beats[1].version).toEqual(version[1]); + expect(beats[2].version).toEqual(version[2]); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beats_for_clusters.js b/x-pack/plugins/monitoring/server/lib/beats/get_beats_for_clusters.test.js similarity index 87% rename from x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beats_for_clusters.js rename to x-pack/plugins/monitoring/server/lib/beats/get_beats_for_clusters.test.js index e70553672080f..3bdc66fa41f1d 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_beats_for_clusters.js +++ b/x-pack/plugins/monitoring/server/lib/beats/get_beats_for_clusters.test.js @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { handleResponse } from '../get_beats_for_clusters'; -import expect from '@kbn/expect'; +import { handleResponse } from './get_beats_for_clusters'; describe('get_beats_for_clusters', () => { it('Handles empty aggregation', () => { const clusterUuid = 'foo_uuid'; const response = {}; - expect(handleResponse(clusterUuid, response)).to.eql({ + expect(handleResponse(clusterUuid, response)).toEqual({ clusterUuid: 'foo_uuid', stats: { totalEvents: null, @@ -43,7 +42,7 @@ describe('get_beats_for_clusters', () => { max_bytes_sent_total: { value: 333476 }, }, }; - expect(handleResponse(clusterUuid, response)).to.eql({ + expect(handleResponse(clusterUuid, response)).toEqual({ clusterUuid: 'foo_uuid', stats: { totalEvents: 6500000, diff --git a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_latest_stats.js b/x-pack/plugins/monitoring/server/lib/beats/get_latest_stats.test.js similarity index 90% rename from x-pack/plugins/monitoring/server/lib/beats/__tests__/get_latest_stats.js rename to x-pack/plugins/monitoring/server/lib/beats/get_latest_stats.test.js index 1a345b05387b9..14c28e1213ffc 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_latest_stats.js +++ b/x-pack/plugins/monitoring/server/lib/beats/get_latest_stats.test.js @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { handleResponse } from '../get_latest_stats'; -import expect from '@kbn/expect'; +import { handleResponse } from './get_latest_stats'; describe('beats/get_latest_stats', () => { it('Handle empty response', () => { - expect(handleResponse()).to.eql({ + expect(handleResponse()).toEqual({ latestActive: [ { range: 'last1m', @@ -52,7 +51,7 @@ describe('beats/get_latest_stats', () => { }, }; - expect(handleResponse(response)).to.eql({ + expect(handleResponse(response)).toEqual({ latestActive: [ { range: 'last1m', count: 10 }, { range: 'last5m', count: 11 }, diff --git a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_stats.js b/x-pack/plugins/monitoring/server/lib/beats/get_stats.test.js similarity index 90% rename from x-pack/plugins/monitoring/server/lib/beats/__tests__/get_stats.js rename to x-pack/plugins/monitoring/server/lib/beats/get_stats.test.js index 597ea64bf9dcf..a825928b7d6e9 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/__tests__/get_stats.js +++ b/x-pack/plugins/monitoring/server/lib/beats/get_stats.test.js @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { handleResponse } from '../get_stats'; -import expect from '@kbn/expect'; +import { handleResponse } from './get_stats'; describe('beats/get_stats', () => { it('Handle empty response', () => { - expect(handleResponse()).to.eql({ + expect(handleResponse()).toEqual({ stats: { bytesSent: null, totalEvents: null, @@ -36,7 +35,7 @@ describe('beats/get_stats', () => { }, }; - expect(handleResponse(response)).to.eql({ + expect(handleResponse(response)).toEqual({ stats: { bytesSent: 40000, totalEvents: 6500000, @@ -66,7 +65,7 @@ describe('beats/get_stats', () => { }, }; - expect(handleResponse(response)).to.eql({ + expect(handleResponse(response)).toEqual({ stats: { bytesSent: null, totalEvents: null, diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_auto.js b/x-pack/plugins/monitoring/server/lib/calculate_auto.test.js similarity index 82% rename from x-pack/plugins/monitoring/server/lib/__tests__/calculate_auto.js rename to x-pack/plugins/monitoring/server/lib/calculate_auto.test.js index 518da3deb1edb..f1bb142e895b1 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_auto.js +++ b/x-pack/plugins/monitoring/server/lib/calculate_auto.test.js @@ -4,15 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { calculateAuto } from '../calculate_auto.js'; -import expect from '@kbn/expect'; +import { calculateAuto } from './calculate_auto.js'; import _ from 'lodash'; import moment from 'moment'; describe('Calculating Time Intervals Based on Size of Buckets', () => { it('Empty Arguments', () => { const nearDuration = calculateAuto(); - expect(nearDuration.milliseconds()).to.be.eql(0); + expect(nearDuration.milliseconds()).toBe(0); }); const duration = moment.duration(1456964549657 - 1456964538365, 'ms'); // about 11 seconds @@ -30,7 +29,7 @@ describe('Calculating Time Intervals Based on Size of Buckets', () => { _.each(tuples, (t) => { it(`Bucket Size: ${t[0]} - Time Interval: ${t[1]}`, () => { const result = calculateAuto(t[0], duration); - expect(result.milliseconds()).to.be.eql(t[1]); + expect(result.milliseconds()).toBe(t[1]); }); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_availabiilty.js b/x-pack/plugins/monitoring/server/lib/calculate_availabiilty.test.js similarity index 69% rename from x-pack/plugins/monitoring/server/lib/__tests__/calculate_availabiilty.js rename to x-pack/plugins/monitoring/server/lib/calculate_availabiilty.test.js index 3f70db5124fcb..99acdfd8a27e5 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_availabiilty.js +++ b/x-pack/plugins/monitoring/server/lib/calculate_availabiilty.test.js @@ -4,18 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import moment from 'moment'; -import { calculateAvailability } from '../calculate_availability'; +import { calculateAvailability } from './calculate_availability'; describe('Calculate Availability', () => { it('is available', () => { const input = moment(); - expect(calculateAvailability(input)).to.be(true); + expect(calculateAvailability(input)).toBe(true); }); it('is not available', () => { const input = moment().subtract(11, 'minutes'); - expect(calculateAvailability(input)).to.be(false); + expect(calculateAvailability(input)).toBe(false); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_overall_status.js b/x-pack/plugins/monitoring/server/lib/calculate_overall_status.test.js similarity index 75% rename from x-pack/plugins/monitoring/server/lib/__tests__/calculate_overall_status.js rename to x-pack/plugins/monitoring/server/lib/calculate_overall_status.test.js index 38d59f9af4f5a..82d2b79ee9e2a 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_overall_status.js +++ b/x-pack/plugins/monitoring/server/lib/calculate_overall_status.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { calculateOverallStatus } from '../calculate_overall_status'; +import { calculateOverallStatus } from './calculate_overall_status'; describe('Calculate Kibana Cluster Helath', () => { it('health status combined from multiple instances', () => { @@ -25,13 +24,13 @@ describe('Calculate Kibana Cluster Helath', () => { ]; greens.forEach((set) => { - expect(calculateOverallStatus(set)).to.be('green'); + expect(calculateOverallStatus(set)).toBe('green'); }); yellows.forEach((set) => { - expect(calculateOverallStatus(set)).to.be('yellow'); + expect(calculateOverallStatus(set)).toBe('yellow'); }); reds.forEach((set) => { - expect(calculateOverallStatus(set)).to.be('red'); + expect(calculateOverallStatus(set)).toBe('red'); }); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_rate.js b/x-pack/plugins/monitoring/server/lib/calculate_rate.test.js similarity index 82% rename from x-pack/plugins/monitoring/server/lib/__tests__/calculate_rate.js rename to x-pack/plugins/monitoring/server/lib/calculate_rate.test.js index bc34f2d6b187f..d7560e805309a 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_rate.js +++ b/x-pack/plugins/monitoring/server/lib/calculate_rate.test.js @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { calculateRate } from '../calculate_rate'; -import expect from '@kbn/expect'; +import { calculateRate } from './calculate_rate'; describe('Calculate Rate', () => { it('returns null when all fields are undefined', () => { const { rate, isEstimate } = calculateRate({}); - expect(rate).to.be(null); - expect(isEstimate).to.be(false); + expect(rate).toBe(null); + expect(isEstimate).toBe(false); }); it('returns null when time window size is 0', () => { @@ -23,8 +22,8 @@ describe('Calculate Rate', () => { timeWindowMin: '2017-08-08T17:33:04.501Z', timeWindowMax: '2017-08-08T17:33:04.501Z', // max === min }); - expect(rate).to.be(null); - expect(isEstimate).to.be(false); + expect(rate).toBe(null); + expect(isEstimate).toBe(false); }); it('returns null when time between latest hit and earliest hit 0', () => { @@ -36,8 +35,8 @@ describe('Calculate Rate', () => { timeWindowMin: '2017-08-08T17:33:04.501Z', timeWindowMax: '2017-08-08T18:33:04.501Z', }); - expect(rate).to.be(null); - expect(isEstimate).to.be(false); + expect(rate).toBe(null); + expect(isEstimate).toBe(false); }); it('calculates a rate over time', () => { @@ -49,8 +48,8 @@ describe('Calculate Rate', () => { timeWindowMin: '2017-08-08T17:33:04.501Z', timeWindowMax: '2017-08-08T18:33:04.501Z', }); - expect(rate).to.be(1.6608333333333334); - expect(isEstimate).to.be(false); + expect(rate).toBe(1.6608333333333334); + expect(isEstimate).toBe(false); }); it('calculates zero as the rate if latest - earliest is 0', () => { @@ -62,8 +61,8 @@ describe('Calculate Rate', () => { timeWindowMin: '2017-08-08T17:33:04.501Z', timeWindowMax: '2017-08-08T18:33:04.501Z', }); - expect(rate).to.be(0); - expect(isEstimate).to.be(false); + expect(rate).toBe(0); + expect(isEstimate).toBe(false); }); it('calculates rate based on latest metric if the count metric reset', () => { @@ -75,7 +74,7 @@ describe('Calculate Rate', () => { timeWindowMin: '2017-08-08T17:33:04.501Z', timeWindowMax: '2017-08-08T18:33:04.501Z', }); - expect(rate).to.be(5.555555555555555); - expect(isEstimate).to.be(true); + expect(rate).toBe(5.555555555555555); + expect(isEstimate).toBe(true); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_timeseries_interval.js b/x-pack/plugins/monitoring/server/lib/calculate_timeseries_interval.test.js similarity index 91% rename from x-pack/plugins/monitoring/server/lib/__tests__/calculate_timeseries_interval.js rename to x-pack/plugins/monitoring/server/lib/calculate_timeseries_interval.test.js index 392494f5a979a..dc533e133d425 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/calculate_timeseries_interval.js +++ b/x-pack/plugins/monitoring/server/lib/calculate_timeseries_interval.test.js @@ -5,8 +5,7 @@ */ import moment from 'moment'; -import expect from '@kbn/expect'; -import { calculateTimeseriesInterval } from '../calculate_timeseries_interval'; +import { calculateTimeseriesInterval } from './calculate_timeseries_interval'; describe('calculateTimeseriesInterval', () => { it('returns an interval of 10s when duration is 15m', () => { @@ -16,7 +15,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(10); + ).toBe(10); }); it('returns an interval of 30s when duration is 30m', () => { @@ -26,7 +25,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(30); + ).toBe(30); }); it('returns an interval of 30s when duration is 1h', () => { @@ -36,7 +35,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(30); + ).toBe(30); }); it('returns an interval of 1m when duration is 4h', () => { @@ -46,7 +45,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(60); + ).toBe(60); }); it('returns an interval of 5m when duration is 12h', () => { @@ -56,7 +55,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(5 * 60); + ).toBe(5 * 60); }); it('returns an interval of 10m when duration is 24h', () => { @@ -66,7 +65,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(10 * 60); + ).toBe(10 * 60); }); it('returns an interval of 1h when duration is 7d', () => { @@ -76,7 +75,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(60 * 60); + ).toBe(60 * 60); }); it('returns an interval of 12h when duration is 30d', () => { @@ -86,7 +85,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(12 * 60 * 60); + ).toBe(12 * 60 * 60); }); it('returns an interval of 12h when duration is 60d', () => { @@ -96,7 +95,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(12 * 60 * 60); + ).toBe(12 * 60 * 60); }); it('returns an interval of 12h when duration is 90d', () => { @@ -106,7 +105,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(12 * 60 * 60); + ).toBe(12 * 60 * 60); }); it('returns an interval of 1d when duration is 6mo', () => { @@ -116,7 +115,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(24 * 60 * 60); + ).toBe(24 * 60 * 60); }); it('returns an interval of 1d when duration is 1y', () => { @@ -126,7 +125,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(24 * 60 * 60); + ).toBe(24 * 60 * 60); }); it('returns an interval of 7d when duration is 2y', () => { @@ -136,7 +135,7 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(7 * 24 * 60 * 60); + ).toBe(7 * 24 * 60 * 60); }); it('returns an interval of 7d when duration is 5y', () => { @@ -146,6 +145,6 @@ describe('calculateTimeseriesInterval', () => { expect( calculateTimeseriesInterval(lowerBound.valueOf(), upperBound.valueOf(), minIntervalSeconds) - ).to.be(7 * 24 * 60 * 60); + ).toBe(7 * 24 * 60 * 60); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/ccs_utils.js b/x-pack/plugins/monitoring/server/lib/ccs_utils.test.js similarity index 95% rename from x-pack/plugins/monitoring/server/lib/__tests__/ccs_utils.js rename to x-pack/plugins/monitoring/server/lib/ccs_utils.test.js index d17253dc0169a..bae23ad84eb75 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/ccs_utils.js +++ b/x-pack/plugins/monitoring/server/lib/ccs_utils.test.js @@ -6,9 +6,11 @@ import expect from '@kbn/expect'; import sinon from 'sinon'; -import { parseCrossClusterPrefix, prefixIndexPattern } from '../ccs_utils'; +import { parseCrossClusterPrefix, prefixIndexPattern } from './ccs_utils'; -describe('ccs_utils', () => { +// TODO: tests were not running and are not updated. +// They need to be changed to run. +describe.skip('ccs_utils', () => { describe('prefixIndexPattern', () => { const indexPattern = '.monitoring-xyz-1-*,.monitoring-xyz-2-*'; diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__test__/fixtures/clusters.json b/x-pack/plugins/monitoring/server/lib/cluster/__fixtures__/clusters.json similarity index 100% rename from x-pack/plugins/monitoring/server/lib/cluster/__test__/fixtures/clusters.json rename to x-pack/plugins/monitoring/server/lib/cluster/__fixtures__/clusters.json diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__test__/__snapshots__/get_clusters_summary.test.js.snap b/x-pack/plugins/monitoring/server/lib/cluster/__snapshots__/get_clusters_summary.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/server/lib/cluster/__test__/__snapshots__/get_clusters_summary.test.js.snap rename to x-pack/plugins/monitoring/server/lib/cluster/__snapshots__/get_clusters_summary.test.js.snap diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/flag_supported_clusters.js b/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.test.js similarity index 93% rename from x-pack/plugins/monitoring/server/lib/cluster/__tests__/flag_supported_clusters.js rename to x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.test.js index 577bf4606c7aa..c808b25b4b6dd 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/flag_supported_clusters.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.test.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import sinon from 'sinon'; -import { flagSupportedClusters } from '../flag_supported_clusters'; +import { flagSupportedClusters } from './flag_supported_clusters'; const mockReq = (log, queryResult = {}) => { return { @@ -38,7 +37,8 @@ const goldLicense = () => ({ license: { type: 'gold' } }); const basicLicense = () => ({ license: { type: 'basic' } }); const standaloneCluster = () => ({ cluster_uuid: '__standalone_cluster__' }); -describe('Flag Supported Clusters', () => { +// TODO: tests were not being run and are not up to date +describe.skip('Flag Supported Clusters', () => { describe('With multiple clusters in the monitoring data', () => { it('When all clusters are non-Basic licensed, all are supported', () => { const logStub = sinon.stub(); @@ -55,7 +55,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((resultClusters) => { - expect(resultClusters).to.eql([ + expect(resultClusters).toEqual([ { ...goldLicense(), isSupported: true }, {}, // no license { ...goldLicense(), isSupported: true }, @@ -87,7 +87,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((resultClusters) => { - expect(resultClusters).to.eql([ + expect(resultClusters).toEqual([ { cluster_uuid: 'supported_cluster_uuid', isSupported: true, @@ -126,7 +126,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((resultClusters) => { - expect(resultClusters).to.eql([ + expect(resultClusters).toEqual([ { cluster_uuid: 'supported_cluster_uuid_1', isSupported: true, @@ -169,7 +169,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((resultClusters) => { - expect(resultClusters).to.eql([ + expect(resultClusters).toEqual([ { cluster_uuid: 'supported_cluster_uuid', isSupported: true, @@ -206,7 +206,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((resultClusters) => { - expect(resultClusters).to.eql([ + expect(resultClusters).toEqual([ { cluster_uuid: 'supported_cluster_uuid', isSupported: true, @@ -243,7 +243,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((resultClusters) => { - expect(resultClusters).to.eql([ + expect(resultClusters).toEqual([ { cluster_uuid: 'supported_cluster_uuid_1', isSupported: true, @@ -281,7 +281,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((result) => { - expect(result).to.eql([{ isSupported: true, ...basicLicense() }]); + expect(result).toEqual([{ isSupported: true, ...basicLicense() }]); sinon.assert.calledWith( logStub, ['debug', 'monitoring', 'supported-clusters'], @@ -298,7 +298,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((result) => { - expect(result).to.eql([{ isSupported: true, ...goldLicense() }]); + expect(result).toEqual([{ isSupported: true, ...goldLicense() }]); sinon.assert.calledWith( logStub, ['debug', 'monitoring', 'supported-clusters'], @@ -316,7 +316,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((result) => { - expect(result).to.eql([{ ...deletedLicense() }]); + expect(result).toEqual([{ ...deletedLicense() }]); sinon.assert.calledWith( logStub, ['debug', 'monitoring', 'supported-clusters'], @@ -334,7 +334,7 @@ describe('Flag Supported Clusters', () => { req, kbnIndices )(clusters).then((result) => { - expect(result).to.eql([{ ...standaloneCluster(), isSupported: true }]); + expect(result).toEqual([{ ...standaloneCluster(), isSupported: true }]); sinon.assert.calledWith( logStub, ['debug', 'monitoring', 'supported-clusters'], diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_cluster_status.js b/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.test.js similarity index 92% rename from x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_cluster_status.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.test.js index 5fb4e95e6bc4c..4b7de58c839de 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_cluster_status.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { getClusterStatus } from '../get_cluster_status'; +import { getClusterStatus } from './get_cluster_status'; let clusterStats = {}; let shardStats; @@ -22,7 +21,7 @@ describe('getClusterStatus', () => { it('gets an unknown status', () => { const defaultResult = getClusterStatus(clusterStats, shardStats); - expect(defaultResult).to.eql({ + expect(defaultResult).toEqual({ status: 'unknown', nodesCount: 0, indicesCount: 0, @@ -85,7 +84,7 @@ describe('getClusterStatus', () => { const calculatedResult = getClusterStatus(clusterStats, shardStats); - expect(calculatedResult).to.eql({ + expect(calculatedResult).toEqual({ status: 'green', nodesCount: 2, indicesCount: 10, @@ -105,7 +104,7 @@ describe('getClusterStatus', () => { const calculatedResult = getClusterStatus(clusterStats, shardStats); - expect(calculatedResult).to.eql({ + expect(calculatedResult).toEqual({ status: 'green', nodesCount: 2, indicesCount: 10, diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_clusters_state.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.test.js similarity index 72% rename from x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_clusters_state.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.test.js index cc62e59986f1d..f962d1930edda 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_clusters_state.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { handleResponse } from '../get_clusters_state'; -import expect from '@kbn/expect'; +import { handleResponse } from './get_clusters_state'; import moment from 'moment'; import { set } from '@elastic/safer-lodash-set'; @@ -43,13 +42,13 @@ const response = { describe('get_clusters_state', () => { it('returns an available cluster', () => { const result = handleResponse(response, clusters); - expect(result).to.be(clusters); - expect(result.length).to.be(1); - expect(result[0].cluster_uuid).to.be('abc123'); - expect(result[0].cluster_state.master_node).to.be('uuid1123'); - expect(result[0].cluster_state.status).to.be('green'); - expect(result[0].cluster_state.state_uuid).to.be('uuid1123'); - expect(result[0].cluster_state.nodes).to.eql({ + expect(result).toBe(clusters); + expect(result.length).toBe(1); + expect(result[0].cluster_uuid).toBe('abc123'); + expect(result[0].cluster_state.master_node).toBe('uuid1123'); + expect(result[0].cluster_state.status).toBe('green'); + expect(result[0].cluster_state.state_uuid).toBe('uuid1123'); + expect(result[0].cluster_state.nodes).toEqual({ nodeUuid0123: { name: 'node01', uuid: 'nodeUuid0123' }, }); }); @@ -57,7 +56,7 @@ describe('get_clusters_state', () => { it('does not filter out an unavailable cluster', () => { set(response, '.hits.hits[0]._source.timestamp', moment().subtract(30, 'days').format()); const result = handleResponse(response, clusters); - expect(result).to.be(clusters); - expect(result.length).to.be(1); + expect(result).toBe(clusters); + expect(result.length).toBe(1); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_clusters_stats.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.test.js similarity index 65% rename from x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_clusters_stats.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.test.js index 87aedf8ff9375..344d1008353f0 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/__tests__/get_clusters_stats.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { handleClusterStats } from '../get_clusters_stats'; +import { handleClusterStats } from './get_clusters_stats'; describe('handleClusterStats', () => { // valid license requires a cluster UUID of "12" for the hkey to match @@ -18,14 +17,14 @@ describe('handleClusterStats', () => { }; it('handles no response by returning an empty array', () => { - expect(handleClusterStats()).to.eql([]); - expect(handleClusterStats(null)).to.eql([]); - expect(handleClusterStats({})).to.eql([]); - expect(handleClusterStats({ hits: { total: 0 } })).to.eql([]); - expect(handleClusterStats({ hits: { hits: [] } })).to.eql([]); + expect(handleClusterStats()).toEqual([]); + expect(handleClusterStats(null)).toEqual([]); + expect(handleClusterStats({})).toEqual([]); + expect(handleClusterStats({ hits: { total: 0 } })).toEqual([]); + expect(handleClusterStats({ hits: { hits: [] } })).toEqual([]); // no _source means we can't use it: - expect(handleClusterStats({ hits: { hits: [{}] } })).to.eql([]); - expect(handleClusterStats({ hits: { hits: [{ _index: '.monitoring' }] } })).to.eql([]); + expect(handleClusterStats({ hits: { hits: [{}] } })).toEqual([]); + expect(handleClusterStats({ hits: { hits: [{ _index: '.monitoring' }] } })).toEqual([]); }); it('handles ccs response by adding it to the cluster detail', () => { @@ -45,10 +44,10 @@ describe('handleClusterStats', () => { const clusters = handleClusterStats(response, { log: () => undefined }); - expect(clusters.length).to.eql(1); - expect(clusters[0].ccs).to.eql('cluster_one'); - expect(clusters[0].cluster_uuid).to.eql('xyz'); - expect(clusters[0].license).to.be(undefined); + expect(clusters.length).toEqual(1); + expect(clusters[0].ccs).toEqual('cluster_one'); + expect(clusters[0].cluster_uuid).toEqual('xyz'); + expect(clusters[0].license).toBe(undefined); }); it('handles invalid license', () => { @@ -69,10 +68,10 @@ describe('handleClusterStats', () => { const clusters = handleClusterStats(response); - expect(clusters.length).to.eql(1); - expect(clusters[0].ccs).to.be(undefined); - expect(clusters[0].cluster_uuid).to.eql('xyz'); - expect(clusters[0].license).to.be(validLicense); + expect(clusters.length).toEqual(1); + expect(clusters[0].ccs).toBe(undefined); + expect(clusters[0].cluster_uuid).toEqual('xyz'); + expect(clusters[0].license).toBe(validLicense); }); it('handles valid license', () => { @@ -92,10 +91,10 @@ describe('handleClusterStats', () => { const clusters = handleClusterStats(response); - expect(clusters.length).to.eql(1); - expect(clusters[0].ccs).to.be(undefined); - expect(clusters[0].cluster_uuid).to.be(validLicenseClusterUuid); - expect(clusters[0].license).to.be(validLicense); + expect(clusters.length).toEqual(1); + expect(clusters[0].ccs).toBe(undefined); + expect(clusters[0].cluster_uuid).toBe(validLicenseClusterUuid); + expect(clusters[0].license).toBe(validLicense); }); it('handles multiple clusters', () => { @@ -136,15 +135,15 @@ describe('handleClusterStats', () => { const clusters = handleClusterStats(response); - expect(clusters.length).to.eql(3); - expect(clusters[0].ccs).to.be(undefined); - expect(clusters[0].cluster_uuid).to.be(validLicenseClusterUuid); - expect(clusters[0].license).to.be(validLicense); - expect(clusters[1].ccs).to.eql('abc'); - expect(clusters[1].cluster_uuid).to.eql('xyz'); - expect(clusters[1].license).to.be(validLicense); - expect(clusters[2].ccs).to.eql('local_cluster'); - expect(clusters[2].cluster_uuid).to.be(validLicenseClusterUuid); - expect(clusters[2].license).to.be(validLicense); + expect(clusters.length).toEqual(3); + expect(clusters[0].ccs).toBe(undefined); + expect(clusters[0].cluster_uuid).toBe(validLicenseClusterUuid); + expect(clusters[0].license).toBe(validLicense); + expect(clusters[1].ccs).toEqual('abc'); + expect(clusters[1].cluster_uuid).toEqual('xyz'); + expect(clusters[1].license).toBe(validLicense); + expect(clusters[2].ccs).toEqual('local_cluster'); + expect(clusters[2].cluster_uuid).toBe(validLicenseClusterUuid); + expect(clusters[2].license).toBe(validLicense); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/cluster/__test__/get_clusters_summary.test.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_summary.test.js similarity index 92% rename from x-pack/plugins/monitoring/server/lib/cluster/__test__/get_clusters_summary.test.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_clusters_summary.test.js index a835344082b01..d7bc9306f3710 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/__test__/get_clusters_summary.test.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_summary.test.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import clusters from './fixtures/clusters'; -import { getClustersSummary } from '../get_clusters_summary'; +import clusters from './__fixtures__/clusters'; +import { getClustersSummary } from './get_clusters_summary'; const mockLog = jest.fn(); const mockServer = { diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/create_query.js b/x-pack/plugins/monitoring/server/lib/create_query.test.js similarity index 81% rename from x-pack/plugins/monitoring/server/lib/__tests__/create_query.js rename to x-pack/plugins/monitoring/server/lib/create_query.test.js index e8862c47d4bf2..bdfb8bcce5306 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/create_query.js +++ b/x-pack/plugins/monitoring/server/lib/create_query.test.js @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import { set } from '@elastic/safer-lodash-set'; -import { MissingRequiredError } from '../error_missing_required'; -import { ElasticsearchMetric } from '../metrics'; -import { createQuery } from '../create_query.js'; +import { MissingRequiredError } from './error_missing_required'; +import { ElasticsearchMetric } from './metrics'; +import { createQuery } from './create_query.js'; let metric; @@ -21,7 +20,7 @@ describe('Create Query', () => { const options = { metric }; const result = createQuery(options); const expected = set({}, 'bool.filter', []); - expect(result).to.be.eql(expected); + expect(result).toEqual(expected); }); it('Uses Elasticsearch timestamp field for start and end time range by default', () => { @@ -41,7 +40,7 @@ describe('Create Query', () => { gte: 1456826400000, lte: 1456826401000, }); - expect(result).to.be.eql(expected); + expect(result).toEqual(expected); }); it('Injects uuid and timestamp fields dynamically, based on metric', () => { @@ -61,7 +60,7 @@ describe('Create Query', () => { gte: 1456826400000, lte: 1456826401000, }); - expect(result).to.be.eql(expected); + expect(result).toEqual(expected); }); it('Throws if missing metric.timestampField', () => { @@ -69,9 +68,7 @@ describe('Create Query', () => { const options = {}; // missing metric object return createQuery(options); } - expect(callCreateQuery).to.throwException((e) => { - expect(e).to.be.a(MissingRequiredError); - }); + expect(callCreateQuery).toThrowError(MissingRequiredError); }); it('Throws if given uuid but missing metric.uuidField', () => { @@ -80,12 +77,11 @@ describe('Create Query', () => { delete options.metric.uuidField; return createQuery(options); } - expect(callCreateQuery).to.throwException((e) => { - expect(e).to.be.a(MissingRequiredError); - }); + expect(callCreateQuery).toThrowError(MissingRequiredError); }); - it('Uses `type` option to add type filter with minimal fields', () => { + // TODO: tests were not running and need to be updated to pass + it.skip('Uses `type` option to add type filter with minimal fields', () => { const options = { type: 'test-type-yay', metric }; const result = createQuery(options); let expected = {}; @@ -93,7 +89,7 @@ describe('Create Query', () => { expect(result).to.be.eql(expected); }); - it('Uses `type` option to add type filter with all other option fields', () => { + it.skip('Uses `type` option to add type filter with all other option fields', () => { const options = { type: 'test-type-yay', uuid: 'abc123', diff --git a/x-pack/plugins/monitoring/server/lib/details/__test__/fixtures/agg_metrics_buckets.json b/x-pack/plugins/monitoring/server/lib/details/__fixtures__/agg_metrics_buckets.json similarity index 100% rename from x-pack/plugins/monitoring/server/lib/details/__test__/fixtures/agg_metrics_buckets.json rename to x-pack/plugins/monitoring/server/lib/details/__fixtures__/agg_metrics_buckets.json diff --git a/x-pack/plugins/monitoring/server/lib/details/__test__/fixtures/deriv_metrics_buckets.json b/x-pack/plugins/monitoring/server/lib/details/__fixtures__/deriv_metrics_buckets.json similarity index 100% rename from x-pack/plugins/monitoring/server/lib/details/__test__/fixtures/deriv_metrics_buckets.json rename to x-pack/plugins/monitoring/server/lib/details/__fixtures__/deriv_metrics_buckets.json diff --git a/x-pack/plugins/monitoring/server/lib/details/__test__/fixtures/non_deriv_metrics_buckets.json b/x-pack/plugins/monitoring/server/lib/details/__fixtures__/non_deriv_metrics_buckets.json similarity index 100% rename from x-pack/plugins/monitoring/server/lib/details/__test__/fixtures/non_deriv_metrics_buckets.json rename to x-pack/plugins/monitoring/server/lib/details/__fixtures__/non_deriv_metrics_buckets.json diff --git a/x-pack/plugins/monitoring/server/lib/details/__test__/__snapshots__/get_metrics.test.js.snap b/x-pack/plugins/monitoring/server/lib/details/__snapshots__/get_metrics.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/server/lib/details/__test__/__snapshots__/get_metrics.test.js.snap rename to x-pack/plugins/monitoring/server/lib/details/__snapshots__/get_metrics.test.js.snap diff --git a/x-pack/plugins/monitoring/server/lib/details/__test__/get_metrics.test.js b/x-pack/plugins/monitoring/server/lib/details/get_metrics.test.js similarity index 92% rename from x-pack/plugins/monitoring/server/lib/details/__test__/get_metrics.test.js rename to x-pack/plugins/monitoring/server/lib/details/get_metrics.test.js index fbe6c8ec4cfa3..8ae8f810ee7de 100644 --- a/x-pack/plugins/monitoring/server/lib/details/__test__/get_metrics.test.js +++ b/x-pack/plugins/monitoring/server/lib/details/get_metrics.test.js @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getMetrics } from '../get_metrics'; +import { getMetrics } from './get_metrics'; import sinon from 'sinon'; -import nonDerivMetricsBuckets from './fixtures/non_deriv_metrics_buckets'; -import derivMetricsBuckets from './fixtures/deriv_metrics_buckets'; -import aggMetricsBuckets from './fixtures/agg_metrics_buckets'; +import nonDerivMetricsBuckets from './__fixtures__/non_deriv_metrics_buckets'; +import derivMetricsBuckets from './__fixtures__/deriv_metrics_buckets'; +import aggMetricsBuckets from './__fixtures__/agg_metrics_buckets'; // max / min window that accepts the above buckets/results const min = 1498968000000; // 2017-07-02T04:00:00.000Z diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/__tests__/get_last_recovery.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.test.js similarity index 62% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/__tests__/get_last_recovery.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.test.js index 1c4c646c1fc77..2528cfed0ebc6 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/__tests__/get_last_recovery.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { handleLastRecoveries, filterOldShardActivity } from '../get_last_recovery'; -import expect from '@kbn/expect'; +import { handleLastRecoveries, filterOldShardActivity } from './get_last_recovery'; describe('get_last_recovery', () => { // Note: times are from the epoch! @@ -47,39 +46,39 @@ describe('get_last_recovery', () => { it('No hits results in an empty array', () => { // Note: we don't expect it to touch hits without total === 1 - expect(handleLastRecoveries({ hits: { hits: [] } }, new Date(0))).to.have.length(0); + expect(handleLastRecoveries({ hits: { hits: [] } }, new Date(0))).toHaveLength(0); }); it('Filters on stop time', () => { - expect(handleLastRecoveries(resp, new Date(0))).to.have.length(5); - expect(handleLastRecoveries(resp, new Date(99))).to.have.length(5); - expect(handleLastRecoveries(resp, new Date(100))).to.have.length(5); - expect(handleLastRecoveries(resp, new Date(101))).to.have.length(3); - expect(handleLastRecoveries(resp, new Date(501))).to.have.length(0); + expect(handleLastRecoveries(resp, new Date(0))).toHaveLength(5); + expect(handleLastRecoveries(resp, new Date(99))).toHaveLength(5); + expect(handleLastRecoveries(resp, new Date(100))).toHaveLength(5); + expect(handleLastRecoveries(resp, new Date(101))).toHaveLength(3); + expect(handleLastRecoveries(resp, new Date(501))).toHaveLength(0); const filteredActivities = handleLastRecoveries(resp, new Date(301)); - expect(filteredActivities).to.have.length(1); - expect(filteredActivities[0].stop_time_in_millis).to.be.equal(500); + expect(filteredActivities).toHaveLength(1); + expect(filteredActivities[0].stop_time_in_millis).toEqual(500); }); it('Sorts based on start time (descending)', () => { const sortedActivities = handleLastRecoveries(resp, new Date(0)); - expect(sortedActivities[0].start_time_in_millis).to.be.equal(100); - expect(sortedActivities[4].start_time_in_millis).to.be.equal(0); + expect(sortedActivities[0].start_time_in_millis).toEqual(100); + expect(sortedActivities[4].start_time_in_millis).toEqual(0); }); it('Filters only on stop time', () => { const filter = filterOldShardActivity(10); - expect(filter({})).to.be(true); - expect(filter({ stop_time_in_millis: null })).to.be(true); - expect(filter({ stop_time_in_millis: 100 })).to.be(true); - expect(filter({ stop_time_in_millis: 10 })).to.be(true); - expect(filter({ stop_time_in_millis: 9 })).to.be(false); - expect(filter({ stop_time_in_millis: 0 })).to.be(false); + expect(filter({})).toBe(true); + expect(filter({ stop_time_in_millis: null })).toBe(true); + expect(filter({ stop_time_in_millis: 100 })).toBe(true); + expect(filter({ stop_time_in_millis: 10 })).toBe(true); + expect(filter({ stop_time_in_millis: 9 })).toBe(false); + expect(filter({ stop_time_in_millis: 0 })).toBe(false); // nonsense in terms of value order, but ensures that start_time isn't considered - expect(filter({ start_time_in_millis: 50, stop_time_in_millis: 0 })).to.be(false); + expect(filter({ start_time_in_millis: 50, stop_time_in_millis: 0 })).toBe(false); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/__tests__/get_ml_jobs.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.test.js similarity index 94% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/__tests__/get_ml_jobs.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.test.js index c2cf19471ecb2..e0c9664bd8daa 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/__tests__/get_ml_jobs.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.test.js @@ -5,14 +5,13 @@ */ import { set } from '@elastic/safer-lodash-set'; -import expect from '@kbn/expect'; -import { handleResponse } from '../get_ml_jobs'; +import { handleResponse } from './get_ml_jobs'; describe('Get ML Jobs', () => { it('returns empty array when there are no hits', () => { const jobStats = []; const result = handleResponse(jobStats); - expect(result).to.eql([]); + expect(result).toEqual([]); }); it('maps out the inner result data when there are a few hits', () => { const jobStats = []; @@ -42,7 +41,7 @@ describe('Get ML Jobs', () => { }); const result = handleResponse(jobStats); - expect(result).to.eql([ + expect(result).toEqual([ { job_id: 'job_id_uno', state: 'opened', diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/cluster.js b/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/cluster.test.js similarity index 85% rename from x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/cluster.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch_settings/cluster.test.js index 426ae70bad93d..41345499c61dd 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/cluster.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/cluster.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { checkClusterSettings } from '../'; +import { checkClusterSettings } from '.'; describe('Elasticsearch Cluster Settings', () => { const makeResponse = (property, response = {}) => { @@ -46,7 +45,7 @@ describe('Elasticsearch Cluster Settings', () => { it('should return { found: false } given no response from ES', async () => { const mockReq = getReq(makeResponse('ignore', {})); const result = await checkClusterSettings(mockReq); - expect(result).to.eql({ found: false }); + expect(result).toEqual({ found: false }); }); it('should find default collection interval reason', async () => { @@ -68,15 +67,15 @@ describe('Elasticsearch Cluster Settings', () => { mockReq = getReq(makeResponse('persistent', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('persistent')); + expect(result).toEqual(makeExpected('persistent')); mockReq = getReq(makeResponse('transient', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('transient')); + expect(result).toEqual(makeExpected('transient')); mockReq = getReq(makeResponse('defaults', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('defaults')); + expect(result).toEqual(makeExpected('defaults')); }); it('should find exporters reason', async () => { @@ -98,15 +97,15 @@ describe('Elasticsearch Cluster Settings', () => { mockReq = getReq(makeResponse('persistent', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('persistent')); + expect(result).toEqual(makeExpected('persistent')); mockReq = getReq(makeResponse('transient', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('transient')); + expect(result).toEqual(makeExpected('transient')); mockReq = getReq(makeResponse('defaults', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('defaults')); + expect(result).toEqual(makeExpected('defaults')); }); it('should find enabled reason', async () => { @@ -128,14 +127,14 @@ describe('Elasticsearch Cluster Settings', () => { mockReq = getReq(makeResponse('persistent', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('persistent')); + expect(result).toEqual(makeExpected('persistent')); mockReq = getReq(makeResponse('transient', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('transient')); + expect(result).toEqual(makeExpected('transient')); mockReq = getReq(makeResponse('defaults', setting)); result = await checkClusterSettings(mockReq); - expect(result).to.eql(makeExpected('defaults')); + expect(result).toEqual(makeExpected('defaults')); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/find_reason.js b/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/find_reason.test.js similarity index 91% rename from x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/find_reason.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch_settings/find_reason.test.js index dd6b99bd0292c..9323ba3e2b418 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/find_reason.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/find_reason.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { findReason } from '../find_reason'; +import { findReason } from './find_reason'; describe('Elasticsearch Settings Find Reason for No Data', () => { const context = { context: 'unit_test' }; @@ -21,7 +20,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }, context ); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -41,7 +40,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }, context ); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -63,7 +62,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }, context ); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -77,14 +76,14 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { describe('collection interval', () => { it('should not flag collection interval if value is > 0', async () => { const result = await findReason({ collection: { interval: 1 } }, context); - expect(result).to.eql({ found: false }); + expect(result).toEqual({ found: false }); }); it('should flag collection interval for any invalid value', async () => { let result; result = await findReason({ collection: { interval: 0 } }, context); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -94,7 +93,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }); result = await findReason({ collection: { interval: -10 } }, context); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -104,7 +103,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }); result = await findReason({ collection: { interval: null } }, context); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -117,16 +116,16 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { it('should not flag enabled if value is true', async () => { const result = await findReason({ enabled: true }, context); - expect(result).to.eql({ found: false }); + expect(result).toEqual({ found: false }); }); it('should not flag exporters if value is undefined/null', async () => { let result; result = await findReason({ exporters: undefined }, context); - expect(result).to.eql({ found: false }); + expect(result).toEqual({ found: false }); result = await findReason({ exporters: null }, context); - expect(result).to.eql({ found: false }); + expect(result).toEqual({ found: false }); }); describe('exporters', () => { @@ -152,7 +151,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }, }; const result = await findReason(input, context); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -177,7 +176,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }; const result = await findReason(input, context, true); // last element is to enable cloud - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -205,7 +204,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }, }; const result = await findReason(input, context); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'unit_test', @@ -237,7 +236,7 @@ describe('Elasticsearch Settings Find Reason for No Data', () => { }, }; const result = await findReason(input, context); - expect(result).to.eql({ found: false }); + expect(result).toEqual({ found: false }); }); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/nodes.js b/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/nodes.test.js similarity index 91% rename from x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/nodes.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch_settings/nodes.test.js index 4604698ece9f4..a06afb5315970 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/__tests__/nodes.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch_settings/nodes.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { checkNodesSettings } from '../'; +import { checkNodesSettings } from '.'; describe('Elasticsearch Nodes Settings', () => { const getReq = (response) => { @@ -25,7 +24,7 @@ describe('Elasticsearch Nodes Settings', () => { it('should return { found: false } given no response from ES', async () => { const mockReq = getReq(); const result = await checkNodesSettings(mockReq); - expect(result).to.eql({ found: false }); + expect(result).toEqual({ found: false }); }); it('should find default collection interval reason', async () => { @@ -40,11 +39,11 @@ describe('Elasticsearch Nodes Settings', () => { }); const result = await checkNodesSettings(mockReq); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'nodeId: node01abc', - data: -1, + data: '-1', property: 'xpack.monitoring.collection.interval', }, }); @@ -62,7 +61,7 @@ describe('Elasticsearch Nodes Settings', () => { }); const result = await checkNodesSettings(mockReq); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'nodeId: node02def', @@ -84,7 +83,7 @@ describe('Elasticsearch Nodes Settings', () => { }); const result = await checkNodesSettings(mockReq); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'nodeId: node02def', @@ -106,7 +105,7 @@ describe('Elasticsearch Nodes Settings', () => { }); const result = await checkNodesSettings(mockReq); - expect(result).to.eql({ + expect(result).toEqual({ found: true, reason: { context: 'nodeId: node02def', diff --git a/x-pack/plugins/monitoring/server/lib/errors/__tests__/auth_errors.js b/x-pack/plugins/monitoring/server/lib/errors/auth_errors.test.js similarity index 59% rename from x-pack/plugins/monitoring/server/lib/errors/__tests__/auth_errors.js rename to x-pack/plugins/monitoring/server/lib/errors/auth_errors.test.js index 677b395caedd4..cea45f2fd22f0 100644 --- a/x-pack/plugins/monitoring/server/lib/errors/__tests__/auth_errors.js +++ b/x-pack/plugins/monitoring/server/lib/errors/auth_errors.test.js @@ -4,28 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import { errors } from 'elasticsearch'; import { forbidden, unauthorized } from '@hapi/boom'; -import { isAuthError, handleAuthError } from '../auth_errors'; +import { isAuthError, handleAuthError } from './auth_errors'; describe('Error handling for 401/403 errors', () => { it('ignores an unknown type', () => { const err = new errors.Generic(); - expect(isAuthError(err)).to.be(false); + expect(isAuthError(err)).toBe(false); }); describe('Boom errors', () => { it('handles Forbidden Error defined by Boom', () => { const err = forbidden(); - expect(isAuthError(err)).to.be(true); + expect(isAuthError(err)).toBe(true); const wrappedErr = handleAuthError(err); - expect(wrappedErr.message).to.be('Insufficient user permissions for monitoring data'); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(false); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.message).toBe('Insufficient user permissions for monitoring data'); + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(false); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 403, payload: { statusCode: 403, @@ -38,14 +37,14 @@ describe('Error handling for 401/403 errors', () => { it('handles Unauthorized Error defined by Boom', () => { const err = unauthorized(); - expect(isAuthError(err)).to.be(true); + expect(isAuthError(err)).toBe(true); const wrappedErr = handleAuthError(err); - expect(wrappedErr.message).to.be('Invalid authentication for monitoring cluster'); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(false); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.message).toBe('Invalid authentication for monitoring cluster'); + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(false); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 403, payload: { statusCode: 403, @@ -60,14 +59,14 @@ describe('Error handling for 401/403 errors', () => { describe('Elasticsearch errors', () => { it('handles Forbidden error defined by ElasticsearchJS', () => { const err = { statusCode: 401 }; - expect(isAuthError(err)).to.be(true); + expect(isAuthError(err)).toBe(true); const wrappedErr = handleAuthError(err); - expect(wrappedErr.message).to.be('Invalid authentication for monitoring cluster'); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(false); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.message).toBe('Invalid authentication for monitoring cluster'); + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(false); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 403, payload: { statusCode: 403, @@ -80,14 +79,14 @@ describe('Error handling for 401/403 errors', () => { it('handles Unauthorized error defined by ElasticsearchJS', () => { const err = { statusCode: 403 }; - expect(isAuthError(err)).to.be(true); + expect(isAuthError(err)).toBe(true); const wrappedErr = handleAuthError(err); - expect(wrappedErr.message).to.be('Insufficient user permissions for monitoring data'); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(false); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.message).toBe('Insufficient user permissions for monitoring data'); + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(false); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 403, payload: { statusCode: 403, diff --git a/x-pack/plugins/monitoring/server/lib/errors/__tests__/known_errors.js b/x-pack/plugins/monitoring/server/lib/errors/known_errors.test.js similarity index 70% rename from x-pack/plugins/monitoring/server/lib/errors/__tests__/known_errors.js rename to x-pack/plugins/monitoring/server/lib/errors/known_errors.test.js index 0f0156fde0290..9f4d709dc7ddc 100644 --- a/x-pack/plugins/monitoring/server/lib/errors/__tests__/known_errors.js +++ b/x-pack/plugins/monitoring/server/lib/errors/known_errors.test.js @@ -4,30 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import { errors } from 'elasticsearch'; -import { isKnownError, handleKnownError } from '../known_errors'; -import { MonitoringLicenseError } from '../custom_errors'; +import { isKnownError, handleKnownError } from './known_errors'; +import { MonitoringLicenseError } from './custom_errors'; -describe('Error handling for 503 errors', () => { +// TODO: tests were not running and are not up to date +describe.skip('Error handling for 503 errors', () => { it('ignores an unknown type', () => { const err = new errors.Generic(); - expect(isKnownError(err)).to.be(false); + expect(isKnownError(err)).toBe(false); }); it('handles ConnectionFault', () => { const err = new errors.ConnectionFault(); - expect(isKnownError(err)).to.be(true); + expect(isKnownError(err)).toBe(true); const wrappedErr = handleKnownError(err); - expect(wrappedErr.message).to.be( + expect(wrappedErr.message).toBe( 'Connection Failure: ' + 'Check the Elasticsearch Monitoring cluster network connection and refer to the Kibana logs for more information.' ); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(true); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(true); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 503, payload: { statusCode: 503, @@ -42,17 +42,17 @@ describe('Error handling for 503 errors', () => { it('handles NoConnections', () => { const err = new errors.NoConnections(); - expect(isKnownError(err)).to.be(true); + expect(isKnownError(err)).toBe(true); const wrappedErr = handleKnownError(err); - expect(wrappedErr.message).to.be( + expect(wrappedErr.message).toBe( 'No Living connections: ' + 'Check the Elasticsearch Monitoring cluster network connection and refer to the Kibana logs for more information.' ); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(true); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(true); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 503, payload: { statusCode: 503, @@ -67,17 +67,17 @@ describe('Error handling for 503 errors', () => { it('handles RequestTimeout', () => { const err = new errors.RequestTimeout(); - expect(isKnownError(err)).to.be(true); + expect(isKnownError(err)).toBe(true); const wrappedErr = handleKnownError(err); - expect(wrappedErr.message).to.be( + expect(wrappedErr.message).toBe( 'Request Timeout: ' + 'Check the Elasticsearch Monitoring cluster network connection or the load level of the nodes.' ); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(true); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(true); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 503, payload: { statusCode: 503, @@ -92,18 +92,18 @@ describe('Error handling for 503 errors', () => { it('handles the custom MonitoringLicenseError error', () => { const clusterName = 'main'; const err = new MonitoringLicenseError(clusterName); - expect(isKnownError(err)).to.be(true); + expect(isKnownError(err)).toBe(true); const wrappedErr = handleKnownError(err); - expect(wrappedErr.message).to.be( + expect(wrappedErr.message).toBe( 'Monitoring License Error: ' + `Could not find license information for cluster = '${clusterName}'. ` + `Please check the cluster's master node server logs for errors or warnings.` ); - expect(wrappedErr.isBoom).to.be(true); - expect(wrappedErr.isServer).to.be(true); - expect(wrappedErr.data).to.be(null); - expect(wrappedErr.output).to.eql({ + expect(wrappedErr.isBoom).toBe(true); + expect(wrappedErr.isServer).toBe(true); + expect(wrappedErr.data).toBe(null); + expect(wrappedErr.output).toEqual({ statusCode: 503, payload: { statusCode: 503, diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/helpers.js b/x-pack/plugins/monitoring/server/lib/helpers.js similarity index 100% rename from x-pack/plugins/monitoring/server/lib/__tests__/helpers.js rename to x-pack/plugins/monitoring/server/lib/helpers.js diff --git a/x-pack/plugins/monitoring/server/lib/kibana/__tests__/get_kibana_info.js b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.test.js similarity index 86% rename from x-pack/plugins/monitoring/server/lib/kibana/__tests__/get_kibana_info.js rename to x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.test.js index d06d9b202f80c..21a9f524700a7 100644 --- a/x-pack/plugins/monitoring/server/lib/kibana/__tests__/get_kibana_info.js +++ b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.test.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import moment from 'moment'; -import { handleResponse } from '../get_kibana_info'; +import { handleResponse } from './get_kibana_info'; describe('get_kibana_info', () => { - it('return undefined for empty response', () => { + // TODO: test was not running before and is not up to date + it.skip('return undefined for empty response', () => { const result = handleResponse({}); - expect(result).to.be(undefined); + expect(result).toBe(undefined); }); it('return mapped data for result with hits, availability = true', () => { @@ -36,7 +36,7 @@ describe('get_kibana_info', () => { ], }, }); - expect(result).to.be.eql({ + expect(result).toEqual({ availability: true, data: 123, os_memory_free: 123000, @@ -65,7 +65,7 @@ describe('get_kibana_info', () => { ], }, }); - expect(result).to.be.eql({ + expect(result).toEqual({ availability: false, data: 123, os_memory_free: 123000, diff --git a/x-pack/plugins/monitoring/server/lib/logstash/__tests__/get_node_info.js b/x-pack/plugins/monitoring/server/lib/logstash/get_node_info.test.js similarity index 91% rename from x-pack/plugins/monitoring/server/lib/logstash/__tests__/get_node_info.js rename to x-pack/plugins/monitoring/server/lib/logstash/get_node_info.test.js index 04db0d7492529..5c553197c6bca 100644 --- a/x-pack/plugins/monitoring/server/lib/logstash/__tests__/get_node_info.js +++ b/x-pack/plugins/monitoring/server/lib/logstash/get_node_info.test.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import moment from 'moment'; -import { handleResponse } from '../get_node_info'; +import { handleResponse } from './get_node_info'; describe('get_logstash_info', () => { - it('return undefined for empty response', () => { + // TODO: test was not running before and is not up to date + it.skip('return undefined for empty response', () => { const result = handleResponse({}); - expect(result).to.be(undefined); + expect(result).toBe(undefined); }); it('return mapped data for result with hits, availability = true', () => { @@ -44,7 +44,7 @@ describe('get_logstash_info', () => { ], }, }); - expect(result).to.be.eql({ + expect(result).toEqual({ host: 'myhost', availability: true, events: { @@ -90,7 +90,7 @@ describe('get_logstash_info', () => { ], }, }); - expect(result).to.be.eql({ + expect(result).toEqual({ host: 'myhost', availability: false, events: { @@ -132,7 +132,7 @@ describe('get_logstash_info', () => { ], }, }); - expect(result).to.be.eql({ + expect(result).toEqual({ host: 'myhost', availability: false, events: { diff --git a/x-pack/plugins/monitoring/server/lib/logstash/__tests__/get_pipeline.js b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline.test.js similarity index 97% rename from x-pack/plugins/monitoring/server/lib/logstash/__tests__/get_pipeline.js rename to x-pack/plugins/monitoring/server/lib/logstash/get_pipeline.test.js index 40b88b520c2b5..c5a757ae7a211 100644 --- a/x-pack/plugins/monitoring/server/lib/logstash/__tests__/get_pipeline.js +++ b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { _vertexStats, _enrichStateWithStatsAggregation } from '../get_pipeline'; +import { _vertexStats, _enrichStateWithStatsAggregation } from './get_pipeline'; describe('get_pipeline', () => { describe('_vertexStats function', () => { @@ -38,7 +37,7 @@ describe('get_pipeline', () => { totalProcessorsDurationInMillis, timeseriesIntervalInSeconds ); - expect(result).to.eql({ + expect(result).toEqual({ events_out_per_millisecond: 0.01, millis_per_event: 2, }); @@ -58,7 +57,7 @@ describe('get_pipeline', () => { totalProcessorsDurationInMillis, timeseriesIntervalInSeconds ); - expect(result).to.eql({ + expect(result).toEqual({ events_in_per_millisecond: 0.011111111111111112, events_out_per_millisecond: 0.01, millis_per_event: 1.8, @@ -81,7 +80,7 @@ describe('get_pipeline', () => { totalProcessorsDurationInMillis, timeseriesIntervalInSeconds ); - expect(result).to.eql({ + expect(result).toEqual({ events_in_per_millisecond: 0.011111111111111112, events_out_per_millisecond: 0.01, millis_per_event: 1.8, @@ -202,7 +201,7 @@ describe('get_pipeline', () => { statsAggregation, timeseriesInterval ); - expect(enrichedStateDocument).to.eql({ + expect(enrichedStateDocument).toEqual({ batch_size: 125, ephemeral_id: '2c53e689-62e8-4ef3-bc57-ea968531a848', hash: 'eada8baceee81726f6be9d0a071beefad3d9a2fd1b5f5d916011dca9fa66d081', diff --git a/x-pack/plugins/monitoring/server/lib/metrics/__test__/__snapshots__/metrics.test.js.snap b/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap similarity index 100% rename from x-pack/plugins/monitoring/server/lib/metrics/__test__/__snapshots__/metrics.test.js.snap rename to x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap diff --git a/x-pack/plugins/monitoring/server/lib/metrics/beats/__test__/cpu_utilization_calculation.test.js b/x-pack/plugins/monitoring/server/lib/metrics/beats/cpu_utilization_calculation.test.js similarity index 97% rename from x-pack/plugins/monitoring/server/lib/metrics/beats/__test__/cpu_utilization_calculation.test.js rename to x-pack/plugins/monitoring/server/lib/metrics/beats/cpu_utilization_calculation.test.js index 78b73374f2cf2..0912ea081892f 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/beats/__test__/cpu_utilization_calculation.test.js +++ b/x-pack/plugins/monitoring/server/lib/metrics/beats/cpu_utilization_calculation.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { BeatsCpuUtilizationMetric } from '../classes'; +import { BeatsCpuUtilizationMetric } from './classes'; describe('Beats CPU Utilization Metric', () => { it('should return null for invalid bucket input', () => { diff --git a/x-pack/plugins/monitoring/server/lib/metrics/classes/__test__/latency_metric_calculation.test.js b/x-pack/plugins/monitoring/server/lib/metrics/classes/latency_metric_calculation.test.js similarity index 97% rename from x-pack/plugins/monitoring/server/lib/metrics/classes/__test__/latency_metric_calculation.test.js rename to x-pack/plugins/monitoring/server/lib/metrics/classes/latency_metric_calculation.test.js index 341a542e8ddee..a882c2448003a 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/classes/__test__/latency_metric_calculation.test.js +++ b/x-pack/plugins/monitoring/server/lib/metrics/classes/latency_metric_calculation.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Metric } from '../'; +import { Metric } from '.'; describe('Latency Metric Calculation', () => { it('should return null if any operands are null', () => { diff --git a/x-pack/plugins/monitoring/server/lib/metrics/classes/__test__/quota_metric_calculation.test.js b/x-pack/plugins/monitoring/server/lib/metrics/classes/quota_metric_calculation.test.js similarity index 98% rename from x-pack/plugins/monitoring/server/lib/metrics/classes/__test__/quota_metric_calculation.test.js rename to x-pack/plugins/monitoring/server/lib/metrics/classes/quota_metric_calculation.test.js index 3201e31f720f8..a5e28d87fd3e8 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/classes/__test__/quota_metric_calculation.test.js +++ b/x-pack/plugins/monitoring/server/lib/metrics/classes/quota_metric_calculation.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { QuotaMetric } from '../'; +import { QuotaMetric } from '.'; describe('Quota Metric Calculation', () => { it('When bucket is invalid, returns undefined', () => { diff --git a/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/__test__/latency_calculation.test.js b/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/latency_calculation.test.js similarity index 97% rename from x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/__test__/latency_calculation.test.js rename to x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/latency_calculation.test.js index 3e7ef15c0b12a..685e1d52e018b 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/__test__/latency_calculation.test.js +++ b/x-pack/plugins/monitoring/server/lib/metrics/elasticsearch/latency_calculation.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LatencyMetric } from '../classes'; +import { LatencyMetric } from './classes'; describe('LatencyMetric for Query/Index Metric derivatives', () => { const getLatencyMetric = (metricType) => { diff --git a/x-pack/plugins/monitoring/server/lib/metrics/__test__/metrics.test.js b/x-pack/plugins/monitoring/server/lib/metrics/metrics.test.js similarity index 92% rename from x-pack/plugins/monitoring/server/lib/metrics/__test__/metrics.test.js rename to x-pack/plugins/monitoring/server/lib/metrics/metrics.test.js index da656c8cd2e17..1f2983b63073d 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/__test__/metrics.test.js +++ b/x-pack/plugins/monitoring/server/lib/metrics/metrics.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { metrics } from '../'; +import { metrics } from '.'; describe('Metrics', () => { it('should export metric objects that match a snapshot', () => { diff --git a/x-pack/plugins/monitoring/server/lib/__tests__/process_version_string.js b/x-pack/plugins/monitoring/server/lib/process_version_string.test.js similarity index 75% rename from x-pack/plugins/monitoring/server/lib/__tests__/process_version_string.js rename to x-pack/plugins/monitoring/server/lib/process_version_string.test.js index 729e43d2f0c76..b0be4a83fddf4 100644 --- a/x-pack/plugins/monitoring/server/lib/__tests__/process_version_string.js +++ b/x-pack/plugins/monitoring/server/lib/process_version_string.test.js @@ -4,20 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { normalizeVersionString } from '../normalize_version_string'; +import { normalizeVersionString } from './normalize_version_string'; describe('Normalizing Version String', () => { it('Returns version string when valid', () => { const result = normalizeVersionString('1.2.30'); - expect(result).to.be('1.2.30'); + expect(result).toBe('1.2.30'); }); it('Strips -SNAPSHOT from a valid string', () => { const result = normalizeVersionString('1.2.30-SNAPSHOT'); - expect(result).to.be('1.2.30'); + expect(result).toBe('1.2.30'); }); it('Returns empty string when invalid', () => { const result = normalizeVersionString('foo-SNAPSHOT'); - expect(result).to.be(''); + expect(result).toBe(''); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/setup/collection/__test__/get_collection_status.test.js b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.js similarity index 72% rename from x-pack/plugins/monitoring/server/lib/setup/collection/__test__/get_collection_status.test.js rename to x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.js index 083ebfb27fd51..f537fbbb7c57c 100644 --- a/x-pack/plugins/monitoring/server/lib/setup/collection/__test__/get_collection_status.test.js +++ b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.js @@ -4,10 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import { getCollectionStatus } from '..'; -import { getIndexPatterns } from '../../../cluster/get_index_patterns'; +import { getCollectionStatus } from '.'; +import { getIndexPatterns } from '../../cluster/get_index_patterns'; const liveClusterUuid = 'a12'; const mockReq = ( @@ -31,7 +29,11 @@ const mockReq = ( }, config() { return { - get: sinon.stub().withArgs('server.uuid').returns('kibana-1234'), + get: jest.fn((prop) => { + if (prop === 'server.uuid') { + return 'kibana-1234'; + } + }), }; }, usage: { @@ -125,25 +127,25 @@ describe('getCollectionStatus', () => { const result = await getCollectionStatus(req, getIndexPatterns(req.server)); - expect(result.kibana.totalUniqueInstanceCount).to.be(1); - expect(result.kibana.totalUniqueFullyMigratedCount).to.be(0); - expect(result.kibana.byUuid.kibana_1.isInternalCollector).to.be(true); + expect(result.kibana.totalUniqueInstanceCount).toBe(1); + expect(result.kibana.totalUniqueFullyMigratedCount).toBe(0); + expect(result.kibana.byUuid.kibana_1.isInternalCollector).toBe(true); - expect(result.beats.totalUniqueInstanceCount).to.be(1); - expect(result.beats.totalUniqueFullyMigratedCount).to.be(0); - expect(result.beats.byUuid.beats_1.isInternalCollector).to.be(true); + expect(result.beats.totalUniqueInstanceCount).toBe(1); + expect(result.beats.totalUniqueFullyMigratedCount).toBe(0); + expect(result.beats.byUuid.beats_1.isInternalCollector).toBe(true); - expect(result.apm.totalUniqueInstanceCount).to.be(1); - expect(result.apm.totalUniqueFullyMigratedCount).to.be(0); - expect(result.apm.byUuid.apm_1.isInternalCollector).to.be(true); + expect(result.apm.totalUniqueInstanceCount).toBe(1); + expect(result.apm.totalUniqueFullyMigratedCount).toBe(0); + expect(result.apm.byUuid.apm_1.isInternalCollector).toBe(true); - expect(result.logstash.totalUniqueInstanceCount).to.be(1); - expect(result.logstash.totalUniqueFullyMigratedCount).to.be(0); - expect(result.logstash.byUuid.logstash_1.isInternalCollector).to.be(true); + expect(result.logstash.totalUniqueInstanceCount).toBe(1); + expect(result.logstash.totalUniqueFullyMigratedCount).toBe(0); + expect(result.logstash.byUuid.logstash_1.isInternalCollector).toBe(true); - expect(result.elasticsearch.totalUniqueInstanceCount).to.be(1); - expect(result.elasticsearch.totalUniqueFullyMigratedCount).to.be(0); - expect(result.elasticsearch.byUuid.es_1.isInternalCollector).to.be(true); + expect(result.elasticsearch.totalUniqueInstanceCount).toBe(1); + expect(result.elasticsearch.totalUniqueFullyMigratedCount).toBe(0); + expect(result.elasticsearch.byUuid.es_1.isInternalCollector).toBe(true); }); it('should handle some stack products as fully migrated', async () => { @@ -174,21 +176,21 @@ describe('getCollectionStatus', () => { const result = await getCollectionStatus(req, getIndexPatterns(req.server)); - expect(result.kibana.totalUniqueInstanceCount).to.be(1); - expect(result.kibana.totalUniqueFullyMigratedCount).to.be(1); - expect(result.kibana.byUuid.kibana_1.isFullyMigrated).to.be(true); + expect(result.kibana.totalUniqueInstanceCount).toBe(1); + expect(result.kibana.totalUniqueFullyMigratedCount).toBe(1); + expect(result.kibana.byUuid.kibana_1.isFullyMigrated).toBe(true); - expect(result.beats.totalUniqueInstanceCount).to.be(1); - expect(result.beats.totalUniqueFullyMigratedCount).to.be(0); - expect(result.beats.byUuid.beats_1.isInternalCollector).to.be(true); + expect(result.beats.totalUniqueInstanceCount).toBe(1); + expect(result.beats.totalUniqueFullyMigratedCount).toBe(0); + expect(result.beats.byUuid.beats_1.isInternalCollector).toBe(true); - expect(result.logstash.totalUniqueInstanceCount).to.be(1); - expect(result.logstash.totalUniqueFullyMigratedCount).to.be(0); - expect(result.logstash.byUuid.logstash_1.isInternalCollector).to.be(true); + expect(result.logstash.totalUniqueInstanceCount).toBe(1); + expect(result.logstash.totalUniqueFullyMigratedCount).toBe(0); + expect(result.logstash.byUuid.logstash_1.isInternalCollector).toBe(true); - expect(result.elasticsearch.totalUniqueInstanceCount).to.be(1); - expect(result.elasticsearch.totalUniqueFullyMigratedCount).to.be(1); - expect(result.elasticsearch.byUuid.es_1.isFullyMigrated).to.be(true); + expect(result.elasticsearch.totalUniqueInstanceCount).toBe(1); + expect(result.elasticsearch.totalUniqueFullyMigratedCount).toBe(1); + expect(result.elasticsearch.byUuid.es_1.isFullyMigrated).toBe(true); }); it('should handle some stack products as partially migrated', async () => { @@ -223,44 +225,44 @@ describe('getCollectionStatus', () => { const result = await getCollectionStatus(req, getIndexPatterns(req.server)); - expect(result.kibana.totalUniqueInstanceCount).to.be(2); - expect(result.kibana.totalUniqueFullyMigratedCount).to.be(1); - expect(result.kibana.byUuid.kibana_1.isPartiallyMigrated).to.be(true); - expect(result.kibana.byUuid.kibana_1.lastInternallyCollectedTimestamp).to.be(12); + expect(result.kibana.totalUniqueInstanceCount).toBe(2); + expect(result.kibana.totalUniqueFullyMigratedCount).toBe(1); + expect(result.kibana.byUuid.kibana_1.isPartiallyMigrated).toBe(true); + expect(result.kibana.byUuid.kibana_1.lastInternallyCollectedTimestamp).toBe(12); - expect(result.beats.totalUniqueInstanceCount).to.be(1); - expect(result.beats.totalUniqueFullyMigratedCount).to.be(0); - expect(result.beats.byUuid.beats_1.isInternalCollector).to.be(true); + expect(result.beats.totalUniqueInstanceCount).toBe(1); + expect(result.beats.totalUniqueFullyMigratedCount).toBe(0); + expect(result.beats.byUuid.beats_1.isInternalCollector).toBe(true); - expect(result.logstash.totalUniqueInstanceCount).to.be(1); - expect(result.logstash.totalUniqueFullyMigratedCount).to.be(0); - expect(result.logstash.byUuid.logstash_1.isInternalCollector).to.be(true); + expect(result.logstash.totalUniqueInstanceCount).toBe(1); + expect(result.logstash.totalUniqueFullyMigratedCount).toBe(0); + expect(result.logstash.byUuid.logstash_1.isInternalCollector).toBe(true); - expect(result.elasticsearch.totalUniqueInstanceCount).to.be(1); - expect(result.elasticsearch.totalUniqueFullyMigratedCount).to.be(1); - expect(result.elasticsearch.byUuid.es_1.isFullyMigrated).to.be(true); + expect(result.elasticsearch.totalUniqueInstanceCount).toBe(1); + expect(result.elasticsearch.totalUniqueFullyMigratedCount).toBe(1); + expect(result.elasticsearch.byUuid.es_1.isFullyMigrated).toBe(true); }); it('should detect products based on other indices', async () => { const req = mockReq({ hits: { total: { value: 1 } } }); const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); - expect(result.kibana.detected.doesExist).to.be(true); - expect(result.elasticsearch.detected.doesExist).to.be(true); - expect(result.beats.detected.mightExist).to.be(true); - expect(result.logstash.detected.mightExist).to.be(true); + expect(result.kibana.detected.doesExist).toBe(true); + expect(result.elasticsearch.detected.doesExist).toBe(true); + expect(result.beats.detected.mightExist).toBe(true); + expect(result.logstash.detected.mightExist).toBe(true); }); it('should work properly when security is disabled', async () => { const req = mockReq({ hits: { total: { value: 1 } } }, false); const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); - expect(result.kibana.detected.doesExist).to.be(true); + expect(result.kibana.detected.doesExist).toBe(true); }); it('should work properly with an unknown security message', async () => { const req = mockReq({ hits: { total: { value: 1 } } }, true, true, 'foobar'); const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); - expect(result._meta.hasPermissions).to.be(false); + expect(result._meta.hasPermissions).toBe(false); }); it('should work properly with a known security message', async () => { @@ -271,7 +273,7 @@ describe('getCollectionStatus', () => { 'no handler found for uri [/_security/user/_has_privileges] and method [POST]' ); const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); - expect(result.kibana.detected.doesExist).to.be(true); + expect(result.kibana.detected.doesExist).toBe(true); }); it('should work properly with another known security message', async () => { @@ -282,12 +284,12 @@ describe('getCollectionStatus', () => { 'Invalid index name [_security]' ); const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); - expect(result.kibana.detected.doesExist).to.be(true); + expect(result.kibana.detected.doesExist).toBe(true); }); it('should not work if the user does not have the necessary permissions', async () => { const req = mockReq({ hits: { total: { value: 1 } } }, true, false); const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); - expect(result._meta.hasPermissions).to.be(false); + expect(result._meta.hasPermissions).toBe(false); }); }); From 8cb622617c4a2b16037b29bc16e9e9d83d66a349 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 7 Jan 2021 10:47:52 -0700 Subject: [PATCH 05/24] [Maps] fix multi-select query from Controls visualization not always getting applied to map in dashboard (#87310) * [Maps] fix multi-select query from Controls visualization not always getting applied to map in dashboard * fix underlying problem in blended layer * clean-up Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/public/actions/data_request_actions.ts | 6 +++++- .../layers/blended_vector_layer/blended_vector_layer.ts | 7 +++++-- .../maps/public/classes/sources/es_source/es_source.ts | 6 +++++- x-pack/plugins/maps/public/embeddable/map_embeddable.tsx | 4 +++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts index 2c59424ec174b..eeb2990e8c658 100644 --- a/x-pack/plugins/maps/public/actions/data_request_actions.ts +++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts @@ -227,11 +227,15 @@ function endDataLoad( data: object, meta: DataMeta ) { - return async ( + return ( dispatch: ThunkDispatch, getState: () => MapStoreState ) => { dispatch(unregisterCancelCallback(requestToken)); + const dataRequest = getDataRequestDescriptor(getState(), layerId, dataId); + if (dataRequest && dataRequest.dataRequestToken !== requestToken) { + throw new DataRequestAbortError(); + } const features = data && 'features' in data ? (data as FeatureCollection).features : []; diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts index fdd8a1e898b6e..825f6ed74777a 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts @@ -41,6 +41,7 @@ import { import { IVectorSource } from '../../sources/vector_source'; import { LICENSED_FEATURES } from '../../../licensed_features'; import { ESSearchSource } from '../../sources/es_search_source/es_search_source'; +import { isSearchSourceAbortError } from '../../sources/es_source/es_source'; const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID'; @@ -311,14 +312,16 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { let isSyncClustered; try { syncContext.startLoading(dataRequestId, requestToken, searchFilters); + const abortController = new AbortController(); + syncContext.registerCancelCallback(requestToken, () => abortController.abort()); const searchSource = await this._documentSource.makeSearchSource(searchFilters, 0); - const resp = await searchSource.fetch(); + const resp = await searchSource.fetch({ abortSignal: abortController.signal }); const maxResultWindow = await this._documentSource.getMaxResultWindow(); isSyncClustered = resp.hits.total > maxResultWindow; const countData = { isSyncClustered } as CountData; syncContext.stopLoading(dataRequestId, requestToken, countData, searchFilters); } catch (error) { - if (!(error instanceof DataRequestAbortError)) { + if (!(error instanceof DataRequestAbortError) || !isSearchSourceAbortError(error)) { syncContext.onLoadError(dataRequestId, requestToken, error.message); } return; diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts index 103fd11263330..967131e900fc6 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts @@ -40,6 +40,10 @@ import { } from '../../../../../../../src/plugins/inspector/common/adapters'; import { isValidStringConfig } from '../../util/valid_string_config'; +export function isSearchSourceAbortError(error: Error) { + return error.name === 'AbortError'; +} + export interface IESSource extends IVectorSource { isESSource(): true; getId(): string; @@ -191,7 +195,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource if (inspectorRequest) { inspectorRequest.error(error); } - if (error.name === 'AbortError') { + if (isSearchSourceAbortError(error)) { throw new DataRequestAbortError(); } diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 1848f841c771b..5448043b35ba8 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -104,7 +104,9 @@ export class MapEmbeddable this._savedMap = new SavedMap({ mapEmbeddableInput: initialInput }); this._initializeSaveMap(); - this._subscription = this.getInput$().subscribe((input) => this.onContainerStateChanged(input)); + this._subscription = this.getUpdated$().subscribe(() => + this.onContainerStateChanged(this.input) + ); } private async _initializeSaveMap() { From 0ac6e6223e16836237f300d17a74cc1f8df1ea47 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 7 Jan 2021 11:10:20 -0700 Subject: [PATCH 06/24] [Maps] labels for polygons and lines (#86191) * [Maps] labels for polygons and lines * remove x-pack yarn.lock * add labels to choropleth map wizard * clean up comment * add mvt tile support * only add centroids if there may be lines or polygons * tslint * tslint * do not add centroid to too many features polygon * update get_tile expect statements * move turf dependencies from devDependencies to dependencies * update jest snapshot and functional test expects * fix functional test expect * another functional test expect update * functional test updates * expect * pew pew source expect updates * update joins expect * update mapbox style expects * update join visibility expects for geocentroids * update join visibility expects for geocentroids * another functional test expect update * review feedback * update yarn.lock * tslint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 14 +- x-pack/plugins/maps/common/constants.ts | 5 + .../maps/common/get_centroid_features.test.ts | 282 ++++++++++++ .../maps/common/get_centroid_features.ts | 88 ++++ .../create_choropleth_layer_descriptor.ts | 10 + .../tiled_vector_layer/tiled_vector_layer.tsx | 1 + .../layers/vector_layer/vector_layer.tsx | 47 ++ .../vector_style_editor.test.tsx.snap | 400 ++++++++++++++++++ .../vector/components/vector_style_editor.tsx | 6 + .../styles/vector/vector_style.test.js | 8 + .../classes/styles/vector/vector_style.tsx | 7 + .../classes/util/mb_filter_expressions.ts | 19 +- .../plugins/maps/server/mvt/get_tile.test.ts | 34 +- x-pack/plugins/maps/server/mvt/get_tile.ts | 2 + .../apps/maps/es_geo_grid_source.js | 28 +- .../functional/apps/maps/es_pew_pew_source.js | 2 +- x-pack/test/functional/apps/maps/joins.js | 26 +- .../functional/apps/maps/mapbox_styles.js | 3 + yarn.lock | 154 ++++++- 19 files changed, 1098 insertions(+), 38 deletions(-) create mode 100644 x-pack/plugins/maps/common/get_centroid_features.test.ts create mode 100644 x-pack/plugins/maps/common/get_centroid_features.ts diff --git a/package.json b/package.json index b1c56b8529dd1..368985e246891 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,16 @@ "@loaders.gl/json": "^2.3.1", "@slack/webhook": "^5.0.0", "@storybook/addons": "^6.0.16", + "@turf/along": "6.0.1", + "@turf/area": "6.0.1", + "@turf/bbox": "6.0.1", + "@turf/bbox-polygon": "6.0.1", + "@turf/boolean-contains": "6.0.1", + "@turf/center-of-mass": "6.0.1", "@turf/circle": "6.0.1", + "@turf/distance": "6.0.1", + "@turf/helpers": "6.0.1", + "@turf/length": "^6.0.2", "JSONStream": "1.3.5", "abort-controller": "^3.0.0", "abortcontroller-polyfill": "^1.4.0", @@ -399,11 +408,6 @@ "@testing-library/react": "^11.0.4", "@testing-library/react-hooks": "^3.4.1", "@testing-library/user-event": "^12.1.6", - "@turf/bbox": "6.0.1", - "@turf/bbox-polygon": "6.0.1", - "@turf/boolean-contains": "6.0.1", - "@turf/distance": "6.0.1", - "@turf/helpers": "6.0.1", "@types/accept": "3.1.1", "@types/angular": "^1.6.56", "@types/angular-mocks": "^1.7.0", diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 6a7448ddc8448..b86d48bfccdab 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -43,8 +43,13 @@ export const API_ROOT_PATH = `/${GIS_API_PATH}`; export const MVT_GETTILE_API_PATH = 'mvt/getTile'; export const MVT_GETGRIDTILE_API_PATH = 'mvt/getGridTile'; export const MVT_SOURCE_LAYER_NAME = 'source_layer'; +// Identifies vector tile "too many features" feature. +// "too many features" feature is a box showing area that contains too many features for single ES search response export const KBN_TOO_MANY_FEATURES_PROPERTY = '__kbn_too_many_features__'; export const KBN_TOO_MANY_FEATURES_IMAGE_ID = '__kbn_too_many_features_image_id__'; +// Identifies centroid feature. +// Centroids are a single point for representing lines, multiLines, polygons, and multiPolygons +export const KBN_IS_CENTROID_FEATURE = '__kbn_is_centroid_feature__'; const MAP_BASE_URL = `/${MAPS_APP_PATH}/${MAP_PATH}`; export function getNewMapPath() { diff --git a/x-pack/plugins/maps/common/get_centroid_features.test.ts b/x-pack/plugins/maps/common/get_centroid_features.test.ts new file mode 100644 index 0000000000000..e7250203ac3b8 --- /dev/null +++ b/x-pack/plugins/maps/common/get_centroid_features.test.ts @@ -0,0 +1,282 @@ +/* + * 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 { Feature, FeatureCollection } from 'geojson'; +import { getCentroidFeatures } from './get_centroid_features'; + +test('should not create centroid feature for point and multipoint', () => { + const pointFeature: Feature = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [30, 10], + }, + properties: { + prop0: 'value0', + prop1: 0.0, + }, + }; + const multiPointFeature: Feature = { + type: 'Feature', + geometry: { + type: 'MultiPoint', + coordinates: [ + [10, 40], + [40, 30], + [20, 20], + [30, 10], + ], + }, + properties: { + prop0: 'value0', + prop1: 0.0, + }, + }; + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [pointFeature, multiPointFeature], + }; + const centroidFeatures = getCentroidFeatures(featureCollection); + expect(centroidFeatures.length).toBe(0); +}); + +test('should not create centroid for too many features polygon', () => { + const polygonFeature: Feature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [35, 10], + [45, 45], + [15, 40], + [10, 20], + [35, 10], + ], + ], + }, + properties: { + __kbn_too_many_features__: true, + prop0: 'value0', + prop1: 0.0, + }, + }; + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [polygonFeature], + }; + const centroidFeatures = getCentroidFeatures(featureCollection); + expect(centroidFeatures.length).toBe(0); +}); + +test('should create centroid feature for line (even number of points)', () => { + const lineFeature: Feature = { + type: 'Feature', + id: 'myfeature', + geometry: { + type: 'LineString', + coordinates: [ + [102.0, 0.0], + [103.0, 1.0], + [104.0, 0.0], + [105.0, 1.0], + ], + }, + properties: { + prop0: 'value0', + prop1: 0.0, + }, + }; + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [lineFeature], + }; + const centroidFeatures = getCentroidFeatures(featureCollection); + expect(centroidFeatures.length).toBe(1); + expect(centroidFeatures[0]).toEqual({ + type: 'Feature', + id: 'myfeature', + geometry: { + type: 'Point', + coordinates: [103.50003808007737, 0.5000190382261022], + }, + properties: { + __kbn_is_centroid_feature__: true, + prop0: 'value0', + prop1: 0.0, + }, + }); +}); + +test('should create centroid feature for line (odd number of points)', () => { + const lineFeature: Feature = { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [ + [102.0, 0.0], + [103.0, 1.0], + [104.0, 0.0], + ], + }, + properties: { + prop0: 'value0', + prop1: 0.0, + }, + }; + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [lineFeature], + }; + const centroidFeatures = getCentroidFeatures(featureCollection); + expect(centroidFeatures.length).toBe(1); + expect(centroidFeatures[0]).toEqual({ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [103.0, 1.0], + }, + properties: { + __kbn_is_centroid_feature__: true, + prop0: 'value0', + prop1: 0.0, + }, + }); +}); + +test('should create centroid feature for multi line', () => { + const multiLineFeature: Feature = { + type: 'Feature', + geometry: { + type: 'MultiLineString', + coordinates: [ + [ + [10, 10], + [20, 20], + [10, 40], + ], + [ + [40, 40], + [30, 30], + [40, 20], + [30, 10], + ], + ], + }, + properties: { + prop0: 'value0', + prop1: 0.0, + }, + }; + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [multiLineFeature], + }; + const centroidFeatures = getCentroidFeatures(featureCollection); + expect(centroidFeatures.length).toBe(1); + expect(centroidFeatures[0]).toEqual({ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [35.56701982106548, 24.717594944805672], + }, + properties: { + __kbn_is_centroid_feature__: true, + prop0: 'value0', + prop1: 0.0, + }, + }); +}); + +test('should create centroid feature for polygon', () => { + const polygonFeature: Feature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [35, 10], + [45, 45], + [15, 40], + [10, 20], + [35, 10], + ], + ], + }, + properties: { + prop0: 'value0', + prop1: 0.0, + }, + }; + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [polygonFeature], + }; + const centroidFeatures = getCentroidFeatures(featureCollection); + expect(centroidFeatures.length).toBe(1); + expect(centroidFeatures[0]).toEqual({ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [27.526881720430108, 28.70967741935484], + }, + properties: { + __kbn_is_centroid_feature__: true, + prop0: 'value0', + prop1: 0.0, + }, + }); +}); + +test('should create centroid feature for multi polygon', () => { + const multiPolygonFeature: Feature = { + type: 'Feature', + geometry: { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [30, 20], + [45, 40], + [10, 40], + [30, 20], + ], + ], + [ + [ + [15, 5], + [40, 10], + [10, 20], + [5, 10], + [15, 5], + ], + ], + ], + }, + properties: { + prop0: 'value0', + prop1: 0.0, + }, + }; + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features: [multiPolygonFeature], + }; + const centroidFeatures = getCentroidFeatures(featureCollection); + expect(centroidFeatures.length).toBe(1); + expect(centroidFeatures[0]).toEqual({ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [28.333333333333332, 33.333333333333336], + }, + properties: { + __kbn_is_centroid_feature__: true, + prop0: 'value0', + prop1: 0.0, + }, + }); +}); diff --git a/x-pack/plugins/maps/common/get_centroid_features.ts b/x-pack/plugins/maps/common/get_centroid_features.ts new file mode 100644 index 0000000000000..9b49b1f7653dc --- /dev/null +++ b/x-pack/plugins/maps/common/get_centroid_features.ts @@ -0,0 +1,88 @@ +/* + * 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 { + Feature, + FeatureCollection, + Geometry, + LineString, + MultiLineString, + MultiPolygon, +} from 'geojson'; +import turfAlong from '@turf/along'; +import turfArea from '@turf/area'; +// @ts-expect-error +import turfCenterOfMass from '@turf/center-of-mass'; +import turfLength from '@turf/length'; +import { lineString, polygon } from '@turf/helpers'; +import { + GEO_JSON_TYPE, + KBN_IS_CENTROID_FEATURE, + KBN_TOO_MANY_FEATURES_PROPERTY, +} from './constants'; + +export function getCentroidFeatures(featureCollection: FeatureCollection): Feature[] { + const centroidFeatures = []; + for (let i = 0; i < featureCollection.features.length; i++) { + const feature = featureCollection.features[i]; + + // do not add centroid for kibana added features + if (feature.properties?.[KBN_TOO_MANY_FEATURES_PROPERTY]) { + continue; + } + + let centroidGeometry: Geometry | null = null; + if (feature.geometry.type === GEO_JSON_TYPE.LINE_STRING) { + centroidGeometry = getLineCentroid(feature); + } else if (feature.geometry.type === GEO_JSON_TYPE.MULTI_LINE_STRING) { + const coordinates = (feature.geometry as MultiLineString).coordinates; + let longestLine = coordinates[0]; + let longestLength = turfLength(lineString(longestLine)); + for (let j = 1; j < coordinates.length; j++) { + const nextLine = coordinates[j]; + const nextLength = turfLength(lineString(nextLine)); + if (nextLength > longestLength) { + longestLine = nextLine; + longestLength = nextLength; + } + } + centroidGeometry = getLineCentroid(lineString(longestLine) as Feature); + } else if (feature.geometry.type === GEO_JSON_TYPE.POLYGON) { + centroidGeometry = turfCenterOfMass(feature).geometry; + } else if (feature.geometry.type === GEO_JSON_TYPE.MULTI_POLYGON) { + const coordinates = (feature.geometry as MultiPolygon).coordinates; + let largestPolygon = coordinates[0]; + let largestArea = turfArea(polygon(largestPolygon)); + for (let j = 1; j < coordinates.length; j++) { + const nextPolygon = coordinates[j]; + const nextArea = turfArea(polygon(nextPolygon)); + if (nextArea > largestArea) { + largestPolygon = nextPolygon; + largestArea = nextArea; + } + } + centroidGeometry = turfCenterOfMass(polygon(largestPolygon)).geometry; + } + + if (centroidGeometry) { + centroidFeatures.push({ + type: 'Feature', + id: feature.id, + properties: { + ...feature.properties, + [KBN_IS_CENTROID_FEATURE]: true, + }, + geometry: centroidGeometry, + } as Feature); + } + } + return centroidFeatures; +} + +function getLineCentroid(feature: Feature): Geometry { + const length = turfLength(feature); + return turfAlong((feature as unknown) as LineString, length / 2).geometry!; +} diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts index fa82b9dc3b542..63834d5685e78 100644 --- a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts @@ -86,6 +86,16 @@ function createChoroplethLayerDescriptor({ color: '#3d3d3d', }, }, + [VECTOR_STYLES.LABEL_TEXT]: { + type: STYLE_TYPE.DYNAMIC, + options: { + ...defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT].options, + field: { + name: joinKey, + origin: FIELD_ORIGIN.JOIN, + }, + }, + }, }), }); } diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx index 95a452c7ce376..5f2771ea2ffed 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx @@ -147,6 +147,7 @@ export class TiledVectorLayer extends VectorLayer { this._setMbPointsProperties(mbMap, sourceMeta.layerName); this._setMbLinePolygonProperties(mbMap, sourceMeta.layerName); + this._setMbCentroidProperties(mbMap, sourceMeta.layerName); } _requiresPrevSourceCleanup(mbMap: MbMap): boolean { diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index add5a980258f3..f72d2c0e6ead9 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -12,6 +12,7 @@ import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { AbstractLayer } from '../layer'; import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style'; +import { getCentroidFeatures } from '../../../../common/get_centroid_features'; import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_REQUEST_ID, @@ -26,6 +27,7 @@ import { LAYER_STYLE_TYPE, KBN_TOO_MANY_FEATURES_IMAGE_ID, FieldFormatter, + VECTOR_SHAPE_TYPE, } from '../../../../common/constants'; import { JoinTooltipProperty } from '../../tooltips/join_tooltip_property'; import { DataRequestAbortError } from '../../util/data_request'; @@ -37,6 +39,7 @@ import { import { assignFeatureIds } from '../../util/assign_feature_ids'; import { getFeatureCollectionBounds } from '../../util/get_feature_collection_bounds'; import { + getCentroidFilterExpression, getFillFilterExpression, getLineFilterExpression, getPointFilterExpression, @@ -519,6 +522,13 @@ export class VectorLayer extends AbstractLayer { } ); const layerFeatureCollection = assignFeatureIds(sourceFeatureCollection); + const supportedShapes = await source.getSupportedShapeTypes(); + if ( + supportedShapes.includes(VECTOR_SHAPE_TYPE.LINE) || + supportedShapes.includes(VECTOR_SHAPE_TYPE.POLYGON) + ) { + layerFeatureCollection.features.push(...getCentroidFeatures(layerFeatureCollection)); + } stopLoading(dataRequestId, requestToken, layerFeatureCollection, meta); return { refreshed: true, @@ -995,9 +1005,41 @@ export class VectorLayer extends AbstractLayer { mbMap.setLayerZoomRange(tooManyFeaturesLayerId, this.getMinZoom(), this.getMaxZoom()); } + _setMbCentroidProperties(mbMap: MbMap, mvtSourceLayer?: string) { + const centroidLayerId = this._getMbCentroidLayerId(); + const centroidLayer = mbMap.getLayer(centroidLayerId); + if (!centroidLayer) { + const mbLayer: MbLayer = { + id: centroidLayerId, + type: 'symbol', + source: this.getId(), + }; + if (mvtSourceLayer) { + mbLayer['source-layer'] = mvtSourceLayer; + } + mbMap.addLayer(mbLayer); + } + + const filterExpr = getCentroidFilterExpression(this.hasJoins()); + if (filterExpr !== mbMap.getFilter(centroidLayerId)) { + mbMap.setFilter(centroidLayerId, filterExpr); + } + + this.getCurrentStyle().setMBPropertiesForLabelText({ + alpha: this.getAlpha(), + mbMap, + textLayerId: centroidLayerId, + }); + + this.syncVisibilityWithMb(mbMap, centroidLayerId); + mbMap.setLayerZoomRange(centroidLayerId, this.getMinZoom(), this.getMaxZoom()); + } + _syncStylePropertiesWithMb(mbMap: MbMap) { this._setMbPointsProperties(mbMap); this._setMbLinePolygonProperties(mbMap); + // centroid layers added after polygon layers to ensure they are on top of polygon layers + this._setMbCentroidProperties(mbMap); } _syncSourceBindingWithMb(mbMap: MbMap) { @@ -1037,6 +1079,10 @@ export class VectorLayer extends AbstractLayer { return this.makeMbLayerId('text'); } + _getMbCentroidLayerId() { + return this.makeMbLayerId('centroid'); + } + _getMbSymbolLayerId() { return this.makeMbLayerId('symbol'); } @@ -1057,6 +1103,7 @@ export class VectorLayer extends AbstractLayer { return [ this._getMbPointLayerId(), this._getMbTextLayerId(), + this._getMbCentroidLayerId(), this._getMbSymbolLayerId(), this._getMbLineLayerId(), this._getMbPolygonLayerId(), diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/__snapshots__/vector_style_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/styles/vector/components/__snapshots__/vector_style_editor.test.tsx.snap index 312f8e5d91ffa..be8c9b0750b94 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/__snapshots__/vector_style_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/__snapshots__/vector_style_editor.test.tsx.snap @@ -151,6 +151,221 @@ exports[`should render 1`] = ` } } /> + + + + + + + + + + + + + + + + + + + + + + { {this._renderLineWidth()} + + + {this._renderLabelProperties()} ); } @@ -481,6 +484,9 @@ export class VectorStyleEditor extends Component { {this._renderLineWidth()} + + + {this._renderLabelProperties()} ); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js index 94090c8abfe4f..acbf2cc8e72ba 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js @@ -221,6 +221,14 @@ describe('pluckStyleMetaFromSourceDataRequest', () => { }, properties: {}, }, + { + geometry: { + type: 'Point', + }, + properties: { + __kbn_is_centroid_feature__: true, + }, + }, ], }, }); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx index 1c36961aae1b1..9bf4cafd66407 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx @@ -14,6 +14,7 @@ import { DEFAULT_ICON, FIELD_ORIGIN, GEO_JSON_TYPE, + KBN_IS_CENTROID_FEATURE, LAYER_STYLE_TYPE, SOURCE_FORMATTERS_DATA_REQUEST_ID, STYLE_TYPE, @@ -493,6 +494,12 @@ export class VectorStyle implements IVectorStyle { if (supportedFeatures.length > 1) { for (let i = 0; i < features.length; i++) { const feature = features[i]; + + // ignore centroid features as they are added for styling and not part of the real data set + if (feature.properties[KBN_IS_CENTROID_FEATURE]) { + continue; + } + if (!hasFeatureType[VECTOR_SHAPE_TYPE.POINT] && POINTS.includes(feature.geometry.type)) { hasFeatureType[VECTOR_SHAPE_TYPE.POINT] = true; } diff --git a/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts b/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts index 0da6f632eb4a8..5b82305cd84a1 100644 --- a/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts +++ b/x-pack/plugins/maps/public/classes/util/mb_filter_expressions.ts @@ -7,16 +7,19 @@ import { GEO_JSON_TYPE, FEATURE_VISIBLE_PROPERTY_NAME, + KBN_IS_CENTROID_FEATURE, KBN_TOO_MANY_FEATURES_PROPERTY, } from '../../../common/constants'; export const EXCLUDE_TOO_MANY_FEATURES_BOX = ['!=', ['get', KBN_TOO_MANY_FEATURES_PROPERTY], true]; +const EXCLUDE_CENTROID_FEATURES = ['!=', ['get', KBN_IS_CENTROID_FEATURE], true]; const VISIBILITY_FILTER_CLAUSE = ['all', ['==', ['get', FEATURE_VISIBLE_PROPERTY_NAME], true]]; -const TOO_MANY_FEATURES_FILTER = ['all', EXCLUDE_TOO_MANY_FEATURES_BOX]; +// Kibana features are features added by kibana that do not exist in real data +const EXCLUDE_KBN_FEATURES = ['all', EXCLUDE_TOO_MANY_FEATURES_BOX, EXCLUDE_CENTROID_FEATURES]; const CLOSED_SHAPE_MB_FILTER = [ - ...TOO_MANY_FEATURES_FILTER, + ...EXCLUDE_KBN_FEATURES, [ 'any', ['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON], @@ -27,7 +30,7 @@ const CLOSED_SHAPE_MB_FILTER = [ const VISIBLE_CLOSED_SHAPE_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, CLOSED_SHAPE_MB_FILTER]; const ALL_SHAPE_MB_FILTER = [ - ...TOO_MANY_FEATURES_FILTER, + ...EXCLUDE_KBN_FEATURES, [ 'any', ['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON], @@ -40,7 +43,7 @@ const ALL_SHAPE_MB_FILTER = [ const VISIBLE_ALL_SHAPE_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, ALL_SHAPE_MB_FILTER]; const POINT_MB_FILTER = [ - ...TOO_MANY_FEATURES_FILTER, + ...EXCLUDE_KBN_FEATURES, [ 'any', ['==', ['geometry-type'], GEO_JSON_TYPE.POINT], @@ -50,6 +53,10 @@ const POINT_MB_FILTER = [ const VISIBLE_POINT_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, POINT_MB_FILTER]; +const CENTROID_MB_FILTER = ['all', ['==', ['get', KBN_IS_CENTROID_FEATURE], true]]; + +const VISIBLE_CENTROID_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, CENTROID_MB_FILTER]; + export function getFillFilterExpression(hasJoins: boolean): unknown[] { return hasJoins ? VISIBLE_CLOSED_SHAPE_MB_FILTER : CLOSED_SHAPE_MB_FILTER; } @@ -61,3 +68,7 @@ export function getLineFilterExpression(hasJoins: boolean): unknown[] { export function getPointFilterExpression(hasJoins: boolean): unknown[] { return hasJoins ? VISIBLE_POINT_MB_FILTER : POINT_MB_FILTER; } + +export function getCentroidFilterExpression(hasJoins: boolean): unknown[] { + return hasJoins ? VISIBLE_CENTROID_MB_FILTER : CENTROID_MB_FILTER; +} diff --git a/x-pack/plugins/maps/server/mvt/get_tile.test.ts b/x-pack/plugins/maps/server/mvt/get_tile.test.ts index 3660039f2513c..634b898fdc18c 100644 --- a/x-pack/plugins/maps/server/mvt/get_tile.test.ts +++ b/x-pack/plugins/maps/server/mvt/get_tile.test.ts @@ -7,7 +7,12 @@ import { getGridTile, getTile } from './get_tile'; import { TILE_GRIDAGGS, TILE_SEARCHES } from './__tests__/tile_es_responses'; import { Logger } from 'src/core/server'; -import { ES_GEO_FIELD_TYPE, MVT_SOURCE_LAYER_NAME, RENDER_AS } from '../../common/constants'; +import { + ES_GEO_FIELD_TYPE, + KBN_IS_CENTROID_FEATURE, + MVT_SOURCE_LAYER_NAME, + RENDER_AS, +} from '../../common/constants'; // @ts-expect-error import { VectorTile, VectorTileLayer } from '@mapbox/vector-tile'; @@ -18,7 +23,6 @@ interface ITileLayerJsonExpectation { version: number; name: string; extent: number; - length: number; features: Array<{ id: string | number | undefined; type: number; @@ -75,7 +79,6 @@ describe('getTile', () => { version: 2, name: 'source_layer', extent: 4096, - length: 1, features: [ { id: undefined, @@ -97,6 +100,18 @@ describe('getTile', () => { ], ], }, + { + id: undefined, + type: 1, + properties: { + __kbn__feature_id__: 'poly:G7PRMXQBgyyZ-h5iYibj:0', + _id: 'G7PRMXQBgyyZ-h5iYibj', + _index: 'poly', + [KBN_IS_CENTROID_FEATURE]: true, + }, + extent: 4096, + pointArrays: [[{ x: 1470, y: 1702 }]], + }, ], }); }); @@ -166,7 +181,6 @@ describe('getGridTile', () => { version: 2, name: 'source_layer', extent: 4096, - length: 1, features: [ { id: undefined, @@ -189,7 +203,6 @@ describe('getGridTile', () => { version: 2, name: 'source_layer', extent: 4096, - length: 1, features: [ { id: undefined, @@ -209,6 +222,17 @@ describe('getGridTile', () => { ], ], }, + { + id: undefined, + type: 1, + properties: { + ['avg_of_TOTAL_AV']: 5398920.390458991, + doc_count: 42637, + [KBN_IS_CENTROID_FEATURE]: true, + }, + extent: 4096, + pointArrays: [[{ x: 1200, y: 1552 }]], + }, ], }); }); diff --git a/x-pack/plugins/maps/server/mvt/get_tile.ts b/x-pack/plugins/maps/server/mvt/get_tile.ts index cc87f3b65522e..ee45849042715 100644 --- a/x-pack/plugins/maps/server/mvt/get_tile.ts +++ b/x-pack/plugins/maps/server/mvt/get_tile.ts @@ -24,6 +24,7 @@ import { import { convertRegularRespToGeoJson, hitsToGeoJson } from '../../common/elasticsearch_util'; import { flattenHit } from './util'; import { ESBounds, tile2lat, tile2long, tileToESBbox } from '../../common/geo_tile_utils'; +import { getCentroidFeatures } from '../../common/get_centroid_features'; export async function getGridTile({ logger, @@ -270,6 +271,7 @@ function createMvtTile( x: number, y: number ): Buffer | null { + featureCollection.features.push(...getCentroidFeatures(featureCollection)); const tileIndex = geojsonvt(featureCollection, { maxZoom: 24, // max zoom to preserve detail on; can't be higher than 24 tolerance: 3, // simplification tolerance (higher means simpler) diff --git a/x-pack/test/functional/apps/maps/es_geo_grid_source.js b/x-pack/test/functional/apps/maps/es_geo_grid_source.js index 19680ae851a34..12af15793ff9a 100644 --- a/x-pack/test/functional/apps/maps/es_geo_grid_source.js +++ b/x-pack/test/functional/apps/maps/es_geo_grid_source.js @@ -13,8 +13,6 @@ export default function ({ getPageObjects, getService }) { const security = getService('security'); describe('layer geo grid aggregation source', () => { - const EXPECTED_NUMBER_FEATURES_ZOOMED_OUT = 4; - const EXPECTED_NUMBER_FEATURES_ZOOMED_IN = 6; const DATA_CENTER_LON = -98; const DATA_CENTER_LAT = 38; @@ -41,7 +39,11 @@ export default function ({ getPageObjects, getService }) { return requestTimestamp; } - function makeRequestTestsForGeoPrecision(LAYER_ID) { + function makeRequestTestsForGeoPrecision( + LAYER_ID, + expectedNumFeaturesZoomedOut, + expectedNumPartialFeatures + ) { describe('geoprecision - requests', () => { let beforeTimestamp; beforeEach(async () => { @@ -84,7 +86,7 @@ export default function ({ getPageObjects, getService }) { it('should request the data when the map covers the databounds', async () => { const mapboxStyle = await PageObjects.maps.getMapboxStyle(); expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal( - EXPECTED_NUMBER_FEATURES_ZOOMED_OUT + expectedNumFeaturesZoomedOut ); }); @@ -92,7 +94,9 @@ export default function ({ getPageObjects, getService }) { //todo this verifies the extent-filtering behavior (not really the correct application of geotile_grid-precision), and should ideally be moved to its own section await PageObjects.maps.setView(DATA_CENTER_LAT, DATA_CENTER_LON, 6); const mapboxStyle = await PageObjects.maps.getMapboxStyle(); - expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(2); + expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal( + expectedNumPartialFeatures + ); }); }); } @@ -115,9 +119,7 @@ export default function ({ getPageObjects, getService }) { it('should decorate feature properties with scaled doc_count property', async () => { const mapboxStyle = await PageObjects.maps.getMapboxStyle(); - expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal( - EXPECTED_NUMBER_FEATURES_ZOOMED_IN - ); + expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(6); mapboxStyle.sources[LAYER_ID].data.features.forEach(({ properties }) => { expect(properties.hasOwnProperty(HEATMAP_PROP_NAME)).to.be(true); @@ -125,7 +127,7 @@ export default function ({ getPageObjects, getService }) { }); }); - makeRequestTestsForGeoPrecision(LAYER_ID); + makeRequestTestsForGeoPrecision(LAYER_ID, 4, 2); describe('query bar', () => { before(async () => { @@ -194,9 +196,7 @@ export default function ({ getPageObjects, getService }) { it('should decorate feature properties with metrics properterties', async () => { const mapboxStyle = await PageObjects.maps.getMapboxStyle(); - expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal( - EXPECTED_NUMBER_FEATURES_ZOOMED_IN - ); + expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(12); mapboxStyle.sources[LAYER_ID].data.features.forEach(({ properties }) => { expect(properties.hasOwnProperty(MAX_OF_BYTES_PROP_NAME)).to.be(true); @@ -204,7 +204,7 @@ export default function ({ getPageObjects, getService }) { }); }); - makeRequestTestsForGeoPrecision(LAYER_ID); + makeRequestTestsForGeoPrecision(LAYER_ID, 8, 4); describe('query bar', () => { before(async () => { @@ -262,7 +262,7 @@ export default function ({ getPageObjects, getService }) { const LAYER_ID = 'g1xkv'; it('should get expected number of grid cells', async () => { const mapboxStyle = await PageObjects.maps.getMapboxStyle(); - expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(13); + expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(26); }); describe('inspector', () => { diff --git a/x-pack/test/functional/apps/maps/es_pew_pew_source.js b/x-pack/test/functional/apps/maps/es_pew_pew_source.js index b0f98f807fd44..7c6ca3f516062 100644 --- a/x-pack/test/functional/apps/maps/es_pew_pew_source.js +++ b/x-pack/test/functional/apps/maps/es_pew_pew_source.js @@ -38,7 +38,7 @@ export default function ({ getPageObjects, getService }) { it('should render lines', async () => { const mapboxStyle = await PageObjects.maps.getMapboxStyle(); const features = mapboxStyle.sources[VECTOR_SOURCE_ID].data.features; - expect(features.length).to.equal(2); + expect(features.length).to.equal(4); expect(features[0].geometry.type).to.equal('LineString'); }); diff --git a/x-pack/test/functional/apps/maps/joins.js b/x-pack/test/functional/apps/maps/joins.js index 9c769b8d9f59d..ff6686eef53ab 100644 --- a/x-pack/test/functional/apps/maps/joins.js +++ b/x-pack/test/functional/apps/maps/joins.js @@ -81,7 +81,7 @@ export default function ({ getPageObjects, getService }) { it('should decorate feature properties with join property', async () => { const mapboxStyle = await PageObjects.maps.getMapboxStyle(); - expect(mapboxStyle.sources[VECTOR_SOURCE_ID].data.features.length).to.equal(4); + expect(mapboxStyle.sources[VECTOR_SOURCE_ID].data.features.length).to.equal(8); mapboxStyle.sources.n1t6f.data.features.forEach(({ properties }) => { if (properties.name === 'tango') { @@ -130,7 +130,17 @@ export default function ({ getPageObjects, getService }) { return feature.properties.__kbn_isvisibleduetojoin__; }); - expect(visibilitiesOfFeatures).to.eql([false, true, true, true]); + expect(visibilitiesOfFeatures).to.eql([ + false, + true, + true, + true, + // geo centroids for above features + false, + true, + true, + true, + ]); }); describe('query bar', () => { @@ -196,7 +206,17 @@ export default function ({ getPageObjects, getService }) { return feature.properties.__kbn_isvisibleduetojoin__; }); - expect(visibilitiesOfFeatures).to.eql([false, true, false, false]); + expect(visibilitiesOfFeatures).to.eql([ + false, + true, + false, + false, + // geo centroids for above features + false, + true, + false, + false, + ]); }); }); diff --git a/x-pack/test/functional/apps/maps/mapbox_styles.js b/x-pack/test/functional/apps/maps/mapbox_styles.js index 78720fa1689ec..ed94b2290af63 100644 --- a/x-pack/test/functional/apps/maps/mapbox_styles.js +++ b/x-pack/test/functional/apps/maps/mapbox_styles.js @@ -17,6 +17,7 @@ export const MAPBOX_STYLES = { [ 'all', ['!=', ['get', '__kbn_too_many_features__'], true], + ['!=', ['get', '__kbn_is_centroid_feature__'], true], ['any', ['==', ['geometry-type'], 'Point'], ['==', ['geometry-type'], 'MultiPoint']], ], ], @@ -91,6 +92,7 @@ export const MAPBOX_STYLES = { [ 'all', ['!=', ['get', '__kbn_too_many_features__'], true], + ['!=', ['get', '__kbn_is_centroid_feature__'], true], ['any', ['==', ['geometry-type'], 'Polygon'], ['==', ['geometry-type'], 'MultiPolygon']], ], ], @@ -161,6 +163,7 @@ export const MAPBOX_STYLES = { [ 'all', ['!=', ['get', '__kbn_too_many_features__'], true], + ['!=', ['get', '__kbn_is_centroid_feature__'], true], [ 'any', ['==', ['geometry-type'], 'Polygon'], diff --git a/yarn.lock b/yarn.lock index daf43e36196c2..954937c765e4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4329,6 +4329,25 @@ dependencies: "@babel/runtime" "^7.10.2" +"@turf/along@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/along/-/along-6.0.1.tgz#595cecdc48fc7fcfa83c940a8e3eb24d4c2e04d4" + integrity sha512-6PptAcrsFR3o0Flpktk8Vo68W2txEVTh14zjoTVu+H5docd2+pv5/upA77bg3YFBoJgAxmUFt1leDdjReJ44BQ== + dependencies: + "@turf/bearing" "6.x" + "@turf/destination" "6.x" + "@turf/distance" "6.x" + "@turf/helpers" "6.x" + "@turf/invariant" "6.x" + +"@turf/area@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/area/-/area-6.0.1.tgz#50ed63c70ef2bdb72952384f1594319d94f3b051" + integrity sha512-Zv+3N1ep9P5JvR0YOYagLANyapGWQBh8atdeR3bKpWcigVXFsEKNUw03U/5xnh+cKzm7yozHD6MFJkqQv55y0g== + dependencies: + "@turf/helpers" "6.x" + "@turf/meta" "6.x" + "@turf/bbox-polygon@6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@turf/bbox-polygon/-/bbox-polygon-6.0.1.tgz#ae0fbb14558831fb34538aae089a23d3336c6379" @@ -4344,6 +4363,14 @@ "@turf/helpers" "6.x" "@turf/meta" "6.x" +"@turf/bearing@6.x": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/bearing/-/bearing-6.0.1.tgz#8da5d17092e571f170cde7bfb2e5b0d74923c92d" + integrity sha512-mXY1NozqV9EFfBTbUItujwfqfQF0G/Xe2fzvnZle90ekPEUfhi4Dgf5JswJTd96J9LiT8kcd6Jonp5khnx0wIg== + dependencies: + "@turf/helpers" "6.x" + "@turf/invariant" "6.x" + "@turf/boolean-contains@6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@turf/boolean-contains/-/boolean-contains-6.0.1.tgz#c3c583215fc5bda47ede51cf52d735ffdc1006a5" @@ -4371,6 +4398,25 @@ "@turf/helpers" "6.x" "@turf/invariant" "6.x" +"@turf/center-of-mass@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/center-of-mass/-/center-of-mass-6.0.1.tgz#be8904edfd6523683706429ea2f4adf5badd5b26" + integrity sha512-cY+RndzVzDBMlEShRmvLko0CSG1+iC+WdeMAtauCGL61e23LTYHxFSjVOOo4gF+aKqKia1veZPol8ENJoOU4ow== + dependencies: + "@turf/centroid" "6.x" + "@turf/convex" "6.x" + "@turf/helpers" "6.x" + "@turf/invariant" "6.x" + "@turf/meta" "6.x" + +"@turf/centroid@6.x": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@turf/centroid/-/centroid-6.0.2.tgz#c4eb16b4bc60b692f74e1809cf9a7c4a4f5ba1cc" + integrity sha512-auyDauOtC4eddH7GC3CHFTDu2PKhpSeKCRhwhHhXtJqn2dWCJQNIoCeJRmfXRIbzCWhWvgvQafvvhq8HNvmvWw== + dependencies: + "@turf/helpers" "6.x" + "@turf/meta" "6.x" + "@turf/circle@6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@turf/circle/-/circle-6.0.1.tgz#0ab72083373ae3c76b700c17a504ab1b5c0910b9" @@ -4379,6 +4425,15 @@ "@turf/destination" "6.x" "@turf/helpers" "6.x" +"@turf/convex@6.x": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@turf/convex/-/convex-6.0.3.tgz#d7e9912b96483f1504cdd2f60b4b1bbdbf77416c" + integrity sha512-S9zvcKiqkIiQ/fhnEP5ftDrsVY3Sh0XeLDVZY761nlvuvzLVzz26Gq7H3NMsCJlmIcQS9jPARFBVpRZi6eTV8Q== + dependencies: + "@turf/helpers" "6.x" + "@turf/meta" "6.x" + concaveman "*" + "@turf/destination@6.x": version "6.0.1" resolved "https://registry.yarnpkg.com/@turf/destination/-/destination-6.0.1.tgz#5275887fa96ec463f44864a2c17f0b712361794a" @@ -4387,7 +4442,7 @@ "@turf/helpers" "6.x" "@turf/invariant" "6.x" -"@turf/distance@6.0.1": +"@turf/distance@6.0.1", "@turf/distance@6.x": version "6.0.1" resolved "https://registry.yarnpkg.com/@turf/distance/-/distance-6.0.1.tgz#0761f28784286e7865a427c4e7e3593569c2dea8" integrity sha512-q7t7rWIWfkg7MP1Vt4uLjSEhe5rPfCO2JjpKmk7JC+QZKEQkuvHEqy3ejW1iC7Kw5ZcZNR3qdMGGz+6HnVwqvg== @@ -4407,6 +4462,15 @@ dependencies: "@turf/helpers" "6.x" +"@turf/length@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@turf/length/-/length-6.0.2.tgz#22d91a6d0174e862a3614865613f1aceb1162dac" + integrity sha512-nyfXMowVtX2dICEG7u7EGC2SMaauVUWIMc9eWQrEauNA/9aw+7wbiuip4GPBoyeXEUUekF0EOjJn5aB9Zc8CzA== + dependencies: + "@turf/distance" "6.x" + "@turf/helpers" "6.x" + "@turf/meta" "6.x" + "@turf/meta@6.x": version "6.0.2" resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-6.0.2.tgz#eb92951126d24a613ac1b7b99d733fcc20fd30cf" @@ -9955,6 +10019,17 @@ concat-stream@~2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" +concaveman@*: + version "1.1.1" + resolved "https://registry.yarnpkg.com/concaveman/-/concaveman-1.1.1.tgz#6c2482580b2523cef82fc2bec00a0415e6e68162" + integrity sha1-bCSCWAslI874L8K+wAoEFebmgWI= + dependencies: + monotone-convex-hull-2d "^1.0.1" + point-in-polygon "^1.0.1" + rbush "^2.0.1" + robust-orientation "^1.1.3" + tinyqueue "^1.1.0" + config-chain@^1.1.12, config-chain@~1.1.8: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" @@ -11574,10 +11649,10 @@ detective@^5.0.2, detective@^5.2.0: defined "^1.0.0" minimist "^1.1.1" -devtools-protocol@0.0.818844: - version "0.0.818844" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.818844.tgz#d1947278ec85b53e4c8ca598f607a28fa785ba9e" - integrity sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg== +devtools-protocol@0.0.809251: + version "0.0.809251" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.809251.tgz#300b3366be107d5c46114ecb85274173e3999518" + integrity sha512-pf+2OY6ghMDPjKkzSWxHMq+McD+9Ojmq5XVRYpv/kPd9sTMQxzEt21592a31API8qRjro0iYYOc3ag46qF/1FA== dezalgo@^1.0.0: version "1.0.3" @@ -20122,6 +20197,13 @@ monocle-ts@^1.0.0: resolved "https://registry.yarnpkg.com/monocle-ts/-/monocle-ts-1.7.1.tgz#03a615938aa90983a4fa29749969d30f72d80ba1" integrity sha512-X9OzpOyd/R83sYex8NYpJjUzi/MLQMvGNVfxDYiIvs+QMXMEUDwR61MQoARFN10Cqz5h/mbFSPnIQNUIGhYd2Q== +monotone-convex-hull-2d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz#47f5daeadf3c4afd37764baa1aa8787a40eee08c" + integrity sha1-R/Xa6t88Sv03dkuqGqh4ekDu4Iw= + dependencies: + robust-orientation "^1.1.3" + moo@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" @@ -22091,6 +22173,11 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" +point-in-polygon@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/point-in-polygon/-/point-in-polygon-1.0.1.tgz#d59b64e8fee41c49458aac82b56718c5957b2af7" + integrity sha1-1Ztk6P7kHElFiqyCtWcYxZV7Kvc= + polished@^1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/polished/-/polished-1.9.2.tgz#d705cac66f3a3ed1bd38aad863e2c1e269baf6b6" @@ -22589,7 +22676,7 @@ puppeteer@^2.0.0: integrity sha512-I4JbNmQHZkE72TPNdipND8GnsEBnqzuksxPSAT25qvudShuuzdY9TwNBQ65IJwPD/pjlpx7fUIUmFyvTHwlxhQ== dependencies: debug "^4.1.0" - devtools-protocol "0.0.818844" + devtools-protocol "0.0.809251" extract-zip "^2.0.0" https-proxy-agent "^4.0.0" node-fetch "^2.6.1" @@ -22665,6 +22752,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quickselect@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-1.1.1.tgz#852e412ce418f237ad5b660d70cffac647ae94c2" + integrity sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ== + quickselect@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" @@ -22775,6 +22867,13 @@ raw-loader@^4.0.1: loader-utils "^2.0.0" schema-utils "^2.6.5" +rbush@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/rbush/-/rbush-2.0.2.tgz#bb6005c2731b7ba1d5a9a035772927d16a614605" + integrity sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA== + dependencies: + quickselect "^1.0.1" + rbush@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/rbush/-/rbush-3.0.1.tgz#5fafa8a79b3b9afdfe5008403a720cc1de882ecf" @@ -24609,6 +24708,34 @@ rison-node@1.0.2: resolved "https://registry.yarnpkg.com/rison-node/-/rison-node-1.0.2.tgz#b7b5f37f39f5ae2a51a973a33c9aa17239a33e4b" integrity sha1-t7Xzfzn1ripRqXOjPJqhcjmjPks= +robust-orientation@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/robust-orientation/-/robust-orientation-1.1.3.tgz#daff5b00d3be4e60722f0e9c0156ef967f1c2049" + integrity sha1-2v9bANO+TmByLw6cAVbvln8cIEk= + dependencies: + robust-scale "^1.0.2" + robust-subtract "^1.0.0" + robust-sum "^1.0.0" + two-product "^1.0.2" + +robust-scale@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/robust-scale/-/robust-scale-1.0.2.tgz#775132ed09542d028e58b2cc79c06290bcf78c32" + integrity sha1-d1Ey7QlULQKOWLLMecBikLz3jDI= + dependencies: + two-product "^1.0.2" + two-sum "^1.0.0" + +robust-subtract@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/robust-subtract/-/robust-subtract-1.0.0.tgz#e0b164e1ed8ba4e3a5dda45a12038348dbed3e9a" + integrity sha1-4LFk4e2LpOOl3aRaEgODSNvtPpo= + +robust-sum@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/robust-sum/-/robust-sum-1.0.0.tgz#16646e525292b4d25d82757a286955e0bbfa53d9" + integrity sha1-FmRuUlKStNJdgnV6KGlV4Lv6U9k= + rollup@^0.25.8: version "0.25.8" resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.25.8.tgz#bf6ce83b87510d163446eeaa577ed6a6fc5835e0" @@ -26929,6 +27056,11 @@ tinymath@1.2.1: resolved "https://registry.yarnpkg.com/tinymath/-/tinymath-1.2.1.tgz#f97ed66c588cdbf3c19dfba2ae266ee323db7e47" integrity sha512-8CYutfuHR3ywAJus/3JUhaJogZap1mrUQGzNxdBiQDhP3H0uFdQenvaXvqI8lMehX4RsanRZzxVfjMBREFdQaA== +tinyqueue@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-1.2.3.tgz#b6a61de23060584da29f82362e45df1ec7353f3d" + integrity sha512-Qz9RgWuO9l8lT+Y9xvbzhPT2efIUIFd69N7eF7tJ9lnQl0iLj1M7peK7IoUGZL9DJHw9XftqLreccfxcQgYLxA== + tinyqueue@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" @@ -27329,6 +27461,16 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +two-product@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/two-product/-/two-product-1.0.2.tgz#67d95d4b257a921e2cb4bd7af9511f9088522eaa" + integrity sha1-Z9ldSyV6kh4stL16+VEfkIhSLqo= + +two-sum@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/two-sum/-/two-sum-1.0.0.tgz#31d3f32239e4f731eca9df9155e2b297f008ab64" + integrity sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q= + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" From d6816384a70c7a9182a3a1bcd8af998fdc3ad81e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 7 Jan 2021 18:26:16 +0000 Subject: [PATCH 07/24] chore(NA): move apm plugin tests out of __tests__ folder (#87601) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../List/{__test__ => }/List.test.tsx | 10 +++++----- .../List/{__test__ => __fixtures__}/props.json | 0 .../__snapshots__/List.test.tsx.snap | 0 .../{__tests__ => }/SelectableUrlList.test.tsx | 6 +++--- .../{__tests__ => }/FormatToSec.test.ts | 2 +- .../{__tests__ => }/KeyUXMetrics.test.tsx | 4 ++-- .../{__tests__ => }/EmbeddedMap.test.tsx | 6 +++--- .../{__tests__ => }/MapToolTip.test.tsx | 2 +- .../__mocks__/regions_layer.mock.ts | 0 .../__snapshots__/EmbeddedMap.test.tsx.snap | 0 .../__snapshots__/MapToolTip.test.tsx.snap | 0 .../{__tests__ => }/useLayerList.test.ts | 2 +- .../{__test__ => }/distribution.test.ts | 2 +- .../{__test__ => }/get_agent_marks.test.ts | 4 ++-- .../{__test__ => }/get_error_marks.test.ts | 4 ++-- .../service_inventory.test.tsx | 2 +- .../ImpactBar/{__test__ => }/ImpactBar.test.js | 2 +- .../__snapshots__/ImpactBar.test.js.snap | 0 .../{__test__ => }/KeyValueTable.test.tsx | 4 ++-- .../DiscoverErrorButton.test.tsx | 4 ++-- .../{__test__ => }/DiscoverErrorLink.test.tsx | 4 ++-- .../DiscoverLinks.integration.test.tsx | 14 +++++++------- .../DiscoverTransactionLink.test.tsx | 4 ++-- .../mock_transaction.json | 0 .../DiscoverErrorButton.test.tsx.snap | 0 .../DiscoverErrorLink.test.tsx.snap | 0 .../DiscoverTransactionLink.test.tsx.snap | 0 .../discover_transaction_button.test.tsx.snap | 0 .../discover_transaction_button.test.tsx | 6 +++--- .../{__test__ => }/ManagedTable.test.js | 2 +- .../__snapshots__/ManagedTable.test.js.snap | 0 .../{__test__ => }/ErrorMetadata.test.tsx | 8 ++++---- .../{__test__ => }/MetadataTable.test.tsx | 8 ++++---- .../{__test__ => }/Section.test.tsx | 4 ++-- .../{__test__ => }/SpanMetadata.test.tsx | 8 ++++---- .../TransactionMetadata.test.tsx | 8 ++++---- .../{__test__ => }/helper.test.ts | 6 +++--- .../ErrorCountSummaryItemBadge.test.tsx | 4 ++-- .../HttpInfoSummaryItem.test.tsx | 4 ++-- .../{__test__ => }/HttpStatusBadge.test.tsx | 4 ++-- .../{__test__ => }/index.test.tsx | 4 ++-- .../TransactionActionMenu.test.tsx | 18 +++++++++--------- .../{__test__ => __fixtures__}/mockData.ts | 0 .../TransactionActionMenu.test.tsx.snap | 0 .../{__test__ => }/sections.test.ts | 4 ++-- .../SessionStorageMock.ts | 0 .../services/{__test__ => }/callApi.test.ts | 6 +++--- .../services/{__test__ => }/callApmApi.test.ts | 4 ++-- .../utils/{__test__ => }/flattenObject.test.ts | 2 +- .../__snapshots__/get_buckets.test.ts.snap | 0 .../{__tests__ => }/get_buckets.test.ts | 6 +++--- .../get_environment_ui_filter_es.test.ts | 6 +++--- 52 files changed, 94 insertions(+), 94 deletions(-) rename x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/{__test__ => }/List.test.tsx (78%) rename x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/{__test__ => __fixtures__}/props.json (100%) rename x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/{__test__ => }/__snapshots__/List.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/{__tests__ => }/SelectableUrlList.test.tsx (86%) rename x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/{__tests__ => }/FormatToSec.test.ts (94%) rename x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/{__tests__ => }/KeyUXMetrics.test.tsx (93%) rename x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/{__tests__ => }/EmbeddedMap.test.tsx (79%) rename x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/{__tests__ => }/MapToolTip.test.tsx (93%) rename x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/{__tests__ => }/__mocks__/regions_layer.mock.ts (100%) rename x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/{__tests__ => }/__snapshots__/EmbeddedMap.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/{__tests__ => }/__snapshots__/MapToolTip.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/{__tests__ => }/useLayerList.test.ts (92%) rename x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/{__test__ => }/distribution.test.ts (96%) rename x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/{__test__ => }/get_agent_marks.test.ts (90%) rename x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/{__test__ => }/get_error_marks.test.ts (94%) rename x-pack/plugins/apm/public/components/shared/ImpactBar/{__test__ => }/ImpactBar.test.js (95%) rename x-pack/plugins/apm/public/components/shared/ImpactBar/{__test__ => }/__snapshots__/ImpactBar.test.js.snap (100%) rename x-pack/plugins/apm/public/components/shared/KeyValueTable/{__test__ => }/KeyValueTable.test.tsx (94%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/DiscoverErrorButton.test.tsx (92%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/DiscoverErrorLink.test.tsx (92%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/DiscoverLinks.integration.test.tsx (87%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/DiscoverTransactionLink.test.tsx (84%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => __fixtures__}/mock_transaction.json (100%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/__snapshots__/DiscoverErrorButton.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/__snapshots__/DiscoverErrorLink.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/__snapshots__/DiscoverTransactionLink.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/__snapshots__/discover_transaction_button.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/{__test__ => }/discover_transaction_button.test.tsx (82%) rename x-pack/plugins/apm/public/components/shared/ManagedTable/{__test__ => }/ManagedTable.test.js (96%) rename x-pack/plugins/apm/public/components/shared/ManagedTable/{__test__ => }/__snapshots__/ManagedTable.test.js.snap (100%) rename x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/{__test__ => }/ErrorMetadata.test.tsx (92%) rename x-pack/plugins/apm/public/components/shared/MetadataTable/{__test__ => }/MetadataTable.test.tsx (87%) rename x-pack/plugins/apm/public/components/shared/MetadataTable/{__test__ => }/Section.test.tsx (83%) rename x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/{__test__ => }/SpanMetadata.test.tsx (92%) rename x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/{__test__ => }/TransactionMetadata.test.tsx (93%) rename x-pack/plugins/apm/public/components/shared/MetadataTable/{__test__ => }/helper.test.ts (92%) rename x-pack/plugins/apm/public/components/shared/Summary/{__test__ => }/ErrorCountSummaryItemBadge.test.tsx (86%) rename x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/{__test__ => }/HttpInfoSummaryItem.test.tsx (95%) rename x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/{__test__ => }/HttpStatusBadge.test.tsx (95%) rename x-pack/plugins/apm/public/components/shared/TimestampTooltip/{__test__ => }/index.test.tsx (94%) rename x-pack/plugins/apm/public/components/shared/TransactionActionMenu/{__test__ => }/TransactionActionMenu.test.tsx (95%) rename x-pack/plugins/apm/public/components/shared/TransactionActionMenu/{__test__ => __fixtures__}/mockData.ts (100%) rename x-pack/plugins/apm/public/components/shared/TransactionActionMenu/{__test__ => }/__snapshots__/TransactionActionMenu.test.tsx.snap (100%) rename x-pack/plugins/apm/public/components/shared/TransactionActionMenu/{__test__ => }/sections.test.ts (98%) rename x-pack/plugins/apm/public/services/{__test__ => __mocks__}/SessionStorageMock.ts (100%) rename x-pack/plugins/apm/public/services/{__test__ => }/callApi.test.ts (97%) rename x-pack/plugins/apm/public/services/{__test__ => }/callApmApi.test.ts (93%) rename x-pack/plugins/apm/public/utils/{__test__ => }/flattenObject.test.ts (96%) rename x-pack/plugins/apm/server/lib/errors/distribution/{__tests__ => }/__snapshots__/get_buckets.test.ts.snap (100%) rename x-pack/plugins/apm/server/lib/errors/distribution/{__tests__ => }/get_buckets.test.ts (92%) rename x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/{__test__ => }/get_environment_ui_filter_es.test.ts (80%) diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/List.test.tsx similarity index 78% rename from x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/List.test.tsx index 4022caedadaab..e6555ed900a6d 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/List.test.tsx @@ -6,11 +6,11 @@ import { mount } from 'enzyme'; import React from 'react'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; -import { MockUrlParamsContextProvider } from '../../../../../context/url_params_context/mock_url_params_context_provider'; -import { mockMoment, toJson } from '../../../../../utils/testHelpers'; -import { ErrorGroupList } from '../index'; -import props from './props.json'; +import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; +import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; +import { mockMoment, toJson } from '../../../../utils/testHelpers'; +import { ErrorGroupList } from './index'; +import props from './__fixtures__/props.json'; import { MemoryRouter } from 'react-router-dom'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => { diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/props.json b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__fixtures__/props.json similarity index 100% rename from x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/props.json rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__fixtures__/props.json diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__snapshots__/List.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__snapshots__/List.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/__tests__/SelectableUrlList.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/SelectableUrlList.test.tsx similarity index 86% rename from x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/__tests__/SelectableUrlList.test.tsx rename to x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/SelectableUrlList.test.tsx index a492938deffab..c469a2c21c34a 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/__tests__/SelectableUrlList.test.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/URLFilter/URLSearch/SelectableUrlList.test.tsx @@ -5,9 +5,9 @@ */ import React from 'react'; import { createMemoryHistory } from 'history'; -import * as fetcherHook from '../../../../../../hooks/use_fetcher'; -import { SelectableUrlList } from '../SelectableUrlList'; -import { render } from '../../../utils/test_helper'; +import * as fetcherHook from '../../../../../hooks/use_fetcher'; +import { SelectableUrlList } from './SelectableUrlList'; +import { render } from '../../utils/test_helper'; describe('SelectableUrlList', () => { it('it uses search term value from url', () => { diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/FormatToSec.test.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/FormatToSec.test.ts similarity index 94% rename from x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/FormatToSec.test.ts rename to x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/FormatToSec.test.ts index 6cdf469d980fa..764d662615031 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/FormatToSec.test.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/FormatToSec.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { formatToSec } from '../KeyUXMetrics'; +import { formatToSec } from './KeyUXMetrics'; describe('FormatToSec', () => { test('it returns the expected value', () => { diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.test.tsx similarity index 93% rename from x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx rename to x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.test.tsx index 5d73cbc4cd3c8..804eeaec26655 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.test.tsx @@ -5,8 +5,8 @@ */ import React from 'react'; import { render } from '@testing-library/react'; -import * as fetcherHook from '../../../../../hooks/use_fetcher'; -import { KeyUXMetrics } from '../KeyUXMetrics'; +import * as fetcherHook from '../../../../hooks/use_fetcher'; +import { KeyUXMetrics } from './KeyUXMetrics'; describe('KeyUXMetrics', () => { it('renders metrics with correct formats', () => { diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/EmbeddedMap.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.test.tsx similarity index 79% rename from x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/EmbeddedMap.test.tsx rename to x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.test.tsx index 388a8824bc73d..125c57f514a59 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/EmbeddedMap.test.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.test.tsx @@ -7,9 +7,9 @@ import { render } from 'enzyme'; import React from 'react'; -import { EmbeddedMap } from '../EmbeddedMap'; -import { KibanaContextProvider } from '../../../../../../../../../src/plugins/kibana_react/public'; -import { embeddablePluginMock } from '../../../../../../../../../src/plugins/embeddable/public/mocks'; +import { EmbeddedMap } from './EmbeddedMap'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; +import { embeddablePluginMock } from '../../../../../../../../src/plugins/embeddable/public/mocks'; describe('Embedded Map', () => { test('it renders', () => { diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/MapToolTip.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.test.tsx similarity index 93% rename from x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/MapToolTip.test.tsx rename to x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.test.tsx index cbaae40b04361..89f20bf24ccba 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/MapToolTip.test.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.test.tsx @@ -7,7 +7,7 @@ import { render, shallow } from 'enzyme'; import React from 'react'; -import { MapToolTip } from '../MapToolTip'; +import { MapToolTip } from './MapToolTip'; describe('Map Tooltip', () => { test('it shallow renders', () => { diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__mocks__/regions_layer.mock.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__mocks__/regions_layer.mock.ts similarity index 100% rename from x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__mocks__/regions_layer.mock.ts rename to x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__mocks__/regions_layer.mock.ts diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/EmbeddedMap.test.tsx.snap b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__snapshots__/EmbeddedMap.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/EmbeddedMap.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__snapshots__/EmbeddedMap.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/MapToolTip.test.tsx.snap b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__snapshots__/MapToolTip.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/MapToolTip.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__snapshots__/MapToolTip.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/useLayerList.test.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useLayerList.test.ts similarity index 92% rename from x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/useLayerList.test.ts rename to x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useLayerList.test.ts index 872553452b263..a63ab11263e5f 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/useLayerList.test.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useLayerList.test.ts @@ -6,7 +6,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { mockLayerList } from './__mocks__/regions_layer.mock'; -import { useLayerList } from '../useLayerList'; +import { useLayerList } from './useLayerList'; describe('useLayerList', () => { test('it returns the region layer', () => { diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/distribution.test.ts similarity index 96% rename from x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/distribution.test.ts index 1586e1f4903a2..0453b113f41de 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/distribution.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getFormattedBuckets } from '../index'; +import { getFormattedBuckets } from './index'; describe('Distribution', () => { it('getFormattedBuckets', () => { diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.test.ts similarity index 90% rename from x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.test.ts index 72533cf2930d2..7666db35d43cf 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; -import { getAgentMarks } from '../get_agent_marks'; +import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; +import { getAgentMarks } from './get_agent_marks'; describe('getAgentMarks', () => { it('should sort the marks by time', () => { diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.test.ts similarity index 94% rename from x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.test.ts index abfecc3f70d24..0eb7a5b89aa3a 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IWaterfallError } from '../../Waterfall/waterfall_helpers/waterfall_helpers'; -import { getErrorMarks } from '../get_error_marks'; +import { IWaterfallError } from '../Waterfall/waterfall_helpers/waterfall_helpers'; +import { getErrorMarks } from './get_error_marks'; describe('getErrorMarks', () => { describe('returns empty array', () => { diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx index 6bb1ea2919c16..e501dd3bb7a56 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx @@ -21,7 +21,7 @@ import { import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import * as useLocalUIFilters from '../../../hooks/useLocalUIFilters'; import * as useDynamicIndexPatternHooks from '../../../hooks/use_dynamic_index_pattern'; -import { SessionStorageMock } from '../../../services/__test__/SessionStorageMock'; +import { SessionStorageMock } from '../../../services/__mocks__/SessionStorageMock'; import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; import * as hook from './use_anomaly_detection_jobs_fetcher'; diff --git a/x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js b/x-pack/plugins/apm/public/components/shared/ImpactBar/ImpactBar.test.js similarity index 95% rename from x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js rename to x-pack/plugins/apm/public/components/shared/ImpactBar/ImpactBar.test.js index d4b3f223f726f..4e94ea85c120b 100644 --- a/x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js +++ b/x-pack/plugins/apm/public/components/shared/ImpactBar/ImpactBar.test.js @@ -6,7 +6,7 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { ImpactBar } from '..'; +import { ImpactBar } from '.'; describe('ImpactBar component', () => { it('should render with default values', () => { diff --git a/x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/__snapshots__/ImpactBar.test.js.snap b/x-pack/plugins/apm/public/components/shared/ImpactBar/__snapshots__/ImpactBar.test.js.snap similarity index 100% rename from x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/__snapshots__/ImpactBar.test.js.snap rename to x-pack/plugins/apm/public/components/shared/ImpactBar/__snapshots__/ImpactBar.test.js.snap diff --git a/x-pack/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx b/x-pack/plugins/apm/public/components/shared/KeyValueTable/KeyValueTable.test.tsx similarity index 94% rename from x-pack/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx rename to x-pack/plugins/apm/public/components/shared/KeyValueTable/KeyValueTable.test.tsx index 5a9e8809ea734..a08ade8e559d0 100644 --- a/x-pack/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/KeyValueTable/KeyValueTable.test.tsx @@ -5,9 +5,9 @@ */ import React from 'react'; -import { KeyValueTable } from '..'; +import { KeyValueTable } from '.'; import { render } from '@testing-library/react'; -import { renderWithTheme } from '../../../../utils/testHelpers'; +import { renderWithTheme } from '../../../utils/testHelpers'; function getKeys(output: ReturnType) { const keys = output.getAllByTestId('dot-key'); diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorButton.test.tsx similarity index 92% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorButton.test.tsx index f71c8b71aa2ee..3a41c19c53f6d 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorButton.test.tsx @@ -6,8 +6,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; -import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; -import { DiscoverErrorLink } from '../DiscoverErrorLink'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; +import { DiscoverErrorLink } from './DiscoverErrorLink'; describe('DiscoverErrorLink without kuery', () => { let wrapper: ShallowWrapper; diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.test.tsx similarity index 92% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.test.tsx index f71c8b71aa2ee..3a41c19c53f6d 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.test.tsx @@ -6,8 +6,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; -import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; -import { DiscoverErrorLink } from '../DiscoverErrorLink'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; +import { DiscoverErrorLink } from './DiscoverErrorLink'; describe('DiscoverErrorLink without kuery', () => { let wrapper: ShallowWrapper; diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLinks.integration.test.tsx similarity index 87% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLinks.integration.test.tsx index ca02abc395992..e77d4d7185273 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLinks.integration.test.tsx @@ -6,13 +6,13 @@ import { Location } from 'history'; import React from 'react'; -import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; -import { Span } from '../../../../../../typings/es_schemas/ui/span'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; -import { getRenderedHref } from '../../../../../utils/testHelpers'; -import { DiscoverErrorLink } from '../DiscoverErrorLink'; -import { DiscoverSpanLink } from '../DiscoverSpanLink'; -import { DiscoverTransactionLink } from '../DiscoverTransactionLink'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; +import { Span } from '../../../../../typings/es_schemas/ui/span'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; +import { getRenderedHref } from '../../../../utils/testHelpers'; +import { DiscoverErrorLink } from './DiscoverErrorLink'; +import { DiscoverSpanLink } from './DiscoverSpanLink'; +import { DiscoverTransactionLink } from './DiscoverTransactionLink'; describe('DiscoverLinks', () => { it('produces the correct URL for a transaction', async () => { diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.test.tsx similarity index 84% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.test.tsx index 48d8bb2b41644..0ded3fb6619e3 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.test.tsx @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; // @ts-expect-error import configureStore from '../../../../../store/config/configureStore'; -import { getDiscoverQuery } from '../DiscoverTransactionLink'; +import { getDiscoverQuery } from './DiscoverTransactionLink'; function getMockTransaction() { return { diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/mock_transaction.json b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__fixtures__/mock_transaction.json similarity index 100% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/mock_transaction.json rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__fixtures__/mock_transaction.json diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorButton.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/DiscoverErrorButton.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorButton.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/DiscoverErrorButton.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorLink.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/DiscoverErrorLink.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorLink.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/DiscoverErrorLink.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionLink.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/DiscoverTransactionLink.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionLink.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/DiscoverTransactionLink.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/discover_transaction_button.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/discover_transaction_button.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/discover_transaction_button.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__snapshots__/discover_transaction_button.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/discover_transaction_button.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/discover_transaction_button.test.tsx similarity index 82% rename from x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/discover_transaction_button.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/discover_transaction_button.test.tsx index 4a68a5c0b4904..75fe18913618d 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/discover_transaction_button.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/discover_transaction_button.test.tsx @@ -6,12 +6,12 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { DiscoverTransactionLink, getDiscoverQuery, -} from '../DiscoverTransactionLink'; -import mockTransaction from './mock_transaction.json'; +} from './DiscoverTransactionLink'; +import mockTransaction from './__fixtures__/mock_transaction.json'; describe('DiscoverTransactionLink component', () => { it('should render with data', () => { diff --git a/x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js b/x-pack/plugins/apm/public/components/shared/ManagedTable/ManagedTable.test.js similarity index 96% rename from x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js rename to x-pack/plugins/apm/public/components/shared/ManagedTable/ManagedTable.test.js index 38f260b04e252..88e1c57e62354 100644 --- a/x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js +++ b/x-pack/plugins/apm/public/components/shared/ManagedTable/ManagedTable.test.js @@ -6,7 +6,7 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { UnoptimizedManagedTable } from '..'; +import { UnoptimizedManagedTable } from '.'; describe('ManagedTable component', () => { let people; diff --git a/x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap b/x-pack/plugins/apm/public/components/shared/ManagedTable/__snapshots__/ManagedTable.test.js.snap similarity index 100% rename from x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap rename to x-pack/plugins/apm/public/components/shared/ManagedTable/__snapshots__/ManagedTable.test.js.snap diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/ErrorMetadata.test.tsx similarity index 92% rename from x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/ErrorMetadata.test.tsx index 8f44d98cecdf7..8a50bc2cde520 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/ErrorMetadata.test.tsx @@ -7,13 +7,13 @@ import { render } from '@testing-library/react'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { ErrorMetadata } from '..'; -import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; +import { ErrorMetadata } from '.'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; +import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { expectTextsInDocument, expectTextsNotInDocument, -} from '../../../../../utils/testHelpers'; +} from '../../../../utils/testHelpers'; function Wrapper({ children }: { children?: ReactNode }) { return ( diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/MetadataTable.test.tsx similarity index 87% rename from x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/MetadataTable.test.tsx index 8a4cd588c8260..9bd3278033f92 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/MetadataTable.test.tsx @@ -7,10 +7,10 @@ import { render } from '@testing-library/react'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { MetadataTable } from '..'; -import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; -import { expectTextsInDocument } from '../../../../utils/testHelpers'; -import { SectionsWithRows } from '../helper'; +import { MetadataTable } from '.'; +import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import { expectTextsInDocument } from '../../../utils/testHelpers'; +import { SectionsWithRows } from './helper'; function Wrapper({ children }: { children?: ReactNode }) { return ( diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/Section.test.tsx similarity index 83% rename from x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/Section.test.tsx index 7a150f81580d8..3dd19778430b7 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/Section.test.tsx @@ -5,8 +5,8 @@ */ import React from 'react'; import { render } from '@testing-library/react'; -import { Section } from '../Section'; -import { expectTextsInDocument } from '../../../../utils/testHelpers'; +import { Section } from './Section'; +import { expectTextsInDocument } from '../../../utils/testHelpers'; describe('Section', () => { it('shows "empty state message" if no data is available', () => { diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/SpanMetadata.test.tsx similarity index 92% rename from x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/SpanMetadata.test.tsx index c97e506187347..c9ed2c4c2b32f 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/SpanMetadata.test.tsx @@ -7,13 +7,13 @@ import { render } from '@testing-library/react'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { SpanMetadata } from '..'; -import { Span } from '../../../../../../typings/es_schemas/ui/span'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; +import { SpanMetadata } from '.'; +import { Span } from '../../../../../typings/es_schemas/ui/span'; +import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { expectTextsInDocument, expectTextsNotInDocument, -} from '../../../../../utils/testHelpers'; +} from '../../../../utils/testHelpers'; function Wrapper({ children }: { children?: ReactNode }) { return ( diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/TransactionMetadata.test.tsx similarity index 93% rename from x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/TransactionMetadata.test.tsx index 4080a300ba17f..6a5a122f23954 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/TransactionMetadata.test.tsx @@ -7,13 +7,13 @@ import { render } from '@testing-library/react'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { TransactionMetadata } from '..'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; +import { TransactionMetadata } from '.'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; +import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { expectTextsInDocument, expectTextsNotInDocument, -} from '../../../../../utils/testHelpers'; +} from '../../../../utils/testHelpers'; function Wrapper({ children }: { children?: ReactNode }) { return ( diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts b/x-pack/plugins/apm/public/components/shared/MetadataTable/helper.test.ts similarity index 92% rename from x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts rename to x-pack/plugins/apm/public/components/shared/MetadataTable/helper.test.ts index ac776e0b8980c..8f3e675c7aeae 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/helper.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getSectionsWithRows, filterSectionsByTerm } from '../helper'; -import { LABELS, HTTP, SERVICE } from '../sections'; -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; +import { getSectionsWithRows, filterSectionsByTerm } from './helper'; +import { LABELS, HTTP, SERVICE } from './sections'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; describe('MetadataTable Helper', () => { const sections = [ diff --git a/x-pack/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.test.tsx similarity index 86% rename from x-pack/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.test.tsx index 26087e1fd85cc..fd531f79c9ac6 100644 --- a/x-pack/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.test.tsx @@ -5,11 +5,11 @@ */ import React from 'react'; -import { ErrorCountSummaryItemBadge } from '../ErrorCountSummaryItemBadge'; +import { ErrorCountSummaryItemBadge } from './ErrorCountSummaryItemBadge'; import { expectTextsInDocument, renderWithTheme, -} from '../../../../utils/testHelpers'; +} from '../../../utils/testHelpers'; describe('ErrorCountSummaryItemBadge', () => { it('shows singular error message', () => { diff --git a/x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/HttpInfoSummaryItem.test.tsx similarity index 95% rename from x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/HttpInfoSummaryItem.test.tsx index d0e1f08aabbbc..9465d94e16dc8 100644 --- a/x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/HttpInfoSummaryItem.test.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; -import { HttpInfoSummaryItem } from '../'; -import * as exampleTransactions from '../../__fixtures__/transactions'; +import { HttpInfoSummaryItem } from '.'; +import * as exampleTransactions from '../__fixtures__/transactions'; describe('HttpInfoSummaryItem', () => { describe('render', () => { diff --git a/x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/HttpStatusBadge.test.tsx similarity index 95% rename from x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/HttpStatusBadge.test.tsx index ecbf41486a3fd..0df23883d3127 100644 --- a/x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/HttpStatusBadge.test.tsx @@ -6,13 +6,13 @@ import React from 'react'; import { mount } from 'enzyme'; -import { HttpStatusBadge } from '../index'; +import { HttpStatusBadge } from './index'; import { successColor, neutralColor, warningColor, errorColor, -} from '../../../../../utils/httpStatusCodeToColor'; +} from '../../../../utils/httpStatusCodeToColor'; describe('HttpStatusBadge', () => { describe('render', () => { diff --git a/x-pack/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.test.tsx similarity index 94% rename from x-pack/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx rename to x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.test.tsx index b4678b287dc16..dd36827ea94f2 100644 --- a/x-pack/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.test.tsx @@ -7,8 +7,8 @@ import { shallow } from 'enzyme'; import React from 'react'; import moment from 'moment-timezone'; -import { TimestampTooltip } from '../index'; -import { mockNow } from '../../../../utils/testHelpers'; +import { TimestampTooltip } from './index'; +import { mockNow } from '../../../utils/testHelpers'; describe('TimestampTooltip', () => { const timestamp = 1570720000123; // Oct 10, 2019, 08:06:40.123 (UTC-7) diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.test.tsx similarity index 95% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.test.tsx index 8cb863c8fc385..6ff395db594f1 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.test.tsx @@ -7,18 +7,18 @@ import { act, fireEvent, render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { License } from '../../../../../../licensing/common/license'; -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; -import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; -import { LicenseContext } from '../../../../context/license/license_context'; -import * as hooks from '../../../../hooks/use_fetcher'; -import * as apmApi from '../../../../services/rest/createCallApmApi'; +import { License } from '../../../../../licensing/common/license'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; +import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import { LicenseContext } from '../../../context/license/license_context'; +import * as hooks from '../../../hooks/use_fetcher'; +import * as apmApi from '../../../services/rest/createCallApmApi'; import { expectTextsInDocument, expectTextsNotInDocument, -} from '../../../../utils/testHelpers'; -import { TransactionActionMenu } from '../TransactionActionMenu'; -import * as Transactions from './mockData'; +} from '../../../utils/testHelpers'; +import { TransactionActionMenu } from './TransactionActionMenu'; +import * as Transactions from './__fixtures__/mockData'; function Wrapper({ children }: { children?: React.ReactNode }) { return ( diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/mockData.ts b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__fixtures__/mockData.ts similarity index 100% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/mockData.ts rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__fixtures__/mockData.ts diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/__snapshots__/TransactionActionMenu.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__snapshots__/TransactionActionMenu.test.tsx.snap similarity index 100% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/__snapshots__/TransactionActionMenu.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__snapshots__/TransactionActionMenu.test.tsx.snap diff --git a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/sections.test.ts similarity index 98% rename from x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/sections.test.ts index 048ae9474c403..f6067a34e2b90 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/sections.test.ts @@ -5,8 +5,8 @@ */ import { Location } from 'history'; import { IBasePath } from 'kibana/public'; -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; -import { getSections } from '../sections'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; +import { getSections } from './sections'; describe('Transaction action menu', () => { const basePath = ({ diff --git a/x-pack/plugins/apm/public/services/__test__/SessionStorageMock.ts b/x-pack/plugins/apm/public/services/__mocks__/SessionStorageMock.ts similarity index 100% rename from x-pack/plugins/apm/public/services/__test__/SessionStorageMock.ts rename to x-pack/plugins/apm/public/services/__mocks__/SessionStorageMock.ts diff --git a/x-pack/plugins/apm/public/services/__test__/callApi.test.ts b/x-pack/plugins/apm/public/services/callApi.test.ts similarity index 97% rename from x-pack/plugins/apm/public/services/__test__/callApi.test.ts rename to x-pack/plugins/apm/public/services/callApi.test.ts index f82201bbd4de8..1e606ac4b9aa9 100644 --- a/x-pack/plugins/apm/public/services/__test__/callApi.test.ts +++ b/x-pack/plugins/apm/public/services/callApi.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mockNow } from '../../utils/testHelpers'; -import { clearCache, callApi } from '../rest/callApi'; -import { SessionStorageMock } from './SessionStorageMock'; +import { mockNow } from '../utils/testHelpers'; +import { clearCache, callApi } from './rest/callApi'; +import { SessionStorageMock } from './__mocks__/SessionStorageMock'; import { HttpSetup } from 'kibana/public'; type HttpMock = HttpSetup & { diff --git a/x-pack/plugins/apm/public/services/__test__/callApmApi.test.ts b/x-pack/plugins/apm/public/services/callApmApi.test.ts similarity index 93% rename from x-pack/plugins/apm/public/services/__test__/callApmApi.test.ts rename to x-pack/plugins/apm/public/services/callApmApi.test.ts index 2307ec9f06bb5..5906053cbd810 100644 --- a/x-pack/plugins/apm/public/services/__test__/callApmApi.test.ts +++ b/x-pack/plugins/apm/public/services/callApmApi.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as callApiExports from '../rest/callApi'; -import { createCallApmApi, callApmApi } from '../rest/createCallApmApi'; +import * as callApiExports from './rest/callApi'; +import { createCallApmApi, callApmApi } from './rest/createCallApmApi'; import { HttpSetup } from 'kibana/public'; const callApi = jest diff --git a/x-pack/plugins/apm/public/utils/__test__/flattenObject.test.ts b/x-pack/plugins/apm/public/utils/flattenObject.test.ts similarity index 96% rename from x-pack/plugins/apm/public/utils/__test__/flattenObject.test.ts rename to x-pack/plugins/apm/public/utils/flattenObject.test.ts index a71ecf73bad3f..68f77573949ea 100644 --- a/x-pack/plugins/apm/public/utils/__test__/flattenObject.test.ts +++ b/x-pack/plugins/apm/public/utils/flattenObject.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { flattenObject } from '../flattenObject'; +import { flattenObject } from './flattenObject'; describe('FlattenObject', () => { it('flattens multi level item', () => { diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/__snapshots__/get_buckets.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/get_buckets.test.ts.snap similarity index 100% rename from x-pack/plugins/apm/server/lib/errors/distribution/__tests__/__snapshots__/get_buckets.test.ts.snap rename to x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/get_buckets.test.ts.snap diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts similarity index 92% rename from x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts rename to x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts index ff7d05efc1802..e05e7d3df2828 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getBuckets } from '../get_buckets'; -import { APMConfig } from '../../../..'; -import { ProcessorEvent } from '../../../../../common/processor_event'; +import { getBuckets } from './get_buckets'; +import { APMConfig } from '../../..'; +import { ProcessorEvent } from '../../../../common/processor_event'; describe('get buckets', () => { let clientSpy: jest.Mock; diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.test.ts similarity index 80% rename from x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts rename to x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.test.ts index a319bba1eabe1..711790d0c4aae 100644 --- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getEnvironmentUiFilterES } from '../get_environment_ui_filter_es'; -import { ENVIRONMENT_NOT_DEFINED } from '../../../../../common/environment_filter_values'; -import { SERVICE_ENVIRONMENT } from '../../../../../common/elasticsearch_fieldnames'; +import { getEnvironmentUiFilterES } from './get_environment_ui_filter_es'; +import { ENVIRONMENT_NOT_DEFINED } from '../../../../common/environment_filter_values'; +import { SERVICE_ENVIRONMENT } from '../../../../common/elasticsearch_fieldnames'; describe('getEnvironmentUiFilterES', () => { it('should return empty array, when environment is undefined', () => { From 24db499ad524cc7709ffbd4ed23966cd21808e2f Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 7 Jan 2021 19:27:06 +0100 Subject: [PATCH 08/24] use less strict parser for JSON. tsconfig is in less restictive format (#87658) --- src/dev/run_find_plugins_without_ts_refs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dev/run_find_plugins_without_ts_refs.ts b/src/dev/run_find_plugins_without_ts_refs.ts index ad63884671e24..995a22bf3e583 100644 --- a/src/dev/run_find_plugins_without_ts_refs.ts +++ b/src/dev/run_find_plugins_without_ts_refs.ts @@ -19,6 +19,7 @@ import Path from 'path'; import Fs from 'fs'; +import JSON5 from 'json5'; import { get } from 'lodash'; import { run } from '@kbn/dev-utils'; import { getPluginDeps, findPlugins } from './plugin_discovery'; @@ -88,7 +89,7 @@ function isMigratedToTsProjectRefs(dir: string): boolean { try { const path = Path.join(dir, 'tsconfig.json'); const content = Fs.readFileSync(path, { encoding: 'utf8' }); - return get(JSON.parse(content), 'compilerOptions.composite', false); + return get(JSON5.parse(content), 'compilerOptions.composite', false); } catch (e) { return false; } From 1b6f737546249ca4ae0b7876d00db37d202fdcf0 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 7 Jan 2021 19:27:18 +0100 Subject: [PATCH 09/24] task_manager to ts project (#87646) --- x-pack/plugins/task_manager/tsconfig.json | 19 +++++++++++++++++++ x-pack/test/tsconfig.json | 1 + x-pack/tsconfig.json | 2 ++ x-pack/tsconfig.refs.json | 1 + 4 files changed, 23 insertions(+) create mode 100644 x-pack/plugins/task_manager/tsconfig.json diff --git a/x-pack/plugins/task_manager/tsconfig.json b/x-pack/plugins/task_manager/tsconfig.json new file mode 100644 index 0000000000000..a72b678da1f7c --- /dev/null +++ b/x-pack/plugins/task_manager/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "server/**/*", + // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 + "server/**/*.json", + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, + ] +} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 5b05628d618a7..b67171f50859a 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -32,6 +32,7 @@ { "path": "../plugins/features/tsconfig.json" }, { "path": "../plugins/embeddable_enhanced/tsconfig.json" }, { "path": "../plugins/licensing/tsconfig.json" }, + { "path": "../plugins/task_manager/tsconfig.json" }, { "path": "../plugins/telemetry_collection_xpack/tsconfig.json" }, { "path": "../plugins/ui_actions_enhanced/tsconfig.json" }, ] diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index f6911d1203104..1182732e64673 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -10,6 +10,7 @@ "plugins/embeddable_enhanced/**/*", "plugins/licensing/**/*", "plugins/security_solution/cypress/**/*", + "plugins/task_manager/**/*", "plugins/telemetry_collection_xpack/**/*", "plugins/ui_actions_enhanced/**/*", "test/**/*" @@ -48,6 +49,7 @@ { "path": "./plugins/features/tsconfig.json" }, { "path": "./plugins/embeddable_enhanced/tsconfig.json" }, { "path": "./plugins/licensing/tsconfig.json" }, + { "path": "./plugins/task_manager/tsconfig.json" }, { "path": "./plugins/telemetry_collection_xpack/tsconfig.json" }, { "path": "./plugins/ui_actions_enhanced/tsconfig.json" } ] diff --git a/x-pack/tsconfig.refs.json b/x-pack/tsconfig.refs.json index 0516f414963ef..d5012df00beb0 100644 --- a/x-pack/tsconfig.refs.json +++ b/x-pack/tsconfig.refs.json @@ -6,6 +6,7 @@ { "path": "./plugins/global_search/tsconfig.json" }, { "path": "./plugins/features/tsconfig.json" }, { "path": "./plugins/embeddable_enhanced/tsconfig.json" }, + { "path": "./plugins/task_manager/tsconfig.json" }, { "path": "./plugins/telemetry_collection_xpack/tsconfig.json" }, { "path": "./plugins/ui_actions_enhanced/tsconfig.json" }, ] From 19687765b1a93916ec00353ede3f9938834c024d Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Thu, 7 Jan 2021 13:27:31 -0500 Subject: [PATCH 10/24] [Canvas] Remove recompose and convert to Typescript expression component (#86969) * Remove recompose from expression component * Fix type check * Fix expression not updating Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../{expression.js => expression.tsx} | 30 ++++- .../public/components/expression/index.js | 112 ---------------- .../public/components/expression/index.tsx | 124 ++++++++++++++++++ .../expression_input/{index.js => index.ts} | 0 .../components/toolbar/toolbar.component.tsx | 1 - 5 files changed, 147 insertions(+), 120 deletions(-) rename x-pack/plugins/canvas/public/components/expression/{expression.js => expression.tsx} (86%) delete mode 100644 x-pack/plugins/canvas/public/components/expression/index.js create mode 100644 x-pack/plugins/canvas/public/components/expression/index.tsx rename x-pack/plugins/canvas/public/components/expression_input/{index.js => index.ts} (100%) diff --git a/x-pack/plugins/canvas/public/components/expression/expression.js b/x-pack/plugins/canvas/public/components/expression/expression.tsx similarity index 86% rename from x-pack/plugins/canvas/public/components/expression/expression.js rename to x-pack/plugins/canvas/public/components/expression/expression.tsx index 37cf1b821d9fd..141963d479724 100644 --- a/x-pack/plugins/canvas/public/components/expression/expression.js +++ b/x-pack/plugins/canvas/public/components/expression/expression.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { FC, MutableRefObject } from 'react'; import PropTypes from 'prop-types'; import { EuiPanel, @@ -16,19 +16,26 @@ import { EuiLink, EuiPortal, } from '@elastic/eui'; +// @ts-expect-error import { Shortcuts } from 'react-shortcuts'; import { ComponentStrings } from '../../../i18n'; import { ExpressionInput } from '../expression_input'; import { ToolTipShortcut } from '../tool_tip_shortcut'; +import { ExpressionFunction } from '../../../types'; +import { FormState } from './'; const { Expression: strings } = ComponentStrings; const { useRef } = React; -const shortcut = (ref, cmd, callback) => ( +const shortcut = ( + ref: MutableRefObject, + cmd: string, + callback: () => void +) => ( { + handler={(command: string) => { const isInputActive = ref.current && ref.current.editor && ref.current.editor.hasTextFocus(); if (isInputActive && command === cmd) { callback(); @@ -40,18 +47,28 @@ const shortcut = (ref, cmd, callback) => ( /> ); -export const Expression = ({ +interface Props { + functionDefinitions: ExpressionFunction[]; + formState: FormState; + updateValue: (expression?: string) => void; + setExpression: (expression: string) => void; + done: () => void; + error?: string; + isCompact: boolean; + toggleCompactView: () => void; +} + +export const Expression: FC = ({ functionDefinitions, formState, updateValue, setExpression, done, error, - fontSize, isCompact, toggleCompactView, }) => { - const refExpressionInput = useRef(null); + const refExpressionInput = useRef(null); const handleRun = () => { setExpression(formState.expression); @@ -78,7 +95,6 @@ export const Expression = ({ ({ - pageId: getSelectedPage(state), - element: getSelectedElement(state), -}); - -const mapDispatchToProps = (dispatch) => ({ - setExpression: (elementId, pageId) => (expression) => { - // destroy the context cache - dispatch(flushContext(elementId)); - - // update the element's expression - dispatch(setExpression(expression, elementId, pageId)); - }, -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { element, pageId } = stateProps; - const allProps = { ...ownProps, ...stateProps, ...dispatchProps }; - - if (!element) { - return allProps; - } - - const { expression } = element; - - const functions = Object.values(allProps.services.expressions.getFunctions()); - - return { - ...allProps, - expression, - functionDefinitions: functions, - setExpression: dispatchProps.setExpression(element.id, pageId), - }; -}; - -const expressionLifecycle = lifecycle({ - componentDidUpdate({ expression }) { - if ( - this.props.expression !== expression && - this.props.expression !== this.props.formState.expression - ) { - this.props.setFormState({ - expression: this.props.expression, - dirty: false, - }); - } - }, -}); - -export const Expression = compose( - withServices, - connect(mapStateToProps, mapDispatchToProps, mergeProps), - withState('formState', 'setFormState', ({ expression }) => ({ - expression, - dirty: false, - })), - withState('isCompact', 'setCompact', true), - withHandlers({ - toggleCompactView: ({ isCompact, setCompact }) => () => { - setCompact(!isCompact); - }, - updateValue: ({ setFormState }) => (expression) => { - setFormState({ - expression, - dirty: true, - }); - }, - setExpression: ({ setExpression, setFormState }) => (exp) => { - setFormState((prev) => ({ - ...prev, - dirty: false, - })); - setExpression(exp); - }, - }), - expressionLifecycle, - withPropsOnChange(['formState'], ({ formState }) => ({ - error: (function () { - try { - // TODO: We should merge the advanced UI input and this into a single validated expression input. - fromExpression(formState.expression); - return null; - } catch (e) { - return e.message; - } - })(), - })), - branch((props) => !props.element, renderComponent(ElementNotSelected)) -)(Component); diff --git a/x-pack/plugins/canvas/public/components/expression/index.tsx b/x-pack/plugins/canvas/public/components/expression/index.tsx new file mode 100644 index 0000000000000..fc4f1958ecb33 --- /dev/null +++ b/x-pack/plugins/canvas/public/components/expression/index.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useState, useCallback, useMemo, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { fromExpression } from '@kbn/interpreter/common'; +import { useServices } from '../../services'; +import { getSelectedPage, getSelectedElement } from '../../state/selectors/workpad'; +// @ts-expect-error +import { setExpression, flushContext } from '../../state/actions/elements'; +// @ts-expect-error +import { ElementNotSelected } from './element_not_selected'; +import { Expression as Component } from './expression'; +import { State, CanvasElement } from '../../../types'; + +interface ExpressionProps { + done: () => void; +} + +interface ExpressionContainerProps extends ExpressionProps { + element: CanvasElement; + pageId: string; +} + +export interface FormState { + dirty: boolean; + expression: string; +} + +export const Expression: FC = ({ done }) => { + const { element, pageId } = useSelector((state: State) => ({ + pageId: getSelectedPage(state), + element: getSelectedElement(state), + })); + + if (!element) { + return ; + } + + return ; +}; + +const ExpressionContainer: FC = ({ done, element, pageId }) => { + const services = useServices(); + const dispatch = useDispatch(); + const [isCompact, setCompact] = useState(true); + const toggleCompactView = useCallback(() => { + setCompact(!isCompact); + }, [isCompact, setCompact]); + + const dispatchSetExpression = useCallback( + (expression: string) => { + // destroy the context cache + dispatch(flushContext(element.id)); + + // update the element's expression + dispatch(setExpression(expression, element.id, pageId)); + }, + [dispatch, element, pageId] + ); + + const [formState, setFormState] = useState({ + dirty: false, + expression: element.expression, + }); + + const updateValue = useCallback( + (expression: string = '') => { + setFormState({ + expression, + dirty: true, + }); + }, + [setFormState] + ); + + const onSetExpression = useCallback( + (expression: string) => { + setFormState({ + ...formState, + dirty: false, + }); + dispatchSetExpression(expression); + }, + [setFormState, dispatchSetExpression, formState] + ); + + const currentExpression = formState.expression; + + const error = useMemo(() => { + try { + // TODO: We should merge the advanced UI input and this into a single validated expression input. + fromExpression(currentExpression); + return null; + } catch (e) { + return e.message; + } + }, [currentExpression]); + + useEffect(() => { + if (element.expression !== formState.expression && !formState.dirty) { + setFormState({ + dirty: false, + expression: element.expression, + }); + } + }, [element, setFormState, formState]); + + return ( + + ); +}; diff --git a/x-pack/plugins/canvas/public/components/expression_input/index.js b/x-pack/plugins/canvas/public/components/expression_input/index.ts similarity index 100% rename from x-pack/plugins/canvas/public/components/expression_input/index.js rename to x-pack/plugins/canvas/public/components/expression_input/index.ts diff --git a/x-pack/plugins/canvas/public/components/toolbar/toolbar.component.tsx b/x-pack/plugins/canvas/public/components/toolbar/toolbar.component.tsx index 6905b3ed23d3f..7151e72a44780 100644 --- a/x-pack/plugins/canvas/public/components/toolbar/toolbar.component.tsx +++ b/x-pack/plugins/canvas/public/components/toolbar/toolbar.component.tsx @@ -21,7 +21,6 @@ import { import { WorkpadManager } from '../workpad_manager'; import { RouterContext } from '../router'; import { PageManager } from '../page_manager'; -// @ts-expect-error untyped local import { Expression } from '../expression'; import { Tray } from './tray'; From cf641f7303be7f12fed033d5a4cd646a063c3df4 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Thu, 7 Jan 2021 13:44:51 -0500 Subject: [PATCH 11/24] [Security Solution][Endpoint][Admin] malware custom notify user message tests (#87603) --- .../apps/endpoint/policy_details.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index e344d4c3c27e4..f53c1c589daab 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -67,6 +67,42 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); + describe('on the Malware protections section', () => { + let policyInfo: PolicyTestResourceInfo; + + beforeEach(async () => { + policyInfo = await policyTestResources.createPolicy(); + await pageObjects.policy.navigateToPolicyDetails(policyInfo.packagePolicy.id); + await testSubjects.existOrFail('malwareProtectionsForm'); + }); + + afterEach(async () => { + if (policyInfo) { + await policyInfo.cleanup(); + } + }); + + it('should show the custom message text area when the Notify User checkbox is checked', async () => { + expect(await testSubjects.isChecked('malwareUserNotificationCheckbox')).to.be(true); + await testSubjects.existOrFail('malwareUserNotificationCustomMessage'); + }); + it('should not show the custom message text area when the Notify User checkbox is unchecked', async () => { + await pageObjects.endpointPageUtils.clickOnEuiCheckbox('malwareUserNotificationCheckbox'); + expect(await testSubjects.isChecked('malwareUserNotificationCheckbox')).to.be(false); + await testSubjects.missingOrFail('malwareUserNotificationCustomMessage'); + }); + it('should preserve a custom notification message upon saving', async () => { + const customMessage = await testSubjects.find('malwareUserNotificationCustomMessage'); + await customMessage.clearValue(); + await customMessage.type('a custom malware notification message'); + await pageObjects.policy.confirmAndSave(); + await testSubjects.existOrFail('policyDetailsSuccessMessage'); + expect(await testSubjects.getVisibleText('malwareUserNotificationCustomMessage')).to.equal( + 'a custom malware notification message' + ); + }); + }); + describe('and the save button is clicked', () => { let policyInfo: PolicyTestResourceInfo; From 94b6087d744751553bede228ade56d1f2ea2176a Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Thu, 7 Jan 2021 13:50:42 -0500 Subject: [PATCH 12/24] [Security Solution][Resolver] Updated baselines to account for re-centering (#87547) --- .../screenshots/baseline/first_child.png | Bin 8701 -> 7270 bytes .../baseline/first_child_selected.png | Bin 11554 -> 9760 bytes ...d_selected_with_primary_button_hovered.png | Bin 11948 -> 9760 bytes ...irst_child_with_primary_button_hovered.png | Bin 10580 -> 9036 bytes .../screenshots/baseline/origin.png | Bin 18701 -> 14572 bytes .../screenshots/baseline/origin_selected.png | Bin 19423 -> 15181 bytes ...rigin_selected_with_first_pill_hovered.png | Bin 19423 -> 15181 bytes ...igin_selected_with_first_pill_selected.png | Bin 18684 -> 14517 bytes ...n_selected_with_primary_button_hovered.png | Bin 20000 -> 15579 bytes .../origin_with_primary_button_hovered.png | Bin 19918 -> 15579 bytes .../screenshots/baseline/second_child.png | Bin 13381 -> 10965 bytes .../baseline/second_child_selected.png | Bin 15991 -> 13265 bytes ...d_selected_with_primary_button_hovered.png | Bin 16647 -> 13300 bytes ...cond_child_with_primary_button_hovered.png | Bin 15284 -> 12791 bytes .../test_suites/resolver/index.ts | 2 +- 15 files changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/plugin_functional/screenshots/baseline/first_child.png b/x-pack/test/plugin_functional/screenshots/baseline/first_child.png index 2d9fd1f039119f639330483d7b290202c741ce93..54385625951bd6b9df4d63166260f48b33c9724d 100644 GIT binary patch literal 7270 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 literal 8701 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 VDMTK42aJM3UFDK;zM?t)e*jF)Ysdfq literal 11554 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 VDMTK42aJM3UFDK;zM?t)e*jF)Ysdfq literal 11948 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 diff --git a/x-pack/test/plugin_functional/screenshots/baseline/first_child_with_primary_button_hovered.png b/x-pack/test/plugin_functional/screenshots/baseline/first_child_with_primary_button_hovered.png index 9eacdc920bacd9534588442337352d8c8882b897..a49aa86d029b4ea3bc4157779509a8f666c10c71 100644 GIT binary patch literal 9036 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 S;D1X}X{l?Uf2(TZ_x}L+K4|d( diff --git a/x-pack/test/plugin_functional/screenshots/baseline/origin.png b/x-pack/test/plugin_functional/screenshots/baseline/origin.png index 5df290b3a2cff94eb00726131a2c62100ac18d52..889101f961a351a6190f847d4e485f11fadbb244 100644 GIT binary patch literal 14572 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 K_N~mlfd2;p6VB}b diff --git a/x-pack/test/plugin_functional/screenshots/baseline/origin_selected_with_primary_button_hovered.png b/x-pack/test/plugin_functional/screenshots/baseline/origin_selected_with_primary_button_hovered.png index b1fd64d9f0b700727799bc957b481465029fbccc..be607c06df3e8119e39d4fa7ae00f2a160fbc2c0 100644 GIT binary patch literal 15579 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 diff --git a/x-pack/test/plugin_functional/screenshots/baseline/origin_with_primary_button_hovered.png b/x-pack/test/plugin_functional/screenshots/baseline/origin_with_primary_button_hovered.png index ac3892e4920586d674dea12463df3eff4eb0de72..be607c06df3e8119e39d4fa7ae00f2a160fbc2c0 100644 GIT binary patch literal 15579 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�XV&AK11N%EN0TOQ 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.O2;^NTv!^1Uob?p|$ znqQfe#C3K3r8BlVnF%|aV)A@{Ic7h7iuK#@20uJ{;)M0@A01!5eEF1Tt~}s9y7%c8)`lwJE3rr1CGvomzj{t}%y4lP0To z<_xvz!s6mo=SxE|e0q915eF-3hUUJfG^Rg4WvL7friaa-Mn%{6pFMk)iA$Wy{7~$W z`t#kl3cC}7@(K&%kpRd_YyKYrkU(<60Q@RP>O6ff5cw~@ z4C!t$OAbkJZ}+ti2h}}llbt8HN|VxLqP-P#^!3?$dwW5#Z?4tv!8xc95Q_>Q93700rK;=Du^mt@tEM?BEl~YVh?}-;&Gy zNm}58H)7SVvqXR46TPszQ9UA=O zURCP{O(n6|`HJ72T0g%RN3(M);bl2ZZ?S!Ts%r;WbL0EF$c`)xtYg;+opTtpWozmw z&H}Le+D&PxIV4`d`0(0ieOY!unTN3T5^FbzLOC zat5b4K9+x*(uj=rL@P-5pmO7Q+jK^r|#vwdc~B9!HJ|A zA4<5@fZoXW6RBks9lOv2k6m(ea}#PUqn>1C-Fy31Dry||GADC`(8N~|`FeVKI4!Nw z+Ut&vLcGlG3T0k%hebqoPx5Mf``4wfUq`yglIGzVtu!3wE=I@Dso#f5?0t;N!?Fbi zV!^(4*TbN(`7@oJow6Bxig(DCHnz5IFThdw$L=zU4jC;hEKmf7U6NVcKJ4NI1HE>k zqX~qv&h3A^>1ds6=YwO_y+drWC-1FmJoC3>S!dxrMDFMi60RfPWdu@&jjnd!=~J$W zg^pJ(6H_e{?B5fotrZt)Ca1V|Vzw^e1PF9-uo@(Yi6iL3v(orFZ@MBDO`Ih? zlw7Fhi|{0`cP5=}jR+giX+fC%_urmEJB0|5@2KFO-Q#X=wVh?$o?+X}3;!HIGvHa_ zyk0mj=$;#jmV-HgGfB<)WxRqjW_-Zup@IP2i|2olXm&Q=q;V+#r_OqhZw; zsR9JZXLtiu1XzD0{^!m8acODlcuSIdf!Xcx{TcpSx7&hYrHBa0I3&6NXdV_qP^YWw zwegkrE`p7PR*lRzt2RAhOAyG-s4?{F`puiR&2hpA3*PwsRC(O`q-kjaLXsu_=DaP< zcZLFY_Y~ML@}J$!!Z$XaWIjGdpbU)bl0~_>hYNzygxhziTbR}K6&>pQF&vS1&H=RY}UWNv~mEs z7AIPHwSWo1L+j-$13tX3!RaG;WYE!#a#t1j>;UnAxVuZg0!T)&9~KA3sZ&RyjPh^e z0L(S+H;y9w-CLpUozr)OyRNRTDlC1`XF7N~VPe7-9&v63a+lfz&$^h7%**n~+{}jh zK&S#ndRtul`OBBOwWS#polXd7X>CHLs+p96O;PlbhNq%<+pVG%8xoG{lgB3}B(Hxt zvbOvyT#O^!weg@Aa6`=`V5h*Ku(qU!1qJBA&$`OmzP^eTca}z?jsCv)23jkIQC!eQAgVRs_^x{wxeA zb?TIa%a95Jh}d=oH3x^IjGUs@f9Nwj2Bl})o}sO#Mhi%H>$A3rXRi%@&S!A6StN5T zw`}%tQPF<$vuAff0%-(l-F@le#cF52MIJ!mUVtV=pJfcI?0zKZ&?4|J67sM%ANL_? z@4f4&QGqq8c+gKCLFsvTmhO$;<{c;kTi#gKB9u)3sSZNndaR;{7ZsvQ+$TqT06~qa z{1k!Q+4Q`z1l@-`*MAChf2!d6*JDU=Xp>}m4m@nMF?v$he~lfS12l=S!1 zoKPNvwR(Wea`uA*q z+9}5_m)gBDu)V4!>YzN?ska~~B7zBwc?GH@=ZYl`nL#{OdA+xtTtTj3+{elJ87Qap zjh^bE5BCrrI{xti(b2hb^)RTOM3jq%RE88=H_LuWM@c(_$q_5}C+`}euz+%RJ0WSaW=`rk9%(R+EMk?7sS$atm1P1tm{Y)(nEWDJeekFp2( z0vB%rT`{@Uah7*#p+)Se7C0zWcdxD%-GBV}5IqxDfLJWf<;32-dlMc!$OrF01`xNP zs#HI8CTX}fnC;uY|B{hW z6N?|27D#gsJR@`5FxR>>Ta$MRrcHE&`Wqg@7`e@73ePpPb=+B*ZJqD){O;7}yV6g} zP)s`;&N2TW55H?LM?1|3CKG>Hdjto#nXRRx!yH(h0!egEX*oX{sR`V&!Atc`Od^~I z%5T?=x`GLt+S-tsV*l~KToeHQg!rNXh}1njrB9tYg-{xyimkPo3#n2lbAULoF}MwR zW3jyuO0mDGGStV=DJaE{uxAbhahg3WDtZ7loy%gYOrc}9XfX4^^Or8MLBaP_>jX#b zqC#8I-Mchkt-3(k-SE(^NM*OVe>zJRtXCmLEs(4TXxC);tvTR<>OOh)EOcq6`?D}d z_%PzEDH9AtuWmD+lZdY~a-4Vw-ql-ZSLpEhaH+Mm^{0UWof8~Z&}1Q{o(dN_dx#AP zYAv8K8UFv#fB!N|5s+|Wt-Hz=dLVs1a}}UuN$V!o&d;Cc56Zd(EH8T^`Ka`jad8x| znTw{T+OQ0D@?<1r96UgP9fS~mdwtMuWwsy52Sg&T8Gin3TM-cV4(N37;mKzJ16Rkz z{F~eTx7?K?z*`Ydoh@I~0Yk&H+hUdTqm6}@we+wUJy+QbJ+_Vw$>pj1NGCm^FF6z#|tpu)DNN=rxEiZT>^3J|a<8RvjVY$B1y zUYc(n19=)m!m>0+>^tleZZA%_uKz0jjM&wtB~b$Ifz;QwwujBP*Sc4~JXH#6UsRw8 z#GXZ1?ANY}Mxc9g?4$^IYAiM_ttJ0wUr9V*2C&M;&aSR=vb;GhZRzsOO-%y8Tu&fw z4~bsMcg^K3Jp9ELULUG{i1#G)j6k~}lWZcS4EWzS&O~=2?r;7oTI4Y&Q9h*Fme613 z6`!6SIeDAt;IsVmk=NV+6bAeM{u)TqJoNkHnjmp{|3hVWpymwH{{qI|h}4-;BK!8l zCM7k_s~=)34QCX6A5bWa4r65?zVNPLkefdPb5jv;X#;)^r3dcNDPe;9h&LQuBr(C_ zP}TK$@4Ic@chzg)$)$rK*Rs8t#l~XlI*=3E#MwhthVpa{S>9@3uNy^0J;5dlaA?KE zp=%iBqQ-zKb1V9=$||C0n`6QvG87r;wyyHtuMew76GN$iD8eNQ?QCw$gWI&%c`igg VlM|nV|IZ+xEb^?lDd-}&SGarVz)wljO*d*!vRweAVi(oi8MWgtCw?i{(AsuKL%IUHMX z?nO)p{!S{{9-KROEmciPLDvgs={-qmdPr00>0-T&+Ra;*)Jlp}&kXFUxVHju|E*j_L5QiyU47=XJ;d`axbYg*JVIML7#f*b- zx^Z{cVK%zc@@8_ChT@gDPD{)klH_|=;tct?!DsiZi{gKO3IBipa`NV`uI5~$O54qy znSHQ$_o;QL-`%5`FTakKw|}W3pZtnjE!RF28}pl`IQ#l`XyM$I?g!nWA181Wa0&2( zH0d!eTPAw(jw4}iL_6$2+7oeJOMbDf6QO$G&Ec)eezRPylxUZE80vA{a2XE zm)p)3BMdDJE%sl*GfkEtBKYS@1=sU5E54hYCvpdz#Z2Y0O;QQ5z!sC%Bn~kToe^D` zh{E}VFgGpvXn6$%SA23Bd=XWyt6EyMdto(uz3nP-hVK2j2t_wHVYA%2&3Wzn_uF45 z3)|c%aG38lysoLEL%mH-4%R~{VsPVN#vcqXgeyaukkDL&1O<k<&PZbQ56sBe;#K#M|ezHIvBqYQgESfply!zrBHw3=Xbsr>lYMF65Ijn9_ z)w>_Twpz;B+`IKFl{{qkMYhJJ9-fZ1wY1b_AS@WueEIaGeZ?X|4_-uW+IE%t`@ zZCnOoUVMcv>Y$9$hQk%@fxNm*uIJI%d`i@tl$2rj-D881&EAtZcMPwU-EmjfKF6RQ zr|LEz-@>agZ(r_F#|BYv6()Zfot%4FRXQ4S>HQkAL6}I11FxKW$+sPbjt<~mL@Vm~ zW`>exmWxF-v%Q`ZckJc&6$S%B6{I78ETyHT+CeFzb+L~v=FD2r#FvP5{YB!aPuFj9%I!TX87Z5T4SpZ8 z!>y;I^AU-6!A9@sByn`_=A`tBu0AGkBg7!x#B(yL5yy8`VdC8}MNU~+jFse0gUS=F zR<;db-u{t*Ly8|ivnaAqsAmIex#zh{rlT#AZdx&*ms=|MO&W?du{+W$^Q^ltMblRH zc0sEy5jbQ5^3FHC&y+UtE)GMmqLn;PY8>9@t-R-Z0!a8Pq#E z$TwS8?m7N-85Q8Z-uvsme$O`zsdb6!?~UU0sh)Ho7}V6$q#~Tg#U6pML7i-4E4b7Y zu~pb1$(e4l+MW6oiG#e4w)XaCU+0^8KMxMZg>$AOVlqyr;Wz4M?+`L;X-ueHWof5N z*|oM46%|FL`&wUG9x{2|uPGho)^o`19VNHJh2|Gq{jyrz@Md6g6_J#mZ{HS`@lM~^ zN#V+-`?Xnzo!MH$qm$N76iE9&Q&h_pmmoV-k;9w~i4z{Nl6LU1){2AtFc^`ZY3tXTlsBDh_Sfnh z^*p^LxztA*;CIk@v{GNt30-zUMk0I{wim5H#L}a8-}Hl!$}v$p(>5!BiD!z z>sc+$>TZ3C3jaH(fP&vPkPV3h%6&l9kx3j+(GOQFJU=pTmn-B2zOi#$Nb@kaN?5KZ zqn_~1_|*@anl>|OEpOaAnpu*4LTq!qr3lcVl;gx>b(!h#Sm%;!pZ0mg)@r4g&58_s z^zr!XfmrS;=+Yv9Ek9lz&6S**ufJ^_3s{i#i&;JXL6eBdI&70!`ymLIUfyF)w3=w# z_Y+*d1@~RiZhKja#V*<|XCAKRs6?&k-j6Wq*B0dcOW%)jN`KdwPj16qOrer`aKks50j>EU0kllriyNLJsUbF9g zp)iiqlx;7sbQnt1(cEXR89%WhjA#Ay^1ENf#8T}?k48zK^X@oT|Q4r;<{(^3CZuYg2K zg!sf?jWg^C?b+)@cP9(K<}0pGd~JrFp1Ys# z1%i&L6d{@1J;Xc3_w@6tILW;WgW(xvf5{O14T8~33U~yaLjM`vfrP{i#E?BsZXTHQ z_#bi44`IrrfQO&V&C$pBOn2IJ0EO7My9!P@1UL-R5l73Ke#xw98@J#f@e1=`CsViK zZu31^Rl)&&q-ci#H$3$8^dqocI$F{45Hld?JSz|Kog_5zbfjT{LBJ-sq@=hEY_=d#Dz*CLk!5vZchebL z=hLL0SPxSa<|`1zyM5)pAM5iYQ$=!P?N;rYjg4DpcFX*3%~;zjf#wf6DNY|OK4w^Y z`)rTI2Q)0HZnsQYdIPL;TdF^7E)362_xcN@u!e&QtNW!hEC=En8Y{^^wxix=Ax*p81Kiy){87SdYrE|7CN zAKyrWH1UEo+FDz6`^kPwmE3pB1^$9X=GndZ#l!(|5pA7Oc{hBA4?>>J@7;UVE$v82 zsd(5_k%4TI!Gxr-7RXtA(mQ0KO7G<5@$hj72ta~~T-4Gs;sJ&X!u{|`(oJ$9Y{A=h z%(pOK+PQ?w{fn~`oBNL+$CAqf)T$Aer6b#)!_@LY6@_5+Gk?O+x>c;wjg~F?!f*{{ z=_f!YgAqOfIkkz93&(MQD#aX(yWiI_GWvj(nz@=Admls%8Zs@oINE0_^z~(NCtNfq z1uRKNLn^W{hLa^rr=0?2>NRC`W|qNLn8oAO9YJRq!q- z0>p%RwP_b;yx#GX&tlcW;!V2^l|mh(u7gF-hu&Az5B>s4Q8xA5#E|yOmoMRru%L7h ze^QI_8_GHAr%8VyRdyo)H{cDeq@o-c5YsY<)_S1(uFFW!S+2K5EF)dE{;NZl#=jRr zuJ->SA>{GSNN`(EYHM)4lzj@rcSJ>cp_=1pbtZuA!Y`nx{T%hY+w9cj4F5)50GS5iMUD?;D+4epFz|-~0oNeElq$uj@ zKR~V>d?%ryqZcx7=U0HT9%AGFcMa(V28BK5uZsW(P=J~o=X)PW4d6qnjwYOZSG2UE zk#(8r3kB_h*3-H)G*o{ud?Ow%TtZyNYUj@@vJD(()i#(A7vu&3Ilc%W8$S|w`!5T- z(J}CVL$&ler2=T4{1QkF6~6r8AbPShYSL%EGFBH}nEsKjNj&rLfcX#R56XEZ?>z}* z?`;}x65la}^Kp9-FYh&ZJZ{jzoau)rHX5k0 z{4$2gF0)kah5PqP3*Hj-wASuc(X)agaV&zliGgYMC5vub*JT^LcAH6bq%u;`ADuZbiIMC152yf>0%St-c?xl<==* zUHLMHUa#l?UMx{$FF@y12XVzzw}yiJ1X8I60^!(@3jQYioTtZPjD?fQMZWHw*?o1D zNjNEvfhgS07`i)T@@GAvNu6&3nTF+`@5l1o0iS3SKUP%M;W(|++xYCHmR8X6JR^Hh-Z zR>{Gjec!jIEhn^#R@FE&%bCRdB3dAP;LZ`g^klDwtrowi@$!pw#4#!0;s;6I#Tppg z*Ng?6^WO{)d4N2T_sr(H-*WN-MY$c_bC%c!eZRyum5%7*CRDgWFk(q_Q}e0XPqrAhj0t^@BRVZ+XnJXm0Md;AIWfm1Do zL&39C?FYK@YNH%fRtY2^IB*%bf(b#eo>5#tZt!Q_7*KNQViDMy_8_+RC~vZT+ZDx^ zWYl0B!OIVPe-;KIpP;}2hw6C|Ehu^!bvoc9)x2CB0P#Ay-~jHJfIH&>W;$9*hVWG}Yj^Cyl^%avCQsHD zJl+=+SM}fz-c|DLW?QIHk~uIAA3oO(Pp=NdjMVU3ew&+ndX4w3Wbh^nLoa!+<|F0n z%>jZH^g|WDS~+bUr`$tS2mrc<9>MuBIg>M-LzUx5Fi5s;$K(A1%y}nmHm=$EYfU#otr!I``(}% znR?IRRS9c%rFYe1?|riCz0-?C!fS2UR-~MKA(I19?GPtAsGje>Isu-a#As+ch$`qR zTCD_1E6FZ=5C44aKe!sD>Z$N1ab?S`ErO*jGl8?`^+<=q4AgAWASj9IxQ?N6$b=dZ#*P-7q z(YU37zkPSL=XQ%5|ivq-=k`!GOn=ri>iOjn+;sKb(ZvYGoRF+R~d) z4+g;HkIT<|aWl!BG5c zkOO|Z*B;A(O?hKjHcFES+g8eZQxysvEbtxtRnt^s94OwmxuoQYvn=lbo5%IefJi=J(@q7 zKNmTSX7pw?6^mBU91V?~#vjlQ#>e<2DXJNr{AEah`x>8Z(MlWM{x-X@c05-%+79VvCsa`~ zoZ&t~vthMzNi+d0W_r4xUb&OY`dCxV1t<4t+c7csR)gZ5mt=o+k&e)DE4}0Xcs1I- zxh`0kXp2ib*t~sZIjoD+=v5bK49F1MMmu7@XeqG6n(Yxd$k3xn3Jz_!XZV{t*RJt>5183Oy(FDY-7pmy6M2StTOKTDau7<}ZTSB7V%5Wkj+_cl<^$7d%9%n5NWi~sa&-|g(M>Ce zy(!>jheFUA1g11+h-X9`%Xt(-yi@7OJW?e}Eg?iCU?pd_VW)-XL)m(~JUMYf7)d&gMN0UY(l ziAC{!a#VjcPbQDr-q5Uf{P7O$2C%pG!g7zt{g#f`cDAJTg*x{j3t!b6?dyJ-R8|^E zj#$6$-Eth&%=18E$)woZngb20eU&&<2ZXeT%DrXAC6TBA(IeR#h-rWL@+jTS)0&Xl zleHqr-AtG3nr`^$km}_-4zkiUoWZm^KthSxmQ!`p@d%<=qt&Ba1Fcb2Eb#sV#2$vJ z?BjD8i36RJjgQQ#MV==**Yg~?+}n3sKB_frn=2n}1-Ya)X$ssveIy{(j#F zsGU1@C)jAg@G*Pz-GBGcCOC#P0jjy4a-yM%e!df0B)#d z$bv%-ILe1johy1elb>Ghfj}uDXYPyB{c5P$ zXEf|=mRU7j!D`dAneoU@nV_7by3U>p%@{TIym31CndbaZ*;^<>G3g8=83VwPwvI=+ zuT+FBI=Ja^ zb$eWT(o%UG6ob1B76ZCQj9wPB=#1r-%jjjqF%^hS2RAx#t&TgkLpjm&^T_Jv7y(O= zN#7ur4LW^@_TmuCxdEh(cn}?q<w01<-~H z+{d#sjxW9T=1s_e;o;Wo64AuefUFpM)gY}v>D!!cLq!MX5!#rSU8IRNy@2v<lWPzQ^Y9%H8u^TS>e$RLsU@537MSXazDj;lto1;OGS_PUFyUd`o zku!u*fx(0s*9)O0wUctzE!H9$Zp4!zd=}?WEdM5HXD6!o1eQgjzAm!pm&OJA9H(z& zqI7k^2Epzxj)4824d;D*347D@ZhL3+6y`PG4@d1h$CLE$RUdZP)Id?REW7Iuj=dp- z)7I#3Dx0Q>ydD4u!M**d*6_i`cl;AsD>;C|T~H}t8bVmWPmjl!Rr&KAE%lTG6O(y9F92NZGW)->9A4Sf)cuA&$fq3Yxf*TYNAWp|*j@_oXgzl8;l;K`F_IV+x7? zrU910#f_p<3tpr>M&Dk1Z(p}F9Omcz+Q|WZPFE{4``XHj|8P z9l9`|$1Kj_{;kQmObXx`?+sP2qqOA8>h0%?f`H_risiiS!YP=0uoBbN;$C|y(mFt1 zG>8jJ_vOAO*{BxbI1H#K-Al^bD5=+Uan}IH+qhBkJvc$s-;|%x)An-;{faVHV6flN zL}jIx@-=K-uBeLl9;Bc1&RZX`gSm>?Gy_b?v%!Jt+Gs%U0CD^8cHy^e6>vpFL|smk zE_m%{X-4;|MMCY8Uyr=&Gj4M7G06k|^bDD>KU)#dShG_C-Y0KZp!^QNvh^BfF>c7u zjX>K_Vf5{@Mu?|P^x??b=_Hkwvv_S%$J6DGi$5O}@)^N}>n*G*^!6-w6f{lAL!pqC zl@nXPpXb<_;xX4;_>vf^`l`%FFYNW(dxjd7@_)^<&pM7}@>2^itAaeLpHB`$HP_q~ z-@rP_L??iPpoC?;x~haN&bKR!3~2EV9r&NyY2FATTW@v?)$B2|lneIQi;YdpTJgu6{d-Z`xpy6p1Oq{if9NjjLtOD%LbqXh? zth~DZ5f{2h%-}^Y-B3k69Y(C&q{h9@c5Qx^A9O%aAvaBN=P3Cs)jGTUy-8O3sk0`# z0|Y1V|Halk=E${QZ(C&qckm9}SMT&jF2({ZQH>0mCEks^eAi!xIxG+(MPRD%n;j8C zZ%8(Wn%V9tker%FC|J?Bo@m6#b8EPGRBCrJo)1+5?X16`gHTF|Syz_?Hc}igPIyhz z&Co$TpOrYMNsGI|ms!G=ciF&&34VO3eJyRn@eB$x9le zZq!#yrJ@^&we&~AL){;jIx(iRz+MejEo_&RuhlCsm5&nwExZAGqm_8hwT$#^iOmzwJSNFxP$o*R11xxXX~5H;@bTBML(|h|HMs)j zc7TL!EZ(Y~#=M3-T52aVqzySJ?v1nAviWEx^BGwAytE@d#jq1zVka2FH&EOG|9uOtM1!Jh$iXJ*2JZ16RSc+XztU}_ z+naGJ8X@4ibYuLx`MB8F+7TB#v{2!{y8&EMuzeGKjuhCM7_#=->>=y79Kk6hzxb_? z&dqt@N8c==hVu9H10W1^!uHRb$evh|SV_5cya8Ne zNNE8x?NDabG&ck^WRsPp7rVFg22WKFj1MNLXROQW(-~l|a@%?z8PW}ixwgUD%B9j} zvS4V4gvlOZa%pI}3X6$U5M3czFe}5HF9mbqh;S?SR4q&z#>xK|=fUpMJFKle1$3Uc zFicY6?eXEUzTR7|uFX~d+Y=TRHt^^UdPA+zcP{}a?{LmIjGo&cZGq)fDPoZSVrF0u zM)e#4=$`Q*>JWV{v$-x=Wy1f04dOXSd#HS!%@o1F>Tei;PQs9uuGJ&v%xz{efx2$^ z{R@M!;IG*bF0nP}fLO>?@J~kX0K`<0N8$;nf`(twZZ`2W@ zJ-|cp-KP>>4aMCC2JN4z7hGqTOEw+hNgeH*4t29-yS0G8Hmm3Km?(mVR%POZ5Ssgr zT4KkzMZ}1FADmvx=q{NVee0$I0-bg8Xe?IFkCL*tMY z2^Zqmtj*&8nJjELiX2>ocQk;$zZFFSa{61GN5BaGlFIE=0rton{NhKl86p19$a|;B zq-M&u!RaHxF6=W*MN>WmU+W#TX#)+INiKpYGy=M^{kNF^#2r+J!jMO1P7Ge>854@z zU3nwEd>Qllq(5dTng&g!uW|V)7t7kiCYrG4#g@l#{s?MzN-D51aW;g9IzDJv4WbJg zYyX|t@u$5}HLUNBiH`q@xHB|!S2>!i+)|CXUts+A(li^iz8k5mf-5u0Oq-Jkl|Q5f zzXt&a4xrv(lLx2(PN;s5`l%G`gi3u`1IjwJ_&;~!sQRHAF{!iCKo+KuP%%F}J^-wR5 zi=@&~=U(nMCfhOPQQ`mwY}F{a;j;lxR@9U3GccP~C9sfTP;T?uTUoW(6E zn4UqA#RaAMAC&cj^Nv)i>7x5X_dFV&Zb~ze#vE$V!whh!v`-al8S0i;&6=aP~=Ld#_Zoc&^4m z`|iXAu(t_Krh(sy|0N3~)KcBLb!x+Av!9+W^k{XyS;lIMs0eiZTJG37)?sXYgO3Y0 zI1t1#xbt6(J6Q3xNA{SnX_ zwZ$e1>jDkNv)}0HpXSQe>V1(4oIZCXZBrs_NFbE=-qLVAg#FcrH$7~Z9+No)6TY{` zFeaIs#RSo_UBV}bqel{(2C)T)x74`o9LV%u``B2I5I|^ZlzVz5nIQtMCg{c%qpqoO z>)p+d&F3~z(}O4I@_RpB_Sq?RBKT2hY=snNWc2oY60JGeGB>kf2__>Bi$o5r5Fr8o31FU`;Kd7tj7nIev&vjj49s!*}I{Lp1?DJk9+hk`cZ`Zxwx z3sP|pN2P}+Cp!CQviy$sIl)smrgxmGd(6(ilQNp4@%G+TICnzNeC{^(oFJSiFh=TJ s_yCSMisBrn`?*VKymzYqU%x^uzqQ+{OyTwfKUY1crmUeHhQJYF0QJoYMHNBaiLzgAfzgu1Nl%-W9W|%Cn~)uD9E(3f909^ zE#myx2dXo8cM%YQ!|n<9-=7ixZ-4HQTdd%9VeMi^ zZ;Ehr`o$zsgL>NDc6X1QfxY-$`}nP8c-ZX{*`4K6_qa_~XtA+qCFKeu8r>_StN0e}#wKp865JCF@qXjnxTSIA$C*Zie=PcM%S0fZLHnvPh1)-xN!A3}7 zL5XBZkOVQKC(f1&qM}o3y$Sb&bOav6Znwl0PEFJ}2=_I&w8$x}%eQBs%g%rIc}Bvf zC#|T+6FrayFMB&xHeTn#^4QJMtn(Kx3CCq<)Qy|RL&se7jE)%-!rPv*5)wC1S8g?k zR9ej_ySnP1<{o8F1ji;WF5TsPvuaS|ps*CiKi9;2aL&9jT@g_DvMM@lHvV$W!=lp2 zl+ncn1D8@LVQXf@ypn?>-C>>YnU2nrbkTZpZUMi~pJnkKe!-bTNH{VR$JVnoitqms z%qB%5k(!n+3w=)Jc^+p_OuP-10Q^mhyn;I4FiO7Y3VYIew*wfRF${ZiUhuh=3kD_M z8(J1O(mY)?0bXNRS68Zy*7fZIDTWd^N9x@uitcGmyqT;ED=oEiJzQ-ML`nn&1?6bz z>fTFCw4C^6$!}}>6?b(m!t>&kfM`!*$If?L=V@DXh?Cm01qlJDv;ZrSEDS6POe+bC zK8dW!XVP_cqC8^BE%=+k(}%`2OuM?>y*e!;1EK>Tl_|MGJ6;XII2ygRB1FVrzb0i& zf9&%rF@JRD$8@9JnhLuK@U))S!rK>xdL2>OJudURiDYy-5u%qo4h{~XWTG*>y}Tk9 z8~c*d(tBdo7pacByVbKE+jJ!EH3_wsHn%J+bQ2FciL3jC>_ve6&ZiSAL7}0GUbCmd zoAV2{^=Gp(tjF)ToGy?gHBKrm2k|RC2Ovv-Hfi`-Lf42YQt7$J% zVx|*Z4KA zg0HH7$NT%c&*6%dfT(w>FAJ-kosyOdyNx6uLzc)wSet1O@KP)UyxEWoVnT?oCBN=2 zawSDlMq>4;e6+*q?M8yCh@ctC*u~as9etCc7><^{2 z*;M7d>dLnRMyFv_3-T)4J*ib!Y4$matqv>H+nHI5XlyfH+ti8s;}X3t8#K!zhXgGF zcsU@F&C)#3$HGRudQk2?E@@g^A>AwHpKUrQ=D!S>o)+Q8#53qOzHPhS$N%fJNB=4r z#O{+P4c2;|FaR#1GkVSImV4qb9~-Pt8%=rya9jv9Dmw`NDJT+7f=&eR1L7F-oZE7j zQ974_`z@SeE;gpWD^%ny8J!Acdy%eS@0q!%u+tVJTOmuw>rpxtPNU{6X?6ekkiaWZkpP2|$mZsYcUvjF zA3qs?BIj~{K=G^RcnvDXha%!A&==5@^lwtMO9n7Kd0~I$;N)H zVzMiK--p|jicg*hYfsiV-0bTYF{VltVi>A*QsH|uuBh_{w+OgU$K&+Rs~??6*-w^Z z>_2||001trvv=co<%@{kOI>f1NVI>v2PdH+0J-fi0UK(?LP!aiL_#5c5-@R#O>xoj z%81yrV6xQF$o-L#<4Oz)N(*yygRw2D(E{Cw{FhZc;pzYr0W|ifU!KD_&JX19bFI^7 z-^@k!`8}dWfvR@qe>vYfaR*>@cWhNP7y0GOm*rnge$zHHywyg=#wS8e8Kx^mkE?|o z6+baF&Zz)&qaM%?WZ~wHIzDzDwH^<(1SwF?pYjC8i|yTR-1t+@Dxs zakr@Y2DBQ{z#N%Wl6S&-IMYZr|Fm{jlYWF zpLGx5wH({uV=1;1$bE%tvN1k%eqhaEyM1w+n3%Y2C?$yt@zq#nf2^^-wf;ys?T zOkl?X4gKQT;*#4_oy~=y_ilhm-YaN=_-Zimzm@_}2pt>{wk!`oIB@~iD7e9KFkCYf zibIQC(g|N{a=ou#>7#0qDv)daaFcFoM=3jt-d=t?jaB=cyeQz+w33n0&AnSVwm-+t z%)91(bubv4cP(&DdyEw-UhEE_*g!CfrMNo9v?p#~YWcPHdN&KZ*+l7*d*rvII1pE~ zAeL%sghmUeI*)C0?MD+A&~K_}{1z3KT)8u?uW!^h zd4S?R0P>6#6&0xUkJocfin@`_s#6{#^Gd7>Xp+t!`?;Lv23`OURX%J#wXKNoD(YTH zYP3daGxjcDbZmbK16+Inh;1O9Atn~I2y-K7CoSBY5Ef#C#R+4T_GXPj3xPs#VNKm0 zXD;*mc<$AY1FT;vaMfTQ4I6w%RI;mL588O@>(xet)O4FPb#auJqF8 z?POhSgqT(!`0}zE2%(7e%m|Va+Yu6-vm;Xbz6yT31tq7bT8@Y~Att9!3i4((S?_Yd z|3bOy4i3*!1Z^KS)^EM4)8ml9G~)yUt-7?#$*6FtH%Lv;e*QTSATmY)hdS=a=TT8a zwwp7x5eUR~a{P9un8woX@89nJXt%JsXwe~m+1Z=*2@J)~0f1ysj_jnxhC=8OHvhz~ ztT)`-MEr?nH~#1&4&eZG45?6AIV1V>EqCHEoeLJx(_=N1f=SoEBnA@F6i5mXYF!5- z)(p(dk@NFJI+2l)D1HjCpZylCRr5wLgv6^o^4)e^dmq2<09{#nRz;1I!oqTL(Msfm z%J4AWn10U(ykxBk*L$RT=eNKNnAzEz9CoEf0)v87*0BlY|-+n+zjq6gX2O80PdBmz>pPgazg94=kd5*iH%*GH}AzvLG){bSbJng{vg z-#pqi2Zy!0uJt@;Cre_6i=Bqmd~c?Ezo~i2kFhfsXw}{yGmX~^<@&HcklO1;?cQo# zTpRRJLfKSbPqeL#0cbgH&+|2u_EGd}B(yvky`#Zz(>KSzGx;0he;kEi#Uj88_>KkS zKQ=DD79eybD3MYqnif>n02R3q{}xmPV;e@oAva{b{YwDBOFw<#gotq3T56DD8CQQH zZYlREyGZxhaTkMF`@y2x05a+1SNgZ(<~;ClAi8}mR}pG1Yi+r5^75^wp#c&zo>SXE zIA$f%<2!Uok#f3^8-z;|1=K8lxJp3j~KK$eNQ5V2t!HC4MWzzO_F*{2?qEB^NA5i_8E9#Kgr7)xV?|5WRQ+7V(La z5*zA=U*7LL-8R+FH%dcnXWT~|&bP#_{D30U1UK^XR-|}snWQ6P2-474+6NKMN{q1Fn?S_J&5a5Ne-AdYUc4R=5KI^$H`jPx%>lM z%-Q6AoKbW#Kgju9kfI#4`)75S@Gsy1b&Fb@zwjN^#^3`ofa+hFP$CQSLBmE6fEu_B zqzzG&6x_5vVxyFsf`T#!D6*sMOQN?0Fo1e`MexLQFm6vB( zoqGJ<|JeLHsy_d3NqJDx8%hM|q|=BV7D$h18ylB>VL|>=lsc-73>~Zkss(XwP)8*- zs7-Mm#>0n+z3<8?O%(|*g(1Gfep9-?>6}!`ZFT%3EBEPbvW>q4u=!f50~AF=CxR(F zua2i-#)fr1%*!e$x)0>1COrWRmGiXdrKq~-=^R-`R!*FUx;mIN2uk$Qz|5(a+8ye} z1!i)N%&5l593w~J53+I|KIo1suA!bEJwAgLSOh;f2{yr<_&7CnHHj7c&DBu`Y;1bK zfuM>-X*SlVK-{CKN%r7|dkX~Y3P#Um>{#+AG&M0b>ESS7J`F0QBE4Zz<1e+O^wfC` zxG?=ub2bK`kKSsi1$WOIOBZFJr>9@6{k24fLE*=P$p)T2W^B$#1%YDvfcrTi=y-T| zi+4y=pX({x1qjq+zk~Wn5`*wXCz6zMmJ5scWZfY^p#K$^W3fAwRT09NpB1$+j061_ z&Iz!pH(-`3^z88R(P{zDV3)`WuuAg~2AKJgklT(f^k9SIq{vwie-yO

6VQ9cDy_ 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

K~ z3@%aqE-elWmVa7;2Fzy#XUPw*O(uHt1?{i>!CtE$-r7Ifs^7bquGl6h&ZbmO(OkY& z#ZE+MTUt=-^qpc-RBWN9EhNE8XJDbxjRXT70|Nut=lvAo>&?gTAVW4YBO{|^YeVTl zOokFR&cn$E=4!rAuyKG3efi>KGi@$&nECa}<5lW6T`T*QPU2&TdwSm3Bnscw>uhO! z+;7iOrzqK~on%AwCOzHu$U(zqf7sN0g;ZrfJHRviuk+)WF~rGva3(*))!H70h|~ER zaGA3ra^y4ej0T^Bk6MO4JS4Je3^RD$GVBur$eEkRPS`0GMdKxxte1zwJ?;lB*2~2l z^{x5EX$DeG#Du$z;CXMs^TsOvKJRo&Y_g&r4Hr@;v)kR z#%(T7@Dw2}PJvPV&pFZ`GMhDTP`q$vfw`sdYo22&6N9d#S%oWO-8j_L)+1R<` z&4Q1%MBXv*Z%(xwd0pH&y4X<7QuWCRRwwb3hBg@ug0*BodH4$%Ph3F>oZ?oNV4Yh~ zOKp5$pn$HTb1G}fqXpt^kxr#El9P?w_ZQ>6oH0#fOTVDKjS>Hs*IvYe+~dKSTF!A_ z{z5q+#LqxG|Bf5!;E0SP*RQ)E@jPBsbKgYD9FK%>pefN{fk%rcJsipj7FS~^QH(7B zj+)|A5p-YkESjiD4h$R_@{g=W7KUuhRYadfh_;PIP>EBT4nDy849~v&osLdnG6=u})>m1U>u$Gp50#GK;x>Q6T7$~> z19NsPx2c4?`Kg7Q)AdEbLTi>fSaol@9olaeDFg)yj3^4=)j1959d3?yMWl&#M-M!1 z(dX-|u&zs(I$|Xxw9QGfNvZ}TMd;e{5)xi<1P2C^Wz*8qO6LUz2C6=!r8SEA|NE;; a{0wuf+(emLLGaIm(B!0)C5xUI`2H`Trpj&r diff --git a/x-pack/test/plugin_functional/screenshots/baseline/second_child_selected.png b/x-pack/test/plugin_functional/screenshots/baseline/second_child_selected.png index 59277eb63f85fbcb76a30003a61dcf3858b48752..4577c28d9bef4b2dd4e632894ae6ba5fd680ad09 100644 GIT binary patch literal 13265 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 G4*VY?+VDgG diff --git a/x-pack/test/plugin_functional/screenshots/baseline/second_child_selected_with_primary_button_hovered.png b/x-pack/test/plugin_functional/screenshots/baseline/second_child_selected_with_primary_button_hovered.png index 5eb5828f5a74d0bd04be5357fd39348d1cb3996f..ad84e5e781420b3ec411799392629369d5687159 100644 GIT binary patch literal 13300 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Ⓢ>dAo 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 Pb52D;Q@;3)dC>m?!DYzC literal 16647 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 H4E_EWE;Jcb literal 15284 zcmdtJcQ{;c_b$$4f)Rv~=w&c5DMT5fGx`uUk{}3zK}57fZ=*(t-fKb*o$H)G&zb9D@7eZ#_VcW@?t9&9?JYu2N1c|Mjhc*%j8+4y za*d3P+z#w}Qc-~4<0^K)$;f1UG*lF?dy+5JQ(7c%RUfYm?C;BJAc8Rn%TW4QK_3_u z-Sz8d-sMHR4N1TG+^Ao0Bj$5|Me_~S#G5ZZf6kAjj;?Tinv3|@7%vA`gTs}S+KQKn z$2EBi?Kycgs?8fdg;(eJE`ZNM(KJ>Qe53_J_rg|b?!frs$mS7C2oAUg_#7LCN)fy$ zK7=qtupZATp25;8-!+nqOH54M4A4n!G(z52xzl1B(elKz0Sk-#R74u;cP)}&c|KeC z=bbRyr4(EG2-Xi}ad0Mk%fFW?rZzsZ)ngW)%62J|4aO24H9z6M!29=h_N=GPjQwq)v}@?z|ocjbRcU6&0W zdKJeO_O;0Q&$$j~=jbC)QA4lRY>djQ)APs7byXdq!bnd`dsy*6ib##FPnbu?tlodK zeMQJ#kZf?pD~6pY<$@~y27{INwzJ!JGaGHO5ZRFL`s|Yn<%^y!ULp!Ih0Ic{_8ipN zib!13)W5B1C`g!}VQqq|A#ogcXT=$C1%mVC=S(mY9P-MLc6xbMIKrxRnd)>+b>?Jf zd1n8m9OY&-Sgz5CT|93C&mSk>fNe;rOo0)6SziiWw)`D0NR?9u%i$px4Z$Gr)>Ju9 zbFVbiCNpo;w}^(FExB`#NXKz8ThWYw6~V$_s3XV0^&|oFns|!WuU|h{`i}Sa^WlqF zU+1h6-!S|5QB-PnhS9$0&g{kH%VymS>JRQr1o{&)r6NDByGF~nBg@MR>{drMgczQv zkqQ0$(5dM%V4SGAUVC(7>3f%H z8zzO#DjvM_DZTvs<;y!bt;2aYvHp)Xbbk9B8gFk3+P}+nMO|eQviX+&`N6N=H2=fj zv35ke0go44&g4irQOqe_WJef zt}63NVRWo2H*fOO-gR(Dlkp&T=r81xsbsB=S<)b8k7{WCmJX4kS_{!l@a^ZE$Fe_!>X#>)bflDmSJ?xvy)!`H5e?GNA_f| z>|NM)|N5m~cC*gxOv*jWhQk;=y;$M}R1P+{oV$CzM?=^7sN7Zr801L)AsctMN!##B z6_KyDoOxblqJCUf&CyX%Ih^LsKZEu5^`#vxp+DJ;W!7y7RW&R+5oS_qccnOG+%3S^ z^pcjF9|&18Ij#H(s@~qxeK~$%OkEj_o=2gEo`+A;4%V3!@(uE%q5k`o;~O)H3Oz>q z+m7WcW5WwWFVIiU{+(e_X`z`kdZA~({eFKGY)7FX-|GjylErIN7_vgl?))||(l*BB z|AR}7hFddu$Cfzz)8W3glgH@VRUPRyPr~0z%D0l)7rwscGdNnMiP6ngvidb#YBSZy z_#<{adn#Vg)UWHwy`k$`Rw?H03@QG{KFD)^-#94i(664beAaJ7)4g6#r}C)%7++~_ z(*7Uzhq5%FVlkpnEMgL!NygfM%5&8XQ4lh1e7=)>&6s^sZ;ggm#L6j8jm{R_s%#3C z!mw+xEPLB~Pn@ow(rtc?Xq#hYPIA5Tcq2#D(9kXZksaZj*&Jd`U4p@piUmO2CuFji*N&W9v_xzcARL@KRoVg4-a! z*xo?o@b~uIWcc#r@5Msi)nU{TMpgBkhKBiP;eCcD><>kz^)9`SSY1x%)G{}PKw$qw zv1o{H7&KmA7oktoKqv=7;Rxlrbk0i82iw=e=H^{pcOW$azlJ~CeAf?Vb^mJ7UpQLV z`JQ0WmHv>#Cc)y9N9run*7P9gCW@<$T824%2#cAn>k%BU2F5)ztYW;f40Pn6}`*>=nCrimB)O zgyM;|c6&t-g_84Cr(VAr(Xg4-8T0pH!cR7at+qKWpIiOqR?|$1!~Np<^*Msa!eFtC zQ>|QNoxU>V#t%M5s?5EDzQGT9@wqQL)3(Mqx>*#GDS$;0sQi;p1kH~aXw|p_XdH)T zdOj~Q2;8npT*4VcuQ*=h|5EK^70+=#fc%NREg=ET%61!7z zc48ev57|>|Eg0|(4bdF$ZdZF3`R-}n-`g71^18#D^HP1M@ZEu(xX1WDI|bd3R{W2i z>PwdtZgq|B9EIUe{O&PA!k^hKxI*~CxK%9%3h!<&4L05|JP5=TLrZbpQw3?o*Z|?IR&Kw*+tn3rBOmfSSC3@8Tsv}%YJAV>9xn*V9_H6Nx9om6 z7@!@Xrw+wx{=OJGNJ*TIJrBTJY0Pc}@=%~KJr*H92a*uflL}u4i~U#Bk&ZxGQP2iw zc$+fZqD|4pEM&Y(@DA3WD=+5!Q`J6xUwy#6FH8pbJRRl)AUh*lmt5UU^noGq6%|xZ znsY@UuF)+@Jgx8jz2v5e2dB9*4$kDvPMtbV@EG^YIX%}sjWP3`3|L>TT-m4}@e=E= zc*wP|u<*6N|D*FHJX&tfPKU)$fb*2JuuJa>m6%g@m#tU8wkZSv&f=iVfYq(nPL`Ng z55jM-%pRR_aN@-@o%Ho zDN@>D^Zj&J+{Q-6ZoE(skj~Tkm7H{yjk!UwHUa?wkNAQVe8O$&&ZAIua3}eU;IxOX zY}dibNY8ug23o$mx7}C9w2%G$JiR-t5|dUYx=P0;k0|-qfx;2-A9=mGFdF&%`TO|k ztJiNcS`Ot3n#z2Blr;PXhNYlGf{1n7rGX(Efw{>=f5MPIrN~90RL&5f!N@2Zz!nRk zfk7oPL0%@BUaq$poZgMLF^m8FF{xEHe?>=X4+=RxOnD8ZmSLhlFDPi$O}RmJ-Ecto zS9t)#4_6h`c7KT@yZ=__;$TW*qHyWqZ+<#8P0iF{e?P{YxmV9lzz%+`5I>feg)lUR z_ZFBivu(3kg08ib#P|cwol!S-{Ggk>b=S~Cl5JHd zW({#g&+S<8fCs&{K1;whf4L#?HGa{^)s9Q=i%EZR9M;^cKmC3(J!iD(WmmIUf1xA$ z`fw>DL!b>P=LTDB zAPo<|Cr~7@ujWaF#u|@7U?8xZNn~s|vM!bkPu7l6Vv$+=>FIW;#^iX(1~Y7`=5g6N zfDqM!WDpMSyj=On(A+0MV$3(ghNQM_J%GIGSd!v){if@L09GY&5-Te!hxh|w90ipI zln~X|l1-3VsUZ?d$N4OLwczCFydo>|fx{(DF3(kcwp7-5s#Ja5|9RN(_L_3CfN@LJ zUZmvy@HpS!!I3W8#?yfO>j-vQ@OWYLHV%j7wV=q7erOFq>^0eJn7a8rLDus)EAz_s znK9o*v$-zwwigOzk29INNJ-k_OC5Qp|Iq7H*8Cjzi|E@q?YYgWroV&Vwsm&C0Ky*w zY6DjJd*^XsNC?mgmq5y=7~dm0xqU6F$KKKC5!F$c_60F#Ti>9HoeJ6@D#E9AWI~zE zy_0fGy*N)$ZswOg5Bd1fRa8_|axOJ*O*V!E+{hMqs!LnqfJ&MBPxwp0p&)v0#&|p# zeifFv$_mF$thNKi_%G;UCxCMhf3ppe2O!%y5Q?QzhFDA1cUQ;}Mv5ZBaRGHXqSkIDDHC-rRQ*J z=g;t31g2TyOpiAlK<-6ZWb;H&;KI^ew$r9rMLo&LPY`w`(8_RH0u6e43EhgeUL}K2 zq_P6JWjV#S9V6LDb-^;a2yoHt9)ZpAkf-tB^#{by^o1-ppq$Xf(hpKAl zh*H4FNafZoYR2U~aoZfJ=&CByix)lOLCQ3?jh;S&mBu9}2c>~u4JUR++rhDaeC~Qn zY(8o&U&MY2+Av9vI~WFnNx-P=(aBTHA1o$$J3OxI8GXTP#K_l`_36FWEyzXtv)mGEwfVI1t|d_{$wLPF9Py6B>)ivp8&uHl^|6P1}GO8*wUczG_5EO zszwdOX^lW0bva5>k%Gj8C6JxgKr}{px1M#MDQi&r3rkjGZB7<>K8xL`WA5pIw?oFN zpeDV1X3E(nTfJ*})Y`^o?Z%tJLjFsaA`f4W-O#>SEFdqmK`K~8MbWzgl@9I+*<)v< zT91W+*wVE0#i@xUBeGYquy86Vm!cyPTUv=DEArrq**>ITQa}P86t0YtU^=lRk@`;o z3kOK}E50C>Kzt#|!=Ml-HV}XY(fFs?z`-4foRTem{wVXM_QG(5I0?Af^ZG=Z6QB)o zBw@K!pc;9P&WA_cy;z{;?qJtPN&3M-V_zPgxTK`|8nA}d+6aKqYpH6o#78RAAxhDS zMbj3lcO8t%T;&{ZUCB4_clcj2=)@`v=^K`S;ff~%L<>QlfLQ{fMUivGVltpnLEL_?K4XLc`oo~uFBRo89XK%)X58Vq=0paQC665@x zflg|>lMFBXGgtr0{Xw0<=&a2jU$Zmk(gJy4tDu%(L@4h<;ai}zrOE-dM6@nd$Ug}r zFPn^H3uv1&e1ZpNtpbPL{PpUoG+a?MlhFcr7C44MI3$e{m+BSxW4pNaPanyKs+b$6 zCXkGXBSnU@jxRsn8WCH^^D~!$>HRJBj1i^>_jAKJ$3zp^qE3Wbw2P%KZxF;87xluASF#PdpXB&lE537;eL z%4UdYfrWBhunb3T^OHi02S}-)Ue2VYH?Nd4p^3r~Z!|3Wm@K3`btM`777cP~1Wi$S|G2Ne=l&Dc`VpufSD8Y#j|v zmXdrBfFOrAfauWKjWUZn|JLI= zy#~M)3`#OX&ZLUm8VpaiAxi`9Xvvk~{xz1=?X>|Ny;Rl@MnRqKQosj~T#H;8?KAM=DkVx2o~(1-JCRmz>k?yVj09g%(x_A0deSjPPy% znx+ADq>b^u`^ewXq*qWPj-Ur*$Jt{%J#486)Kc9l?AV5lug|d(s1T<+_A;@sx z5=$0xVx26hUB1vcBMAj$&@2an;7u89(0ctVsU4!DY@cU8@^!P=c#n!?LDZ)htm*_kOvif7p3@%$*Pvj&mcf{fHX6Xk{z}Sb z6@4P9@Ce}{&&^IF4{Im6ar*eI{RN(0gT|S8Yx=g%`+z_lf2-a>8iez{WgfdtJk35b-G?t@f@{*i5 zAs;67L4gNVqXXtBuiX}L2$NcSsHsa^EP&zK&fe(E1T6kk&%iN$bTpV09<-Y9QowF-VTk*Lax8OIw-u<{B({muwH`#>9 zO3=2YnaG}JUh6a1kIiPo{iIx!447K{)N!|A(?J)9vAooKO=&5*sodc=WZBP>fi{dI zu9Z9BfvnjrTRugxWf7Y>&BEPAf$jMZSJhk=*5t}O*R_rg#_qC>JZK@iFJ~((aeS!E zUsd#o=(;EsT)q%RKC*GZ>+gM+8w8iup#|r>u5Z?~&u6PCRlbG(E9^WaD{p3}+RzF1{OkvKIf+rZU1u{7 zzc}1G|Mq45^GJ2qNs&?ecsOs7mpX0!*gpJH>5_H8WB$ymDg9k`8+i)XZm_?o&!}Z- zZR2wM;s49r*!*Uf?Q7rlJIRiPh&&DI>J2xlPi0FxU%xi*4%+*b|4yv=sQgoK?ZNj#h&kx_NfIg!^ zC!r>qb^l$l{cC$O%>u)wifRdGyyxfX?OX;|UcZi9%jb@n`+9UzOjq|@@~Y?kF$z=3 z8M}BU{&P$8=81s!0IbR5K@ukkGVnYHI%NexDpLtD*qZMF~z-;~YB0>6!iAM>0hAju>6(cuXgOpHSt+VK!`S@TolLHTOF{ zxbA8rqmRoTtonP7#(Tj=4O2f&ZnnyqGA_x83uwcSLVy%9n*azbbj7+Iu0HnK{QP3m zZXh$|J`2g~o<4gwlbH;^@XKVS6mpY}P-Tvko~W(dWJwp-p8R#zBo)LdAhdCFf7xFq z)GThN_Q32>OFdy7qYV8iH;RSqC+UH4=GqRJ2quOdE918{w9TSOII3o*;d^UO zEhO+AyHF^%dC9JQWSH2X0Uax!rHNkr7X7Podv!<3VX(?df@Iy_L>*kf9azlSb4h1D z5uU48&WW+~N#roG+WVFjCLU^>3TNs6EUrFRYB5%E&Hs2?^X|_(2OH@kroy?WDz{a< z0P?`qZqYxrQgxq)C<8%cG{Tp?Drp1(nv+Ch$PiU0%cAf7D99e3Jx0vZHUDLBJ1odaK4OR)uzQN zt*L33Wu4ADX5_nde)Sp=_anzIZC>vP@L}-ClnH6Y3FLuT8F>6St=L;;r>5u;CMSr4 zY3w1L?de}lPUA-1M^zQp=xNHcuCC*Bq+L9kf*F3*Q!8zcf%48+g=pBQ=o1Q3K$mp& zmD~K3T~4nF#JGE3oUN^XB-72=V1-WZ5fg86P;Dj{S7-T{8>u|iRirz$A{|~F?mkuO zBw%ZveAck2`tIq&Qzr3n*Cseuxv}PWy65IJci>Xj`12g8l>7)SMK=yh3 zYX#26QVM3*+4gF$-6Y=RliTL=0WGiA6_}6z7?O9t$D*S(XV`^S#?CX zP+^>N@QF!&H{WV-kDLoUz$<{3^3C`Y17rPb8o?3FT-p!KqM{9t(g1TqUmiW68D}$X zOG547rruulax;dhW_UP{@xAYRx_vrDbmoyPU6Zm9^V()i^!#s^!}6I@G|bfFmI9>q zXx^9ZdM$hY$K2=f7~-~|q?I0z+d#7DlwqH}p6r55h`kE@^#vYKNDE|>MV;U$J-7zi zfTSisp$TLk1`4zyJtWE-JUoHqeqhOnylR)069bAc(o;TActPVQGs!^bm89)85V7D9 zJfJ_AaOR12I|2}SbcRBLBPv#{TldtT-W%Y$ELTHGd(nNMN3uS*rwfh(BbPhPdqaAkamB7w@_K9eo&g35SM@`HOxs!whAnMmR9 z_rDnWdqr_F;1#hpVmr`-O(0uzbM~iEuy?$H0qdoQn_rlEtU%x;sb@8J2j$x60_PyY zCt=Dd;!43>L}#?k!BZu+sHL%FKzeh9g0q-=qsE$Vt=T{Jym!8UpOEjY z^ZZ2^5b|GD8BWee&7$2qgWF8_Rw35r{0jj2%EsbCM+et+ekR1IRC_i|TL5*W zRLD7;(y6v%4Ln?Kp~5@`i$RUus>cuMo2duKGP2rRLK12NXnY*v;?$l#bT=5QnWZ7A zp%cWY;I+0(s)F9S(p^!fASk3Xg9LM#E{fX9)8*_h*on_5RlB1;7Z z_t6m4R{Ok@VHURdv6Ktk=VHL1FUvTOlK0v?a7 zlAh)v>t3N^S6RtH?ME}e%GwQd4IV?6!}IUbTs|RfO}of(o@+f_eD;;OPM$aLl`OhH zX?f};nnL)clROBs^1T)A_20dBI^HfmI$98=YYC{W4STDz+*RHfJ(1{ta_QZZ^)dC~ z-KvsbBg(gnUvg}9+FxIZ=uNe4spvH$DchFKE?eL`lDNHXeoM>iA!m<*SquKn2&ZLp zvZi;5Lw($y)F;R6?u<@NtY^!7+-j8F2v8%=U%QAOONZ<->xu_5wMYu%VLQ%$!e)n?aoa?7FCP(VRb@K622Yw;+M2Xwd^=nq!*58NoR~%8Np+8N!ewrk@sa$s%|K&5tEz~$^8VDU z!4Vbl{<3P2;jx1NUgo9XMC5m)+uAo1&VaSubbNc~TgkWRzt9ACyy(*CLC(=ZUiq5_*x$)ty z3276TF`8H@B>6bHJWVnp>XWcTiJ{9D_qbL4Z&!G>%R$^PDVA(jC&6K*jHL&K@}Exw z{Wl|#yx}Eysjv=hWnp>gpBaG-uSDeq4|C@;oNj2T3KMn+k#MUkSV+*)Y~eBp48X_j z^}EiF7tV?^=qwE2d9vJ$i~X2~-)^sZRhL=t;fuo`ke2q(crA425d`vhmAjgcRN5F~ zP^+?eBL6BMoWCYuAPm9)c7K|pVJdQy;MF8X@)M+j0kazgUhUY*1T7&_GYIPX=Sij% zc^D(2QHq?)bt+?d+jf2i%f1_x@RsBew94QZ;0JhdxVP8Eq-V#*yxnjeMutP2DA?-&XhEut=3vNGo9xnq1oOJ z=WOYgn?P$~I1ypd&Nx?k< z^y9~C;IAeg{h|lS3stVuMqu1KV0c+Hlha5&seqn%U3$}XZbBz&A%kc(PkWA3FZoH& zOt5arqS?^X0c(WR&#vsP+=J1iy3pC$pN`YSX5Ai?Emg&?m$F%N=74SY)>g)6$4*60 zeIh@ZdF5$fg;9_~ZE1RCwh4Bkp>^DPnbX!qi&(z21@Z!f#ByQ6|hK-3E#L&xVfLufMo>v1h2z zI_}!b8$H(n(_B6a({#VuhlASK8>c(dPR2WHr&3MdF%WhaE*am+-0<0!c`qTJ7xsOX zExLGqqkTr*X1#P|^NYLtuPm$CcvkuCW+${u3+%1es7t40s@#6K?;#%5KJ!%m05xvC zHzl89?tIbk-~h)DE|`{(_c&FmaBzk(Ku2lxQ{-rQ23Ob#TD;sr)|NdboLYioeiq zGg^0|d1SAxPqy4aNNBF=?Gbu>_gAFk;=@+gZ_OrcA*k%p$iYd~r7UeV$MtJrc_MlG zojIdhe*Qa=0jgM(K88wBQJSB}0#5zul?5Ea---L3>Ctx{jA-GSL4&~5~62I!gHo9k_CTm%xN!{&G*=T|KP(d)T%Bt*sx;i ze!13bRPZ1}XuJ{SKMV0cY$)~=$q$&>kp1K*q0pV{A5*L&ojlCt|2WvVAIZ1 z;$+&~Ew?`iW=rWdr}5TU`iC3IW%m;r&vq0CZ2Avr-Sk_HN&CLfE#Ica$U!iRk?kty zJ{013+{JGA!HwwezBJZ)YD;OX%AQkKp)`jw`+IZeTG({VWR1h+dG^!y=jZ)bu2wAo z&$YH*o^C!^RH#k}*yDDZ*CQ=!*=v(^`*f>VmD1r$_4mx1N=niZLE!U1>x?jL0eLa~ zyq)L=56PLu;|-Vo%O*|bsCQO_T8-opfkA;tM%OwwH#gsad7b5!5Bf7T8oZWo2 zd?mfpOHkaT)TLwSY}KMR%Jt~A!``x$j2EzZRAF^cis+9W7Biod?~`zgSNKV0YZX$+ zc@Z3@PvnsqNX0hw?Mq$#jshlM=bNljTpE*Ba8qxDMp~LUxhtbm)=ikR z8lB7@(1pLXBoFD`gkaCmpE+aAtN#Ug#!kP!>1l%2c@^Q6h3fsi4&|8DmT5z`q4aN9 zbxfVLk|ohOdPS}{p|W&^EP+Z|<2)4wi17p}JTgcJ&hrJ%!$^_UL}vZ-FlImJ`C!G9 zf!-~{+imQ+MqNz`hmExs?qaxHaM<2sWu+G2kR{s0e}QoT-2*K5NoId$dbj^uis;&I zF>xb~I7Uz@e#qXpyp#|#ZDMHN(KJnzC#uW)pc%p1v?|BTmrReEE4!_yPL;|MB)CVq zNvEqTH3n&%0iWkqxlc6!Q0tLUDU4!T{)2e><$$}WnC0I*r~=dO1&0__I9~3LbBleQ z=5?~UyPs)LL8#NuV{@NAg+%mfD8-VT4Seh+a&gwlcm2}%>~7N4-LK|j;Qi@+$y*`o z5ek)&fG9d`YmN~6;a1U1mDxG7=|baO+NXa_T=kG`=&>HyC^p@gtt?-7#_78dtuC2< zu$QG^abIk115A2U!844n!15Ut6*YCQ2#h>I@2Tp^1bSg|eLX!rX^jv}WXD)fEM?AM zyOqKLPkw<}m)$q6<)O7Ry7D_53{N$nSt=*%pC+&%z^QOW6krqWy1;k5b1z)YmUH`qx=a*gdLVIx~Jbjw`PUsM*5JRAQR4MBlHNa}cCJ4gh_QziZv ziR*W%nPLGIQm2PNA|U(>f$bqm(DUcBkV?WZa>kQdXT(6fLA80w66on^>PRv4=PQ)) z1o+xSrVWTOVkR^mmK5>iZL*%`1$E6LJ?KIz%fFne0X>uGrHKL%=CR;KXi!m~k3DYz z55iEThmgKq{PPXTgM|lyUw5dG)a}Ptt(DT4>tJtF{tkz^^3IfwpT`bPjigQu34}cY ztk4Sx8BFMmun30nqy{f0fvQf0yq-*p<_i)c76E2OMw;Ij()>cSFrtbxWzC&wdU$JI zDU16fu3ZY!G1oMut_7X^X!*_6|5w(05Xq7~V4cwaVo9agXh?*pkoE1qvxR`!W+B11 z_z*-yP++gdnRu1Sf<=ingG({$=cK?Z&=xC44DZcLgM9o~Ml=1UG&L{SJQEj)rM;B0 z;jki`;$~ip57^2#x7uHpv$*p`gZ~U~Du{S0e6pUfKHxk4i?Jbr7G(U-5G)ViCrP#i z*bTv44bcMMPaq>=wdQOZ(wjT?zV(fF+rRBkNDKUw#zH~j62JoRTyhv>9afZG#FV8$(JuU0Mu++Ef9GE}g`6g>_L z?3=u)3-yvF*!Fwm?OIULgcHvbuhHFTyqao{#1C42q#t`YhwGNI=aU%mQOGIZy8=b( z>tXOJOyab`i0#V4aV+1<}h=vm9d;leATE zQfM%W{!l)z5s!(*oIu7Yit;f20i9u^CAH-jY%OYkK3+dVAjEvcSK8K57JqzH=V$7% zFKCwwK>k;x{2}Fc##M=>jw4Oy^6y(d+f@23$FdnrK%(O?2~h|}J#Q>7K8=Gu@mfdc zw^h;5B5Q;jUn=`K$MwerCLe+VT#rs3G|ctwMN+OzS2&GE1tA$A5#;GWx&jYO^coQ7 z;%kdTL*Yubaf*D5X%UY#!EldQl=$0Zldfrcyu3A@~0txbqB1SP@{A#jGbDERY#?LK*L`8FoCkN+~BDD1M+qDpiZCch! znipo1?i!Kt(6BA+SWD{Q!rPUMDt%8H*3NUUr<@+`WUlc;>&n#vP>5J}P1%^1EO3mK zxG-X}%|2|}M$#W$+XBNw{Y4fE` zNe@Ip=oe>bmss!S-+6vYv$xha2c*p=&SCkV=I%`Ri^I&*fe5 z`W*Vj$@&ZS!V9g+FhgU~&18Zm&)X*$Ha`^#Rs)u>>se)E{f{W)djT^RDO9ou`u_k5nF90x diff --git a/x-pack/test/plugin_functional/test_suites/resolver/index.ts b/x-pack/test/plugin_functional/test_suites/resolver/index.ts index 5b50afb401793..437b279647eb0 100644 --- a/x-pack/test/plugin_functional/test_suites/resolver/index.ts +++ b/x-pack/test/plugin_functional/test_suites/resolver/index.ts @@ -26,7 +26,7 @@ export default function ({ const browser = getService('browser'); // FLAKY: https://github.com/elastic/kibana/issues/87425 - describe.skip('Resolver test app', function () { + describe('Resolver test app', function () { this.tags('ciGroup7'); // Note: these tests are intended to run on the same page in serial. From a22f2853ba37e3fe023b14c92abd2808e03c3f28 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Thu, 7 Jan 2021 14:08:46 -0500 Subject: [PATCH 13/24] [ILM] Fix hot phase serialization (#87213) --- .../components/phases/hot_phase/hot_phase.tsx | 272 +++++++++--------- .../sections/edit_policy/constants.ts | 2 +- .../form/configuration_issues_context.tsx | 14 +- .../sections/edit_policy/form/deserializer.ts | 8 +- .../form/deserializer_and_serializer.test.ts | 32 ++- .../sections/edit_policy/form/schema.ts | 24 +- .../edit_policy/form/serializer/serializer.ts | 119 ++++++-- .../application/sections/edit_policy/types.ts | 18 +- 8 files changed, 308 insertions(+), 181 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx index d9976605393c7..a777f30fd2e42 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/hot_phase/hot_phase.tsx @@ -52,7 +52,7 @@ export const HotPhase: FunctionComponent = () => { watch: isUsingDefaultRolloverPath, }); const { isUsingRollover } = useConfigurationIssues(); - const isUsingDefaultRollover = get(formData, isUsingDefaultRolloverPath); + const isUsingDefaultRollover: boolean = get(formData, isUsingDefaultRolloverPath); const [showEmptyRolloverFieldsError, setShowEmptyRolloverFieldsError] = useState(false); return ( @@ -145,145 +145,145 @@ export const HotPhase: FunctionComponent = () => { } fullWidth > -

- path="_meta.hot.useRollover"> - {(field) => ( - <> - field.setValue(e.target.checked)} - data-test-subj="rolloverSwitch" - /> -   - + path="_meta.hot.customRollover.enabled"> + {(field) => ( + <> + field.setValue(e.target.checked)} + data-test-subj="rolloverSwitch" + /> +   + + } + /> + + )} + + {isUsingRollover && ( + <> + + {showEmptyRolloverFieldsError && ( + <> + +
{i18nTexts.editPolicy.errors.rollOverConfigurationCallout.body}
+
+ + + )} + + + + {(field) => { + const showErrorCallout = field.errors.some( + (e) => e.code === ROLLOVER_EMPTY_VALIDATION + ); + if (showErrorCallout !== showEmptyRolloverFieldsError) { + setShowEmptyRolloverFieldsError(showErrorCallout); + } + return ( + + ); + }} + + + + - } - /> + + + + + + + + + + + + + + + + + )} - - {isUsingRollover && ( - <> - - {showEmptyRolloverFieldsError && ( - <> - -
{i18nTexts.editPolicy.errors.rollOverConfigurationCallout.body}
-
- - - )} - - - - {(field) => { - const showErrorCallout = field.errors.some( - (e) => e.code === ROLLOVER_EMPTY_VALIDATION - ); - if (showErrorCallout !== showEmptyRolloverFieldsError) { - setShowEmptyRolloverFieldsError(showErrorCallout); - } - return ( - - ); - }} - - - - - - - - - - - - - - - - - - - - - - - )} -
+
+ ) : ( +
+ )} {isUsingRollover && ( <> diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/constants.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/constants.ts index 48ed38fc8a0d7..af59aa4b9323a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/constants.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/constants.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const useRolloverPath = '_meta.hot.useRollover'; +export const isUsingCustomRolloverPath = '_meta.hot.customRollover.enabled'; export const isUsingDefaultRolloverPath = '_meta.hot.isUsingDefaultRollover'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx index 3a66abebccc1a..4ddb85899f3ac 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/configuration_issues_context.tsx @@ -9,7 +9,7 @@ import React, { FunctionComponent, createContext, useContext } from 'react'; import { useFormData } from '../../../../shared_imports'; -import { isUsingDefaultRolloverPath, useRolloverPath } from '../constants'; +import { isUsingDefaultRolloverPath, isUsingCustomRolloverPath } from '../constants'; export interface ConfigurationIssues { /** @@ -33,14 +33,20 @@ const pathToHotPhaseSearchableSnapshot = export const ConfigurationIssuesProvider: FunctionComponent = ({ children }) => { const [formData] = useFormData({ - watch: [pathToHotPhaseSearchableSnapshot, useRolloverPath, isUsingDefaultRolloverPath], + watch: [ + pathToHotPhaseSearchableSnapshot, + isUsingCustomRolloverPath, + isUsingDefaultRolloverPath, + ], }); const isUsingDefaultRollover = get(formData, isUsingDefaultRolloverPath); - const rolloverSwitchEnabled = get(formData, useRolloverPath); + // Provide default value, as path may become undefined if removed from the DOM + const isUsingCustomRollover = get(formData, isUsingCustomRolloverPath, true); + return ( { const _meta: FormInternal['_meta'] = { hot: { - useRollover: Boolean(hot?.actions?.rollover), isUsingDefaultRollover: isUsingDefaultRollover(policy), + customRollover: { + enabled: Boolean(hot?.actions?.rollover), + }, bestCompression: hot?.actions?.forcemerge?.index_codec === 'best_compression', readonlyEnabled: Boolean(hot?.actions?.readonly), }, @@ -53,13 +55,13 @@ export const deserializer = (policy: SerializedPolicy): FormInternal => { if (draft.phases.hot.actions.rollover.max_size) { const maxSize = splitSizeAndUnits(draft.phases.hot.actions.rollover.max_size); draft.phases.hot.actions.rollover.max_size = maxSize.size; - draft._meta.hot.maxStorageSizeUnit = maxSize.units; + draft._meta.hot.customRollover.maxStorageSizeUnit = maxSize.units; } if (draft.phases.hot.actions.rollover.max_age) { const maxAge = splitSizeAndUnits(draft.phases.hot.actions.rollover.max_age); draft.phases.hot.actions.rollover.max_age = maxAge.size; - draft._meta.hot.maxAgeUnit = maxAge.units; + draft._meta.hot.customRollover.maxAgeUnit = maxAge.units; } } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts index d72dbb38f6c95..b5abf51c29028 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts @@ -7,6 +7,7 @@ import { setAutoFreeze } from 'immer'; import { cloneDeep } from 'lodash'; import { SerializedPolicy } from '../../../../../common/types'; +import { defaultRolloverAction } from '../../../constants'; import { deserializer } from './deserializer'; import { createSerializer } from './serializer'; import { FormInternal } from '../types'; @@ -202,6 +203,18 @@ describe('deserializer and serializer', () => { expect(result.phases.warm!.actions.readonly).toBeUndefined(); }); + it('allows force merge and readonly actions to be configured in hot with default rollover enabled', () => { + formInternal._meta.hot.isUsingDefaultRollover = true; + formInternal._meta.hot.bestCompression = false; + formInternal.phases.hot!.actions.forcemerge = undefined; + formInternal._meta.hot.readonlyEnabled = false; + + const result = serializer(formInternal); + + expect(result.phases.hot!.actions.readonly).toBeUndefined(); + expect(result.phases.hot!.actions.forcemerge).toBeUndefined(); + }); + it('removes set priority if it is disabled in the form', () => { delete formInternal.phases.hot!.actions.set_priority; delete formInternal.phases.warm!.actions.set_priority; @@ -234,17 +247,21 @@ describe('deserializer and serializer', () => { expect(result.phases.cold!.actions.allocate!.exclude).toBeUndefined(); }); - it('removes forcemerge and rollover config when rollover is disabled in hot phase', () => { - formInternal._meta.hot.useRollover = false; + it('removes forcemerge, readonly, and rollover config when rollover is disabled in hot phase', () => { + // These two toggles jointly control whether rollover is enabled since the default is + // for rollover to be enabled. + formInternal._meta.hot.isUsingDefaultRollover = false; + formInternal._meta.hot.customRollover.enabled = false; const result = serializer(formInternal); expect(result.phases.hot!.actions.rollover).toBeUndefined(); expect(result.phases.hot!.actions.forcemerge).toBeUndefined(); + expect(result.phases.hot!.actions.readonly).toBeUndefined(); }); it('removes min_age from warm when rollover is enabled', () => { - formInternal._meta.hot.useRollover = true; + formInternal._meta.hot.customRollover.enabled = true; formInternal._meta.warm.warmPhaseOnRollover = true; const result = serializer(formInternal); @@ -252,6 +269,15 @@ describe('deserializer and serializer', () => { expect(result.phases.warm!.min_age).toBeUndefined(); }); + it('adds default rollover configuration when enabled, but previously not configured', () => { + delete formInternal.phases.hot!.actions.rollover; + formInternal._meta.hot.isUsingDefaultRollover = true; + + const result = serializer(formInternal); + + expect(result.phases.hot!.actions.rollover).toEqual(defaultRolloverAction); + }); + it('removes snapshot_repository when it is unset', () => { delete formInternal.phases.hot!.actions.searchable_snapshot; delete formInternal.phases.cold!.actions.searchable_snapshot; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts index ae2432971059c..4bdf902d27b6d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/schema.ts @@ -32,23 +32,25 @@ const serializers = { export const schema: FormSchema = { _meta: { hot: { - useRollover: { - defaultValue: true, - label: i18n.translate('xpack.indexLifecycleMgmt.hotPhase.enableRolloverLabel', { - defaultMessage: 'Enable rollover', - }), - }, isUsingDefaultRollover: { defaultValue: true, label: i18n.translate('xpack.indexLifecycleMgmt.hotPhase.isUsingDefaultRollover', { defaultMessage: 'Use recommended defaults', }), }, - maxStorageSizeUnit: { - defaultValue: 'gb', - }, - maxAgeUnit: { - defaultValue: 'd', + customRollover: { + enabled: { + defaultValue: true, + label: i18n.translate('xpack.indexLifecycleMgmt.hotPhase.enableRolloverLabel', { + defaultMessage: 'Enable rollover', + }), + }, + maxStorageSizeUnit: { + defaultValue: 'gb', + }, + maxAgeUnit: { + defaultValue: 'd', + }, }, bestCompression: { label: i18nTexts.editPolicy.bestCompressionFieldLabel, diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts index 2a7c109fec950..f718073afa352 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts @@ -5,7 +5,6 @@ */ import { produce } from 'immer'; - import { merge, cloneDeep } from 'lodash'; import { SerializedPolicy } from '../../../../../../common/types'; @@ -29,6 +28,13 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( // Copy over all updated fields merge(draft, updatedPolicy); + /** + * Important shared values for serialization + */ + const isUsingRollover = Boolean( + _meta.hot.isUsingDefaultRollover || _meta.hot.customRollover.enabled + ); + // Next copy over all meta fields and delete any fields that have been removed // by fields exposed in the form. It is very important that we do not delete // data that the form does not control! E.g., unfollow action in hot phase. @@ -42,25 +48,40 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( if (draft.phases.hot?.actions) { const hotPhaseActions = draft.phases.hot.actions; - if (_meta.hot.isUsingDefaultRollover) { - hotPhaseActions.rollover = cloneDeep(defaultRolloverAction); - } else if (hotPhaseActions.rollover && _meta.hot.useRollover) { - if (updatedPolicy.phases.hot!.actions.rollover?.max_age) { - hotPhaseActions.rollover.max_age = `${hotPhaseActions.rollover.max_age}${_meta.hot.maxAgeUnit}`; - } else { - delete hotPhaseActions.rollover.max_age; - } - if (typeof updatedPolicy.phases.hot!.actions.rollover?.max_docs !== 'number') { - delete hotPhaseActions.rollover.max_docs; - } - - if (updatedPolicy.phases.hot!.actions.rollover?.max_size) { - hotPhaseActions.rollover.max_size = `${hotPhaseActions.rollover.max_size}${_meta.hot.maxStorageSizeUnit}`; + /** + * HOT PHASE ROLLOVER + */ + if (isUsingRollover) { + if (_meta.hot.isUsingDefaultRollover) { + hotPhaseActions.rollover = cloneDeep(defaultRolloverAction); } else { - delete hotPhaseActions.rollover.max_size; + // Rollover may not exist if editing an existing policy with initially no rollover configured + if (!hotPhaseActions.rollover) { + hotPhaseActions.rollover = {}; + } + + // We are using user-defined, custom rollover settings. + if (updatedPolicy.phases.hot!.actions.rollover?.max_age) { + hotPhaseActions.rollover.max_age = `${hotPhaseActions.rollover.max_age}${_meta.hot.customRollover.maxAgeUnit}`; + } else { + delete hotPhaseActions.rollover.max_age; + } + + if (typeof updatedPolicy.phases.hot!.actions.rollover?.max_docs !== 'number') { + delete hotPhaseActions.rollover.max_docs; + } + + if (updatedPolicy.phases.hot!.actions.rollover?.max_size) { + hotPhaseActions.rollover.max_size = `${hotPhaseActions.rollover.max_size}${_meta.hot.customRollover.maxStorageSizeUnit}`; + } else { + delete hotPhaseActions.rollover.max_size; + } } + /** + * HOT PHASE FORCEMERGE + */ if (!updatedPolicy.phases.hot!.actions?.forcemerge) { delete hotPhaseActions.forcemerge; } else if (_meta.hot.bestCompression) { @@ -73,6 +94,9 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( hotPhaseActions.forcemerge.index_codec = 'best_compression'; } + /** + * HOT PHASE READ-ONLY + */ if (_meta.hot.readonlyEnabled) { hotPhaseActions.readonly = hotPhaseActions.readonly ?? {}; } else { @@ -84,14 +108,23 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( delete hotPhaseActions.readonly; } + /** + * HOT PHASE SET PRIORITY + */ if (!updatedPolicy.phases.hot!.actions?.set_priority) { delete hotPhaseActions.set_priority; } + /** + * HOT PHASE SHRINK + */ if (!updatedPolicy.phases.hot?.actions?.shrink) { delete hotPhaseActions.shrink; } + /** + * HOT PHASE SEARCHABLE SNAPSHOT + */ if (!updatedPolicy.phases.hot!.actions?.searchable_snapshot) { delete hotPhaseActions.searchable_snapshot; } @@ -103,11 +136,16 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( if (_meta.warm.enabled) { draft.phases.warm!.actions = draft.phases.warm?.actions ?? {}; const warmPhase = draft.phases.warm!; - // If warm phase on rollover is enabled, delete min age field - // An index lifecycle switches to warm phase when rollover occurs, so you cannot specify a warm phase time - // They are mutually exclusive + + /** + * WARM PHASE MIN AGE + * + * If warm phase on rollover is enabled, delete min age field + * An index lifecycle switches to warm phase when rollover occurs, so you cannot specify a warm phase time + * They are mutually exclusive + */ if ( - (!_meta.hot.useRollover || !_meta.warm.warmPhaseOnRollover) && + (!isUsingRollover || !_meta.warm.warmPhaseOnRollover) && updatedPolicy.phases.warm?.min_age ) { warmPhase.min_age = `${updatedPolicy.phases.warm!.min_age}${_meta.warm.minAgeUnit}`; @@ -115,6 +153,9 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( delete warmPhase.min_age; } + /** + * WARM PHASE DATA ALLOCATION + */ warmPhase.actions = serializeMigrateAndAllocateActions( _meta.warm, warmPhase.actions, @@ -122,6 +163,9 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( updatedPolicy.phases.warm?.actions?.allocate?.number_of_replicas ); + /** + * WARM PHASE FORCEMERGE + */ if (!updatedPolicy.phases.warm?.actions?.forcemerge) { delete warmPhase.actions.forcemerge; } else if (_meta.warm.bestCompression) { @@ -130,16 +174,25 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( delete warmPhase.actions.forcemerge!.index_codec; } + /** + * WARM PHASE READ ONLY + */ if (_meta.warm.readonlyEnabled) { warmPhase.actions.readonly = warmPhase.actions.readonly ?? {}; } else { delete warmPhase.actions.readonly; } + /** + * WARM PHASE SET PRIORITY + */ if (!updatedPolicy.phases.warm?.actions?.set_priority) { delete warmPhase.actions.set_priority; } + /** + * WARM PHASE SHRINK + */ if (!updatedPolicy.phases.warm?.actions?.shrink) { delete warmPhase.actions.shrink; } @@ -154,10 +207,16 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( draft.phases.cold!.actions = draft.phases.cold?.actions ?? {}; const coldPhase = draft.phases.cold!; + /** + * COLD PHASE MIN AGE + */ if (updatedPolicy.phases.cold?.min_age) { coldPhase.min_age = `${updatedPolicy.phases.cold!.min_age}${_meta.cold.minAgeUnit}`; } + /** + * COLD PHASE DATA ALLOCATION + */ coldPhase.actions = serializeMigrateAndAllocateActions( _meta.cold, coldPhase.actions, @@ -165,16 +224,25 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( updatedPolicy.phases.cold?.actions?.allocate?.number_of_replicas ); + /** + * COLD PHASE FREEZE + */ if (_meta.cold.freezeEnabled) { coldPhase.actions.freeze = coldPhase.actions.freeze ?? {}; } else { delete coldPhase.actions.freeze; } + /** + * COLD PHASE SET PRIORITY + */ if (!updatedPolicy.phases.cold?.actions?.set_priority) { delete coldPhase.actions.set_priority; } + /** + * COLD PHASE SEARCHABLE SNAPSHOT + */ if (!updatedPolicy.phases.cold?.actions?.searchable_snapshot) { delete coldPhase.actions.searchable_snapshot; } @@ -187,12 +255,23 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => ( */ if (_meta.delete.enabled) { const deletePhase = draft.phases.delete!; + + /** + * DELETE PHASE DELETE + */ deletePhase.actions = deletePhase.actions ?? {}; deletePhase.actions.delete = deletePhase.actions.delete ?? {}; + + /** + * DELETE PHASE SEARCHABLE SNAPSHOT + */ if (updatedPolicy.phases.delete?.min_age) { deletePhase.min_age = `${updatedPolicy.phases.delete!.min_age}${_meta.delete.minAgeUnit}`; } + /** + * DELETE PHASE WAIT FOR SNAPSHOT + */ if (!updatedPolicy.phases.delete?.actions?.wait_for_snapshot) { delete deletePhase.actions.wait_for_snapshot; } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts index 4dfd7503b9973..247f607106216 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/types.ts @@ -22,11 +22,23 @@ export interface ForcemergeFields { } interface HotPhaseMetaFields extends ForcemergeFields { - useRollover: boolean; + /** + * By default rollover is enabled with set values for max age, max size and max docs. In this policy form + * opting in to default rollover overrides custom rollover values. + */ isUsingDefaultRollover: boolean; - maxStorageSizeUnit?: string; - maxAgeUnit?: string; + readonlyEnabled: boolean; + + /** + * If a policy has defined values other than the default rollover {@link defaultRolloverAction}, we store + * them here. + */ + customRollover: { + enabled: boolean; + maxStorageSizeUnit?: string; + maxAgeUnit?: string; + }; } interface WarmPhaseMetaFields extends DataAllocationMetaFields, MinAgeField, ForcemergeFields { From 021bb4e3e9f73cafd4f0c3be9aaa2af2dc748ae3 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 7 Jan 2021 13:10:30 -0600 Subject: [PATCH 14/24] Create runtime field plugin, runtime field editor plugin (#87387) * create runtime field plugin, runtime field editor plugin --- docs/developer/plugin-list.asciidoc | 8 +++-- packages/kbn-optimizer/limits.yml | 3 +- src/plugins/runtime_fields/README.mdx | 4 +++ .../runtime_fields/common/constants.ts | 20 +++++++++++++ src/plugins/runtime_fields/common/index.ts | 21 ++++++++++++++ src/plugins/runtime_fields/common/types.ts | 29 +++++++++++++++++++ src/plugins/runtime_fields/kibana.json | 6 ++++ src/plugins/runtime_fields/public/index.ts | 28 ++++++++++++++++++ x-pack/.i18nrc.json | 2 +- x-pack/plugins/index_management/kibana.json | 2 +- .../mappings_editor/shared_imports.ts | 2 +- .../README.md | 4 +-- .../jest.config.js | 2 +- .../kibana.json | 2 +- .../public/__jest__/setup_environment.tsx | 0 .../public/components/index.ts | 0 .../components/runtime_field_editor/index.ts | 0 .../runtime_field_editor.test.tsx | 0 .../runtime_field_editor.tsx | 0 .../index.ts | 0 ...ntime_field_editor_flyout_content.test.tsx | 0 .../runtime_field_editor_flyout_content.tsx | 0 .../components/runtime_field_form/index.ts | 0 .../runtime_field_form.test.tsx | 0 .../runtime_field_form/runtime_field_form.tsx | 0 .../components/runtime_field_form/schema.ts | 0 .../public/constants.ts | 6 +--- .../public/index.ts | 0 .../public/lib/documentation.ts | 0 .../public/lib/index.ts | 0 .../public/load_editor.tsx | 0 .../public/plugin.test.ts | 0 .../public/plugin.ts | 0 .../public/shared_imports.ts | 0 .../public/test_utils.ts | 0 .../public/types.ts | 16 ++++------ 36 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 src/plugins/runtime_fields/README.mdx create mode 100644 src/plugins/runtime_fields/common/constants.ts create mode 100644 src/plugins/runtime_fields/common/index.ts create mode 100644 src/plugins/runtime_fields/common/types.ts create mode 100644 src/plugins/runtime_fields/kibana.json create mode 100644 src/plugins/runtime_fields/public/index.ts rename x-pack/plugins/{runtime_fields => runtime_field_editor}/README.md (98%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/jest.config.js (83%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/kibana.json (88%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/__jest__/setup_environment.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/index.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_editor/index.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_editor/runtime_field_editor.test.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_editor/runtime_field_editor.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_editor_flyout_content/index.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.test.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_form/index.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_form/runtime_field_form.test.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_form/runtime_field_form.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/components/runtime_field_form/schema.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/constants.ts (75%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/index.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/lib/documentation.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/lib/index.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/load_editor.tsx (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/plugin.test.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/plugin.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/shared_imports.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/test_utils.ts (100%) rename x-pack/plugins/{runtime_fields => runtime_field_editor}/public/types.ts (80%) diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index d4d2b229eeba7..c79e46c1d9173 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -168,6 +168,10 @@ Content is fetched from the remote (https://feeds.elastic.co and https://feeds-s |Create choropleth maps. Display the results of a term-aggregation as e.g. countries, zip-codes, states. +|{kib-repo}blob/{branch}/src/plugins/runtime_fields/README.mdx[runtimeFields] +|The runtime fields plugin provides types and constants for OSS and xpack runtime field related code. + + |{kib-repo}blob/{branch}/src/plugins/saved_objects/README.md[savedObjects] |The savedObjects plugin exposes utilities to manipulate saved objects on the client side. @@ -483,8 +487,8 @@ Elastic. |Welcome to the Kibana rollup plugin! This plugin provides Kibana support for Elasticsearch's rollup feature. Please refer to the Elasticsearch documentation to understand rollup indices and how to create rollup jobs. -|{kib-repo}blob/{branch}/x-pack/plugins/runtime_fields/README.md[runtimeFields] -|Welcome to the home of the runtime field editor and everything related to runtime fields! +|{kib-repo}blob/{branch}/x-pack/plugins/runtime_field_editor/README.md[runtimeFieldEditor] +|Welcome to the home of the runtime field editor! |{kib-repo}blob/{branch}/x-pack/plugins/saved_objects_tagging/README.md[savedObjectsTagging] diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 08d883a7cbb4d..67287089489e1 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -102,6 +102,7 @@ pageLoadAssetSize: visualizations: 295025 visualize: 57431 watcher: 43598 - runtimeFields: 41752 + runtimeFields: 10000 stackAlerts: 29684 presentationUtil: 28545 + runtimeFieldEditor: 46986 diff --git a/src/plugins/runtime_fields/README.mdx b/src/plugins/runtime_fields/README.mdx new file mode 100644 index 0000000000000..15985b07caf96 --- /dev/null +++ b/src/plugins/runtime_fields/README.mdx @@ -0,0 +1,4 @@ + +# Runtime Fields + +The runtime fields plugin provides types and constants for OSS and xpack runtime field related code. diff --git a/src/plugins/runtime_fields/common/constants.ts b/src/plugins/runtime_fields/common/constants.ts new file mode 100644 index 0000000000000..568003508f4bd --- /dev/null +++ b/src/plugins/runtime_fields/common/constants.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const; diff --git a/src/plugins/runtime_fields/common/index.ts b/src/plugins/runtime_fields/common/index.ts new file mode 100644 index 0000000000000..b08ac661a4bd6 --- /dev/null +++ b/src/plugins/runtime_fields/common/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './constants'; +export * from './types'; diff --git a/src/plugins/runtime_fields/common/types.ts b/src/plugins/runtime_fields/common/types.ts new file mode 100644 index 0000000000000..f16d3d75d6ecf --- /dev/null +++ b/src/plugins/runtime_fields/common/types.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { RUNTIME_FIELD_TYPES } from './constants'; + +export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; +export interface RuntimeField { + name: string; + type: RuntimeType; + script: { + source: string; + }; +} diff --git a/src/plugins/runtime_fields/kibana.json b/src/plugins/runtime_fields/kibana.json new file mode 100644 index 0000000000000..e71116f81532e --- /dev/null +++ b/src/plugins/runtime_fields/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "runtimeFields", + "version": "kibana", + "server": false, + "ui": true +} diff --git a/src/plugins/runtime_fields/public/index.ts b/src/plugins/runtime_fields/public/index.ts new file mode 100644 index 0000000000000..a7a94b07ac6e8 --- /dev/null +++ b/src/plugins/runtime_fields/public/index.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from '../common'; + +export function plugin() { + return { + setup() {}, + start() {}, + stop() {}, + }; +} diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 6937862d20536..7380d25930bc0 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -42,7 +42,7 @@ "xpack.remoteClusters": "plugins/remote_clusters", "xpack.reporting": ["plugins/reporting"], "xpack.rollupJobs": ["plugins/rollup"], - "xpack.runtimeFields": "plugins/runtime_fields", + "xpack.runtimeFields": "plugins/runtime_field_editor", "xpack.searchProfiler": "plugins/searchprofiler", "xpack.security": "plugins/security", "xpack.server": "legacy/server", diff --git a/x-pack/plugins/index_management/kibana.json b/x-pack/plugins/index_management/kibana.json index 5dcff0ba942e1..af3d61c8808ef 100644 --- a/x-pack/plugins/index_management/kibana.json +++ b/x-pack/plugins/index_management/kibana.json @@ -9,6 +9,6 @@ "requiredBundles": [ "kibanaReact", "esUiShared", - "runtimeFields" + "runtimeFieldEditor" ] } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts index 36f7fecbcff21..652925a977fa0 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts @@ -58,7 +58,7 @@ export { RuntimeField, RuntimeFieldEditorFlyoutContent, RuntimeFieldEditorFlyoutContentProps, -} from '../../../../../runtime_fields/public'; +} from '../../../../../runtime_field_editor/public'; export { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/runtime_fields/README.md b/x-pack/plugins/runtime_field_editor/README.md similarity index 98% rename from x-pack/plugins/runtime_fields/README.md rename to x-pack/plugins/runtime_field_editor/README.md index eb7b31e6e1154..1eddfd75a39f3 100644 --- a/x-pack/plugins/runtime_fields/README.md +++ b/x-pack/plugins/runtime_field_editor/README.md @@ -1,6 +1,6 @@ -# Runtime fields +# Runtime fields editor -Welcome to the home of the runtime field editor and everything related to runtime fields! +Welcome to the home of the runtime field editor! ## The runtime field editor diff --git a/x-pack/plugins/runtime_fields/jest.config.js b/x-pack/plugins/runtime_field_editor/jest.config.js similarity index 83% rename from x-pack/plugins/runtime_fields/jest.config.js rename to x-pack/plugins/runtime_field_editor/jest.config.js index 9c4ec56593c8b..f5a9e988fd867 100644 --- a/x-pack/plugins/runtime_fields/jest.config.js +++ b/x-pack/plugins/runtime_field_editor/jest.config.js @@ -7,5 +7,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../../..', - roots: ['/x-pack/plugins/runtime_fields'], + roots: ['/x-pack/plugins/runtime_field_editor'], }; diff --git a/x-pack/plugins/runtime_fields/kibana.json b/x-pack/plugins/runtime_field_editor/kibana.json similarity index 88% rename from x-pack/plugins/runtime_fields/kibana.json rename to x-pack/plugins/runtime_field_editor/kibana.json index 65932c723c474..3270ada544ba4 100644 --- a/x-pack/plugins/runtime_fields/kibana.json +++ b/x-pack/plugins/runtime_field_editor/kibana.json @@ -1,5 +1,5 @@ { - "id": "runtimeFields", + "id": "runtimeFieldEditor", "version": "kibana", "server": false, "ui": true, diff --git a/x-pack/plugins/runtime_fields/public/__jest__/setup_environment.tsx b/x-pack/plugins/runtime_field_editor/public/__jest__/setup_environment.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/__jest__/setup_environment.tsx rename to x-pack/plugins/runtime_field_editor/public/__jest__/setup_environment.tsx diff --git a/x-pack/plugins/runtime_fields/public/components/index.ts b/x-pack/plugins/runtime_field_editor/public/components/index.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/index.ts rename to x-pack/plugins/runtime_field_editor/public/components/index.ts diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_editor/index.ts b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor/index.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_editor/index.ts rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor/index.ts diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_editor/runtime_field_editor.test.tsx b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor/runtime_field_editor.test.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_editor/runtime_field_editor.test.tsx rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor/runtime_field_editor.test.tsx diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_editor/runtime_field_editor.tsx b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor/runtime_field_editor.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_editor/runtime_field_editor.tsx rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor/runtime_field_editor.tsx diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_editor_flyout_content/index.ts b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor_flyout_content/index.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_editor_flyout_content/index.ts rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor_flyout_content/index.ts diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.test.tsx b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.test.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.test.tsx rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.test.tsx diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.tsx b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.tsx rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_editor_flyout_content/runtime_field_editor_flyout_content.tsx diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_form/index.ts b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/index.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_form/index.ts rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/index.ts diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.test.tsx b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/runtime_field_form.test.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.test.tsx rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/runtime_field_form.test.tsx diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.tsx b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/runtime_field_form.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.tsx rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/runtime_field_form.tsx diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_form/schema.ts b/x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/schema.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/components/runtime_field_form/schema.ts rename to x-pack/plugins/runtime_field_editor/public/components/runtime_field_form/schema.ts diff --git a/x-pack/plugins/runtime_fields/public/constants.ts b/x-pack/plugins/runtime_field_editor/public/constants.ts similarity index 75% rename from x-pack/plugins/runtime_fields/public/constants.ts rename to x-pack/plugins/runtime_field_editor/public/constants.ts index 017b58c246afe..eebc3007d79d5 100644 --- a/x-pack/plugins/runtime_fields/public/constants.ts +++ b/x-pack/plugins/runtime_field_editor/public/constants.ts @@ -3,11 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ComboBoxOption } from './types'; - -export const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const; - -type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; +import { ComboBoxOption, RuntimeType } from './types'; export const RUNTIME_FIELD_OPTIONS: Array> = [ { diff --git a/x-pack/plugins/runtime_fields/public/index.ts b/x-pack/plugins/runtime_field_editor/public/index.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/index.ts rename to x-pack/plugins/runtime_field_editor/public/index.ts diff --git a/x-pack/plugins/runtime_fields/public/lib/documentation.ts b/x-pack/plugins/runtime_field_editor/public/lib/documentation.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/lib/documentation.ts rename to x-pack/plugins/runtime_field_editor/public/lib/documentation.ts diff --git a/x-pack/plugins/runtime_fields/public/lib/index.ts b/x-pack/plugins/runtime_field_editor/public/lib/index.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/lib/index.ts rename to x-pack/plugins/runtime_field_editor/public/lib/index.ts diff --git a/x-pack/plugins/runtime_fields/public/load_editor.tsx b/x-pack/plugins/runtime_field_editor/public/load_editor.tsx similarity index 100% rename from x-pack/plugins/runtime_fields/public/load_editor.tsx rename to x-pack/plugins/runtime_field_editor/public/load_editor.tsx diff --git a/x-pack/plugins/runtime_fields/public/plugin.test.ts b/x-pack/plugins/runtime_field_editor/public/plugin.test.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/plugin.test.ts rename to x-pack/plugins/runtime_field_editor/public/plugin.test.ts diff --git a/x-pack/plugins/runtime_fields/public/plugin.ts b/x-pack/plugins/runtime_field_editor/public/plugin.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/plugin.ts rename to x-pack/plugins/runtime_field_editor/public/plugin.ts diff --git a/x-pack/plugins/runtime_fields/public/shared_imports.ts b/x-pack/plugins/runtime_field_editor/public/shared_imports.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/shared_imports.ts rename to x-pack/plugins/runtime_field_editor/public/shared_imports.ts diff --git a/x-pack/plugins/runtime_fields/public/test_utils.ts b/x-pack/plugins/runtime_field_editor/public/test_utils.ts similarity index 100% rename from x-pack/plugins/runtime_fields/public/test_utils.ts rename to x-pack/plugins/runtime_field_editor/public/test_utils.ts diff --git a/x-pack/plugins/runtime_fields/public/types.ts b/x-pack/plugins/runtime_field_editor/public/types.ts similarity index 80% rename from x-pack/plugins/runtime_fields/public/types.ts rename to x-pack/plugins/runtime_field_editor/public/types.ts index b1bbb06d79655..984d6ab9f8655 100644 --- a/x-pack/plugins/runtime_fields/public/types.ts +++ b/x-pack/plugins/runtime_field_editor/public/types.ts @@ -4,8 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { DataPublicPluginStart } from 'src/plugins/data/public'; +export type { + RuntimeField, + RuntimeType, + RUNTIME_FIELD_TYPES, +} from 'src/plugins/runtime_fields/common'; -import { RUNTIME_FIELD_TYPES } from './constants'; import { OpenRuntimeFieldEditorProps } from './load_editor'; export interface LoadEditorResponse { @@ -26,16 +30,6 @@ export interface StartPlugins { data: DataPublicPluginStart; } -export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; - -export interface RuntimeField { - name: string; - type: RuntimeType; - script: { - source: string; - }; -} - export interface ComboBoxOption { label: string; value?: T; From e5fe735d76497b666ff8f47a7e7cdfb51fa27e5b Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Thu, 7 Jan 2021 13:24:53 -0600 Subject: [PATCH 15/24] TS project references for apmOss plugin (#87676) References #80508. References #81003. --- src/plugins/apm_oss/tsconfig.json | 18 ++++++++++++++++++ tsconfig.json | 6 ++++-- tsconfig.refs.json | 3 ++- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/plugins/apm_oss/tsconfig.json diff --git a/src/plugins/apm_oss/tsconfig.json b/src/plugins/apm_oss/tsconfig.json new file mode 100644 index 0000000000000..aeb6837c69a99 --- /dev/null +++ b/src/plugins/apm_oss/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 + "server/tutorial/index_pattern.json" + ], + "references": [{ "path": "../../core/tsconfig.json" }, { "path": "../home/tsconfig.json" }] +} diff --git a/tsconfig.json b/tsconfig.json index 75e1b097c734f..d882697bbf484 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "exclude": [ "src/**/__fixtures__/**/*", "src/core/**/*", + "src/plugins/apm_oss/**/*", "src/plugins/bfetch/**/*", "src/plugins/data/**/*", "src/plugins/dev_tools/**/*", @@ -28,7 +29,7 @@ "src/plugins/telemetry_collection_manager/**/*", "src/plugins/ui_actions/**/*", "src/plugins/url_forwarding/**/*", - "src/plugins/usage_collection/**/*", + "src/plugins/usage_collection/**/*" // In the build we actually exclude **/public/**/* from this config so that // we can run the TSC on both this and the .browser version of this config // file, but if we did it during development IDEs would not be able to find @@ -37,6 +38,7 @@ ], "references": [ { "path": "./src/core/tsconfig.json" }, + { "path": "./src/plugins/apm_oss/tsconfig.json" }, { "path": "./src/plugins/bfetch/tsconfig.json" }, { "path": "./src/plugins/data/tsconfig.json" }, { "path": "./src/plugins/dev_tools/tsconfig.json" }, @@ -58,6 +60,6 @@ { "path": "./src/plugins/telemetry_collection_manager/tsconfig.json" }, { "path": "./src/plugins/ui_actions/tsconfig.json" }, { "path": "./src/plugins/url_forwarding/tsconfig.json" }, - { "path": "./src/plugins/usage_collection/tsconfig.json" }, + { "path": "./src/plugins/usage_collection/tsconfig.json" } ] } diff --git a/tsconfig.refs.json b/tsconfig.refs.json index 282bf7fb1f011..c712d46204f35 100644 --- a/tsconfig.refs.json +++ b/tsconfig.refs.json @@ -2,6 +2,7 @@ "include": [], "references": [ { "path": "./src/core/tsconfig.json" }, + { "path": "./src/plugins/apm_oss/tsconfig.json" }, { "path": "./src/plugins/bfetch/tsconfig.json" }, { "path": "./src/plugins/data/tsconfig.json" }, { "path": "./src/plugins/dev_tools/tsconfig.json" }, @@ -23,6 +24,6 @@ { "path": "./src/plugins/telemetry_collection_manager/tsconfig.json" }, { "path": "./src/plugins/ui_actions/tsconfig.json" }, { "path": "./src/plugins/url_forwarding/tsconfig.json" }, - { "path": "./src/plugins/usage_collection/tsconfig.json" }, + { "path": "./src/plugins/usage_collection/tsconfig.json" } ] } From 52e3371c393bc8f33a1ffa24fffcaf909c19da4d Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Thu, 7 Jan 2021 14:37:27 -0500 Subject: [PATCH 16/24] [Lens] Transitions for reference-based operations (#83348) * [Lens] Transition between functions involving references * Organize transition cases and cover all the basic transitions * Add functional test * Change logic for displaying valid transitions * Show valid transitions more accurately * Fix transition to only consider valid outputs * Update test names and style Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dimension_panel/dimension_editor.tsx | 20 +- .../dimension_panel/dimension_panel.test.tsx | 131 +++- .../operations/__mocks__/index.ts | 1 + .../definitions/calculations/utils.ts | 19 +- .../operations/layer_helpers.test.ts | 674 +++++++++++++----- .../operations/layer_helpers.ts | 410 +++++++++-- .../test/functional/apps/lens/smokescreen.ts | 38 +- 7 files changed, 1018 insertions(+), 275 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index cc22cbbf57883..1144a1043c5b1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -30,6 +30,7 @@ import { updateColumnParam, resetIncomplete, FieldBasedIndexPatternColumn, + canTransition, } from '../operations'; import { mergeLayer } from '../state_helpers'; import { FieldSelect } from './field_select'; @@ -147,15 +148,20 @@ export function DimensionEditor(props: DimensionEditorProps) { const operationsWithCompatibility = [...possibleOperations].map((operationType) => { const definition = operationDefinitionMap[operationType]; + const currentField = + selectedColumn && + hasField(selectedColumn) && + currentIndexPattern.getFieldByName(selectedColumn.sourceField); return { operationType, - compatibleWithCurrentField: - !selectedColumn || - (selectedColumn && - hasField(selectedColumn) && - definition.input === 'field' && - fieldByOperation[operationType]?.has(selectedColumn.sourceField)) || - (selectedColumn && !hasField(selectedColumn) && definition.input === 'none'), + compatibleWithCurrentField: canTransition({ + layer: state.layers[layerId], + columnId, + op: operationType, + indexPattern: currentIndexPattern, + field: currentField || undefined, + filterOperations: props.filterOperations, + }), disabledStatus: definition.getDisabledStatus && definition.getDisabledStatus( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 5d477d98d042d..fc6c317365886 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -337,17 +337,124 @@ describe('IndexPatternDimensionEditorPanel', () => { const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; - expect(items.find(({ label }) => label === 'Minimum')!['data-test-subj']).not.toContain( + expect(items.find(({ id }) => id === 'min')!['data-test-subj']).not.toContain('incompatible'); + expect(items.find(({ id }) => id === 'date_histogram')!['data-test-subj']).toContain( 'incompatible' ); + // Incompatible because there is no date field + expect(items.find(({ id }) => id === 'cumulative_sum')!['data-test-subj']).toContain( + 'incompatible' + ); + + expect(items.find(({ id }) => id === 'filters')!['data-test-subj']).not.toContain( + 'incompatible' + ); + }); + + it('should indicate when a transition is invalid due to filterOperations', () => { + wrapper = mount( + meta.dataType === 'number' && !meta.isBucketed} + /> + ); + + const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; + + expect(items.find(({ id }) => id === 'min')!['data-test-subj']).toContain('incompatible'); + expect(items.find(({ id }) => id === 'cumulative_sum')!['data-test-subj']).toContain( + 'incompatible' + ); + }); + + it('should indicate that reference-based operations are not compatible when they are incomplete', () => { + wrapper = mount( + + ); + + const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; + + expect(items.find(({ id }) => id === 'derivative')!['data-test-subj']).toContain( + 'incompatible' + ); + expect(items.find(({ id }) => id === 'cumulative_sum')!['data-test-subj']).toContain( + 'incompatible' + ); + expect(items.find(({ id }) => id === 'moving_average')!['data-test-subj']).toContain( + 'incompatible' + ); + }); - expect(items.find(({ label }) => label === 'Date histogram')!['data-test-subj']).toContain( + it('should indicate that reference-based operations are compatible sometimes', () => { + wrapper = mount( + + ); + + const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; + + expect(items.find(({ id }) => id === 'counter_rate')!['data-test-subj']).toContain( 'incompatible' ); - // Fieldless operation is compatible with field - expect(items.find(({ label }) => label === 'Filters')!['data-test-subj']).toContain( - 'compatible' + expect(items.find(({ id }) => id === 'derivative')!['data-test-subj']).not.toContain( + 'incompatible' + ); + expect(items.find(({ id }) => id === 'moving_average')!['data-test-subj']).not.toContain( + 'incompatible' ); }); @@ -640,9 +747,7 @@ describe('IndexPatternDimensionEditorPanel', () => { .find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]') .simulate('click'); - wrapper - .find('button[data-test-subj="lns-indexPatternDimension-filters incompatible"]') - .simulate('click'); + wrapper.find('button[data-test-subj="lns-indexPatternDimension-filters"]').simulate('click'); expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); }); @@ -1623,7 +1728,15 @@ describe('IndexPatternDimensionEditorPanel', () => { id: '1', title: 'my-fake-index-pattern', hasRestrictions: false, - fields, + fields: [ + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + ], getFieldByName: getFieldByNameFactory([ { name: 'bytes', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts index 6d7a0117a1770..3d10080aea0c6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts @@ -43,6 +43,7 @@ export const { isReferenced, resetIncomplete, isOperationAllowedAsReference, + canTransition, } = actualHelpers; export const { adjustTimeScaleLabelSuffix, DEFAULT_TIME_SCALE } = actualTimeScaleUtils; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts index ca4b7c53b7ec7..8058f0a264229 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts @@ -10,8 +10,8 @@ import type { TimeScaleUnit } from '../../../time_scale'; import type { IndexPattern, IndexPatternLayer } from '../../../types'; import { adjustTimeScaleLabelSuffix } from '../../time_scale_utils'; import type { ReferenceBasedIndexPatternColumn } from '../column_types'; +import { isColumnValidAsReference } from '../../layer_helpers'; import { operationDefinitionMap } from '..'; -import type { IndexPatternColumn, RequiredReference } from '..'; export const buildLabelFunction = (ofName: (name?: string) => string) => ( name?: string, @@ -85,23 +85,6 @@ export function checkReferences(layer: IndexPatternLayer, columnId: string) { return errors.length ? errors : undefined; } -export function isColumnValidAsReference({ - column, - validation, -}: { - column: IndexPatternColumn; - validation: RequiredReference; -}): boolean { - if (!column) return false; - const operationType = column.operationType; - const operationDefinition = operationDefinitionMap[operationType]; - return ( - validation.input.includes(operationDefinition.input) && - (!validation.specificOperations || validation.specificOperations.includes(operationType)) && - validation.validateMetadata(column) - ); -} - export function getErrorsForDateReference( layer: IndexPatternLayer, columnId: string, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index 9496f95f74dec..e0d9d864e5656 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -864,7 +864,7 @@ describe('state_helpers', () => { columns: { col1: termsColumn, willBeReference: { - label: 'Count', + label: 'Count of records', dataType: 'number', isBucketed: false, sourceField: 'Records', @@ -878,14 +878,18 @@ describe('state_helpers', () => { }); expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( - { - indexPatternId: '1', - columnOrder: ['col1', 'willBeReference'], + expect.objectContaining({ columns: { col1: { ...termsColumn, params: { orderBy: { type: 'alphabetical' }, orderDirection: 'asc', size: 5 }, }, + id1: expect.objectContaining({ + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }), willBeReference: expect.objectContaining({ dataType: 'number', isBucketed: false, @@ -893,225 +897,531 @@ describe('state_helpers', () => { }), }, incompleteColumns: {}, - }, + }), 'col1', 'willBeReference' ); }); - it('should not wrap the previous operation when switching to reference', () => { - const layer: IndexPatternLayer = { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Count', - customLabel: true, - dataType: 'number' as const, - isBucketed: false, - sourceField: 'Records', - operationType: 'count' as const, - }, - }, - }; - const result = replaceColumn({ - layer, - indexPattern, - columnId: 'col1', - op: 'testReference' as OperationType, + describe('switching from non-reference to reference test cases', () => { + it('should wrap around the previous operation as a reference if possible (case new1)', () => { + const expectedColumn = { + label: 'Count', + customLabel: true, + dataType: 'number' as const, + isBucketed: false, + sourceField: 'Records', + operationType: 'count' as const, + }; + + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { col1: expectedColumn }, + }; + const result = replaceColumn({ + layer, + indexPattern, + columnId: 'col1', + op: 'testReference' as OperationType, + }); + + expect(operationDefinitionMap.testReference.buildColumn).toHaveBeenCalledWith( + expect.objectContaining({ + referenceIds: ['id1'], + }) + ); + expect(result.columnOrder).toEqual(['id1', 'col1']); + expect(result.columns).toEqual( + expect.objectContaining({ + id1: expectedColumn, + col1: expect.any(Object), + }) + ); }); - expect(operationDefinitionMap.testReference.buildColumn).toHaveBeenCalledWith( - expect.objectContaining({ - referenceIds: ['id1'], - }) - ); - expect(result.columns).toEqual( - expect.objectContaining({ - col1: expect.objectContaining({ operationType: 'testReference' }), - }) - ); - }); + it('should create a new no-input operation to use as reference (case new2)', () => { + // @ts-expect-error this function is not valid + operationDefinitionMap.testReference.requiredReferences = [ + { + input: ['none'], + validateMetadata: () => true, + }, + ]; + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Avg', + dataType: 'number' as const, + isBucketed: false, + sourceField: 'bytes', + operationType: 'avg' as const, + }, + }, + }; + const result = replaceColumn({ + layer, + indexPattern, + columnId: 'col1', + // @ts-expect-error + op: 'testReference', + }); - it('should delete the previous references and reset to default values when going from reference to no-input', () => { - // @ts-expect-error this function is not valid - operationDefinitionMap.testReference.requiredReferences = [ - { - input: ['none'], - validateMetadata: () => true, - }, - ]; - const expectedCol = { - dataType: 'string' as const, - isBucketed: true, + expect(result.columnOrder).toEqual(['id1', 'col1']); + expect(result.columns).toEqual({ + id1: expect.objectContaining({ + operationType: 'filters', + }), + col1: expect.objectContaining({ + operationType: 'testReference', + }), + }); + }); - operationType: 'filters' as const, - params: { - // These filters are reset - filters: [{ input: { query: 'field: true', language: 'kuery' }, label: 'Custom label' }], - }, - }; - const layer: IndexPatternLayer = { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - ...expectedCol, - label: 'Custom label', - customLabel: true, + it('should use the previous field, but select the best operation, when creating a reference (case new3)', () => { + // @ts-expect-error this function is not valid + operationDefinitionMap.testReference.requiredReferences = [ + { + input: ['field'], + validateMetadata: () => true, + specificOperations: ['cardinality', 'sum', 'avg'], // this order is ignored }, - col2: { - label: 'Test reference', - dataType: 'number', - isBucketed: false, + ]; + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Max', + dataType: 'number' as const, + isBucketed: false, + sourceField: 'bytes', + operationType: 'max' as const, + }, + }, + }; + const result = replaceColumn({ + layer, + indexPattern, + columnId: 'col1', + // @ts-expect-error test only + op: 'testReference', + }); - // @ts-expect-error not a valid type + expect(result.columnOrder).toEqual(['id1', 'col1']); + expect(result.columns).toEqual({ + id1: expect.objectContaining({ + operationType: 'avg', + }), + col1: expect.objectContaining({ operationType: 'testReference', - references: ['col1'], + }), + }); + }); + + it('should ignore previous field and previous operation, but set incomplete operation if known (case new4)', () => { + // @ts-expect-error this function is not valid + operationDefinitionMap.testReference.requiredReferences = [ + { + input: ['field'], + validateMetadata: () => true, + specificOperations: ['cardinality'], }, - }, - }; - expect( - replaceColumn({ + ]; + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Count', + dataType: 'number' as const, + isBucketed: false, + sourceField: 'Records', + operationType: 'count' as const, + }, + }, + }; + const result = replaceColumn({ layer, indexPattern, - columnId: 'col2', - op: 'filters', - }) - ).toEqual( - expect.objectContaining({ - columnOrder: ['col2'], + columnId: 'col1', + // @ts-expect-error + op: 'testReference', + }); + + expect(result.incompleteColumns).toEqual({ + id1: { operationType: 'cardinality' }, + }); + expect(result.columns).toEqual({ + col1: expect.objectContaining({ + operationType: 'testReference', + }), + }); + }); + + it('should leave an empty reference if all the other cases fail (case new6)', () => { + // @ts-expect-error this function is not valid + operationDefinitionMap.testReference.requiredReferences = [ + { + input: ['field'], + validateMetadata: () => false, + specificOperations: [], + }, + ]; + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], columns: { - col2: { - ...expectedCol, - label: 'Filters', - scale: 'ordinal', // added in buildColumn - params: { - filters: [{ input: { query: '', language: 'kuery' }, label: '' }], - }, + col1: { + label: 'Count', + dataType: 'number' as const, + isBucketed: false, + sourceField: 'Records', + operationType: 'count' as const, }, }, - }) - ); + }; + const result = replaceColumn({ + layer, + indexPattern, + columnId: 'col1', + // @ts-expect-error + op: 'testReference', + }); + + expect(result.incompleteColumns).toEqual({}); + expect(result.columns).toEqual({ + col1: expect.objectContaining({ + operationType: 'testReference', + references: ['id1'], + }), + }); + }); }); - it('should delete the inner references when switching away from reference to field-based operation', () => { - const expectedCol = { - label: 'Count of records', - dataType: 'number' as const, - isBucketed: false, + describe('switching from reference to reference test cases', () => { + beforeEach(() => { + operationDefinitionMap.secondTest = { + input: 'fullReference', + displayName: 'Reference test 2', + // @ts-expect-error this type is not statically available + type: 'secondTest', + requiredReferences: [ + { + // Any numeric metric that isn't also a reference + input: ['none', 'field'], + validateMetadata: (meta: OperationMetadata) => + meta.dataType === 'number' && !meta.isBucketed, + }, + ], + // @ts-expect-error don't want to define valid arguments + buildColumn: jest.fn((args) => { + return { + label: 'Test reference', + isBucketed: false, + dataType: 'number', - operationType: 'count' as const, - sourceField: 'Records', - }; - const layer: IndexPatternLayer = { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: expectedCol, - col2: { - label: 'Test reference', - dataType: 'number', - isBucketed: false, + operationType: 'secondTest', + references: args.referenceIds, + }; + }), + isTransferable: jest.fn(), + toExpression: jest.fn().mockReturnValue([]), + getPossibleOperation: jest + .fn() + .mockReturnValue({ dataType: 'number', isBucketed: false }), + getDefaultLabel: jest.fn().mockReturnValue('Test reference'), + }; + }); - // @ts-expect-error not a valid type - operationType: 'testReference', - references: ['col1'], + afterEach(() => { + delete operationDefinitionMap.secondTest; + }); + + it('should use existing references, delete invalid, when switching from one reference to another (case ref1)', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['ref1', 'invalid', 'output'], + columns: { + ref1: { + label: 'Count', + customLabel: true, + dataType: 'number' as const, + isBucketed: false, + + operationType: 'count' as const, + sourceField: 'Records', + }, + invalid: { + label: 'Test reference', + dataType: 'number', + isBucketed: false, + + // @ts-expect-error not a valid type + operationType: 'testReference', + references: [], + }, + output: { + label: 'Test reference', + dataType: 'number', + isBucketed: false, + + // @ts-expect-error not a valid type + operationType: 'testReference', + references: ['ref1', 'invalid'], + }, }, - }, - }; - expect( + }; + expect( + replaceColumn({ + layer, + indexPattern, + columnId: 'output', + // @ts-expect-error not statically available + op: 'secondTest', + }) + ).toEqual( + expect.objectContaining({ + columnOrder: ['ref1', 'output'], + columns: { + ref1: layer.columns.ref1, + output: expect.objectContaining({ references: ['ref1'] }), + }, + incompleteColumns: {}, + }) + ); + }); + + it('should modify a copied object, not the original layer', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['ref1', 'invalid', 'output'], + columns: { + ref1: { + label: 'Count', + customLabel: true, + dataType: 'number' as const, + isBucketed: false, + + operationType: 'count' as const, + sourceField: 'Records', + }, + invalid: { + label: 'Test reference', + dataType: 'number', + isBucketed: false, + + // @ts-expect-error not a valid type + operationType: 'testReference', + references: [], + }, + output: { + label: 'Test reference', + dataType: 'number', + isBucketed: false, + + // @ts-expect-error not a valid type + operationType: 'testReference', + references: ['ref1', 'invalid'], + }, + }, + }; replaceColumn({ layer, indexPattern, - columnId: 'col2', - op: 'count', - field: documentField, - }) - ).toEqual( - expect.objectContaining({ - columnOrder: ['col2'], + columnId: 'output', + // @ts-expect-error not statically available + op: 'secondTest', + }); + expect(layer.columns.output).toEqual( + expect.objectContaining({ references: ['ref1', 'invalid'] }) + ); + }); + + it('should transition by using the field from the previous reference if nothing else works (case new5)', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['fieldReused', 'output'], columns: { - col2: expect.objectContaining(expectedCol), + fieldReused: { + label: 'Date histogram', + dataType: 'date' as const, + isBucketed: true, + operationType: 'date_histogram' as const, + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + output: { + label: 'Test reference', + dataType: 'number', + isBucketed: false, + // @ts-expect-error not a valid type + operationType: 'testReference', + references: ['fieldReused'], + }, }, - }) - ); + }; + expect( + replaceColumn({ + layer, + indexPattern, + columnId: 'output', + // @ts-expect-error not statically available + op: 'secondTest', + }) + ).toEqual( + expect.objectContaining({ + columnOrder: ['id1', 'output'], + columns: { + id1: expect.objectContaining({ + sourceField: 'timestamp', + operationType: 'cardinality', + }), + output: expect.objectContaining({ references: ['id1'] }), + }, + incompleteColumns: {}, + }) + ); + }); }); - it('should reset when switching from one reference to another', () => { - operationDefinitionMap.secondTest = { - input: 'fullReference', - displayName: 'Reference test 2', - // @ts-expect-error this type is not statically available - type: 'secondTest', - requiredReferences: [ + describe('switching from reference to non-reference', () => { + it('should promote the inner references when switching away from reference to no-input (case a1)', () => { + // @ts-expect-error this function is not valid + operationDefinitionMap.testReference.requiredReferences = [ { - // Any numeric metric that isn't also a reference - input: ['none', 'field'], - validateMetadata: (meta: OperationMetadata) => - meta.dataType === 'number' && !meta.isBucketed, + input: ['none'], + validateMetadata: () => true, }, - ], - // @ts-expect-error don't want to define valid arguments - buildColumn: jest.fn((args) => { - return { - label: 'Test reference', - isBucketed: false, - dataType: 'number', + ]; + const expectedCol = { + label: 'Custom label', + customLabel: true, + dataType: 'string' as const, + isBucketed: true, - operationType: 'secondTest', - references: args.referenceIds, - }; - }), - isTransferable: jest.fn(), - toExpression: jest.fn().mockReturnValue([]), - getPossibleOperation: jest.fn().mockReturnValue({ dataType: 'number', isBucketed: false }), - getDefaultLabel: jest.fn().mockReturnValue('Test reference'), - }; + operationType: 'filters' as const, + params: { + // These filters are reset + filters: [ + { input: { query: 'field: true', language: 'kuery' }, label: 'Custom label' }, + ], + }, + }; + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: expectedCol, + col2: { + label: 'Test reference', + dataType: 'number', + isBucketed: false, - const layer: IndexPatternLayer = { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - label: 'Count', - customLabel: true, - dataType: 'number' as const, - isBucketed: false, + // @ts-expect-error not a valid type + operationType: 'testReference', + references: ['col1'], + }, + }, + }; + expect( + replaceColumn({ + layer, + indexPattern, + columnId: 'col2', + op: 'filters', + }) + ).toEqual( + expect.objectContaining({ + columnOrder: ['col2'], + columns: { + col2: expectedCol, + }, + }) + ); + }); - operationType: 'count' as const, - sourceField: 'Records', + it('should promote the inner references when switching away from reference to field-based operation (case a2)', () => { + const expectedCol = { + label: 'Count of records', + dataType: 'number' as const, + isBucketed: false, + + operationType: 'count' as const, + sourceField: 'Records', + }; + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: expectedCol, + col2: { + label: 'Default label', + dataType: 'number', + isBucketed: false, + + // @ts-expect-error not a valid type + operationType: 'testReference', + references: ['col1'], + }, }, - col2: { - label: 'Test reference', - dataType: 'number', - isBucketed: false, + }; + expect( + replaceColumn({ + layer, + indexPattern, + columnId: 'col2', + op: 'count', + field: documentField, + }) + ).toEqual( + expect.objectContaining({ + columnOrder: ['col2'], + columns: { + col2: expect.objectContaining(expectedCol), + }, + }) + ); + }); - // @ts-expect-error not a valid type - operationType: 'testReference', - references: ['col1'], + it('should promote only the field when going from reference to field-based operation (case a3)', () => { + const expectedColumn = { + dataType: 'number' as const, + isBucketed: false, + sourceField: 'bytes', + operationType: 'avg' as const, + }; + + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['metric', 'ref'], + columns: { + metric: { ...expectedColumn, label: 'Avg', customLabel: true }, + ref: { + label: 'Reference', + dataType: 'number', + isBucketed: false, + operationType: 'derivative', + references: ['metric'], + }, }, - }, - }; - expect( - replaceColumn({ + }; + const result = replaceColumn({ layer, indexPattern, - columnId: 'col2', - // @ts-expect-error not statically available - op: 'secondTest', - }) - ).toEqual( - expect.objectContaining({ - columnOrder: ['col2'], - columns: { - col2: expect.objectContaining({ references: ['id1'] }), - }, - incompleteColumns: {}, - }) - ); + columnId: 'ref', + op: 'sum', + }); - delete operationDefinitionMap.secondTest; + expect(result.columnOrder).toEqual(['ref']); + expect(result.columns).toEqual( + expect.objectContaining({ + ref: expect.objectContaining({ ...expectedColumn, operationType: 'sum' }), + }) + ); + }); }); it('should allow making a replacement on an operation that is being referenced, even if it ends up invalid', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 2d8078b9a6154..21fc36d7418ba 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -5,6 +5,7 @@ */ import _, { partition } from 'lodash'; +import type { OperationMetadata } from '../../types'; import { operationDefinitionMap, operationDefinitions, @@ -59,17 +60,11 @@ export function insertNewColumn({ } const possibleOperation = operationDefinition.getPossibleOperation(); const isBucketed = Boolean(possibleOperation.isBucketed); - if (isBucketed) { - return updateDefaultLabels( - addBucket(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), - indexPattern - ); - } else { - return updateDefaultLabels( - addMetric(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), - indexPattern - ); - } + const addOperationFn = isBucketed ? addBucket : addMetric; + return updateDefaultLabels( + addOperationFn(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), + indexPattern + ); } if (operationDefinition.input === 'fullReference') { @@ -78,9 +73,6 @@ export function insertNewColumn({ } let tempLayer = { ...layer }; const referenceIds = operationDefinition.requiredReferences.map((validation) => { - // TODO: This logic is too simple because it's not using fields. Once we have - // access to the operationSupportMatrix, we should validate the metadata against - // the possible fields const validOperations = Object.values(operationDefinitionMap).filter(({ type }) => isOperationAllowedAsReference({ validation, operationType: type, indexPattern }) ); @@ -240,42 +232,77 @@ export function replaceColumn({ tempLayer = resetIncomplete(tempLayer, columnId); - if (previousDefinition.input === 'fullReference') { - (previousColumn as ReferenceBasedIndexPatternColumn).references.forEach((id: string) => { - tempLayer = deleteColumn({ layer: tempLayer, columnId: id, indexPattern }); + if (operationDefinition.input === 'fullReference') { + return applyReferenceTransition({ + layer: tempLayer, + columnId, + previousColumn, + op, + indexPattern, }); } - tempLayer = resetIncomplete(tempLayer, columnId); + // Makes common inferences about what the user meant when switching away from a reference: + // 1. Switching from "Differences of max" to "max" will promote as-is + // 2. Switching from "Differences of avg of bytes" to "max" will keep the field, but change operation + if ( + previousDefinition.input === 'fullReference' && + (previousColumn as ReferenceBasedIndexPatternColumn).references.length === 1 + ) { + const previousReferenceId = (previousColumn as ReferenceBasedIndexPatternColumn) + .references[0]; + const referenceColumn = layer.columns[previousReferenceId]; + if (referenceColumn) { + const referencedOperation = operationDefinitionMap[referenceColumn.operationType]; + + if (referencedOperation.type === op) { + // Unit tests are labelled as case a1, case a2 + tempLayer = deleteColumn({ + layer: tempLayer, + columnId: previousReferenceId, + indexPattern, + }); - if (operationDefinition.input === 'fullReference') { - const referenceIds = operationDefinition.requiredReferences.map(() => generateId()); + tempLayer = { + ...tempLayer, + columns: { + ...tempLayer.columns, + [columnId]: copyCustomLabel({ ...referenceColumn }, previousColumn), + }, + }; + return updateDefaultLabels( + { + ...tempLayer, + columnOrder: getColumnOrder(tempLayer), + columns: adjustColumnReferencesForChangedColumn(tempLayer, columnId), + }, + indexPattern + ); + } else if ( + !field && + 'sourceField' in referenceColumn && + referencedOperation.input === 'field' && + operationDefinition.input === 'field' + ) { + // Unit test is case a3 + const matchedField = indexPattern.getFieldByName(referenceColumn.sourceField); + if (matchedField && operationDefinition.getPossibleOperationForField(matchedField)) { + field = matchedField; + } + } + } + } - const newLayer = { - ...tempLayer, - columns: { - ...tempLayer.columns, - [columnId]: operationDefinition.buildColumn({ - ...baseOptions, - layer: tempLayer, - referenceIds, - previousColumn, - }), - }, - }; - return updateDefaultLabels( - { - ...tempLayer, - columnOrder: getColumnOrder(newLayer), - columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), - }, - indexPattern - ); + // This logic comes after the transitions because they need to look at previous columns + if (previousDefinition.input === 'fullReference') { + (previousColumn as ReferenceBasedIndexPatternColumn).references.forEach((id: string) => { + tempLayer = deleteColumn({ layer: tempLayer, columnId: id, indexPattern }); + }); } if (operationDefinition.input === 'none') { let newColumn = operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer }); - newColumn = adjustLabel(newColumn, previousColumn); + newColumn = copyCustomLabel(newColumn, previousColumn); const newLayer = { ...tempLayer, columns: { ...tempLayer.columns, [columnId]: newColumn } }; return updateDefaultLabels( @@ -298,8 +325,18 @@ export function replaceColumn({ }; } + const validOperation = operationDefinition.getPossibleOperationForField(field); + if (!validOperation) { + return { + ...tempLayer, + incompleteColumns: { + ...(tempLayer.incompleteColumns ?? {}), + [columnId]: { operationType: op }, + }, + }; + } let newColumn = operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, field }); - newColumn = adjustLabel(newColumn, previousColumn); + newColumn = copyCustomLabel(newColumn, previousColumn); const newLayer = { ...tempLayer, columns: { ...tempLayer.columns, [columnId]: newColumn } }; return updateDefaultLabels( @@ -317,34 +354,274 @@ export function replaceColumn({ previousColumn.sourceField !== field.name ) { // Same operation, new field - const newColumn = operationDefinition.onFieldChange(previousColumn, field); - - if (previousColumn.customLabel) { - newColumn.customLabel = true; - newColumn.label = previousColumn.label; - } + const newColumn = copyCustomLabel( + operationDefinition.onFieldChange(previousColumn, field), + previousColumn + ); - const newLayer = { ...layer, columns: { ...layer.columns, [columnId]: newColumn } }; - return updateDefaultLabels( - { - ...resetIncomplete(layer, columnId), - columnOrder: getColumnOrder(newLayer), - columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), - }, - indexPattern + const newLayer = resetIncomplete( + { ...layer, columns: { ...layer.columns, [columnId]: newColumn } }, + columnId ); + return { + ...newLayer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }; } else { throw new Error('nothing changed'); } } -function adjustLabel(newColumn: IndexPatternColumn, previousColumn: IndexPatternColumn) { +export function canTransition({ + layer, + columnId, + op, + field, + indexPattern, + filterOperations, +}: ColumnChange & { + filterOperations: (meta: OperationMetadata) => boolean; +}): boolean { + const previousColumn = layer.columns[columnId]; + if (!previousColumn) { + return true; + } + + if (previousColumn.operationType === op) { + return true; + } + + try { + const newLayer = replaceColumn({ layer, columnId, op, field, indexPattern }); + const newDefinition = operationDefinitionMap[op]; + const newColumn = newLayer.columns[columnId]; + return ( + Boolean(newColumn) && + !newLayer.incompleteColumns?.[columnId] && + filterOperations(newColumn) && + !newDefinition.getErrorMessage?.(newLayer, columnId, indexPattern) + ); + } catch (e) { + return false; + } +} + +/** + * Function to transition to a fullReference from any different operation. + * It is always possible to transition to a fullReference, but there are multiple + * passes needed to copy all the previous state. These are the passes in priority + * order, each of which has a unit test: + * + * 1. Case ref1: referenced columns are an exact match + * Side effect: Modifies the reference list directly + * 2. Case new1: the previous column is an exact match. + * Side effect: Deletes and then inserts the previous column + * 3. Case new2: the reference supports `none` inputs, like filters. not visible in the UI. + * Side effect: Inserts a new column + * 4. Case new3, new4: Fuzzy matching on the previous field + * Side effect: Inserts a new column, or an incomplete column + * 5. Fuzzy matching based on the previous references (case new6) + * Side effect: Inserts a new column, or an incomplete column + * Side effect: Modifies the reference list directly + * 6. Case new6: Fall back by generating the column with empty references + */ +function applyReferenceTransition({ + layer, + columnId, + previousColumn, + op, + indexPattern, +}: { + layer: IndexPatternLayer; + columnId: string; + previousColumn: IndexPatternColumn; + op: OperationType; + indexPattern: IndexPattern; +}): IndexPatternLayer { + const operationDefinition = operationDefinitionMap[op]; + + if (operationDefinition.input !== 'fullReference') { + throw new Error(`Requirements for transitioning are not met`); + } + + let hasExactMatch = false; + let hasFieldMatch = false; + + const unusedReferencesQueue = + 'references' in previousColumn + ? [...(previousColumn as ReferenceBasedIndexPatternColumn).references] + : []; + + const referenceIds = operationDefinition.requiredReferences.map((validation) => { + const newId = generateId(); + + // First priority is to use any references that can be kept (case ref1) + if (unusedReferencesQueue.length) { + const otherColumn = layer.columns[unusedReferencesQueue[0]]; + if (isColumnValidAsReference({ validation, column: otherColumn })) { + return unusedReferencesQueue.shift()!; + } + } + + // Second priority is to wrap around the previous column (case new1) + if (!hasExactMatch && isColumnValidAsReference({ validation, column: previousColumn })) { + hasExactMatch = true; + + const newLayer = { ...layer, columns: { ...layer.columns, [newId]: { ...previousColumn } } }; + layer = { + ...layer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, newId), + }; + return newId; + } + + // Look for any fieldless operations that can be inserted directly (case new2) + if (validation.input.includes('none')) { + const validOperations = operationDefinitions.filter((def) => { + if (def.input !== 'none') return; + return isOperationAllowedAsReference({ + validation, + operationType: def.type, + indexPattern, + }); + }); + + if (validOperations.length === 1) { + layer = insertNewColumn({ + layer, + columnId: newId, + op: validOperations[0].type, + indexPattern, + }); + return newId; + } + } + + // Try to reuse the previous field by finding a possible operation. Because we've alredy + // checked for an exact operation match, this is guaranteed to be different from previousColumn + if (!hasFieldMatch && 'sourceField' in previousColumn && validation.input.includes('field')) { + const defIgnoringfield = operationDefinitions + .filter( + (def) => + def.input === 'field' && + isOperationAllowedAsReference({ validation, operationType: def.type, indexPattern }) + ) + .sort(getSortScoreByPriority); + + // No exact match found, so let's determine that the current field can be reused + const defWithField = defIgnoringfield.filter((def) => { + const previousField = indexPattern.getFieldByName(previousColumn.sourceField); + if (!previousField) return; + return isOperationAllowedAsReference({ + validation, + operationType: def.type, + field: previousField, + indexPattern, + }); + }); + + if (defWithField.length > 0) { + // Found the best match that keeps the field (case new3) + hasFieldMatch = true; + layer = insertNewColumn({ + layer, + columnId: newId, + op: defWithField[0].type, + indexPattern, + field: indexPattern.getFieldByName(previousColumn.sourceField), + }); + return newId; + } else if (defIgnoringfield.length === 1) { + // Can't use the field, but there is an exact match on the operation (case new4) + hasFieldMatch = true; + layer = { + ...layer, + incompleteColumns: { + ...layer.incompleteColumns, + [newId]: { operationType: defIgnoringfield[0].type }, + }, + }; + return newId; + } + } + + // Look for field-based references that we can use to assign a new field-based operation from (case new5) + if (unusedReferencesQueue.length) { + const otherColumn = layer.columns[unusedReferencesQueue[0]]; + if (otherColumn && 'sourceField' in otherColumn && validation.input.includes('field')) { + const previousField = indexPattern.getFieldByName(otherColumn.sourceField); + if (previousField) { + const defWithField = operationDefinitions + .filter( + (def) => + def.input === 'field' && + isOperationAllowedAsReference({ + validation, + operationType: def.type, + field: previousField, + indexPattern, + }) + ) + .sort(getSortScoreByPriority); + + if (defWithField.length > 0) { + layer = insertNewColumn({ + layer, + columnId: newId, + op: defWithField[0].type, + indexPattern, + field: previousField, + }); + return newId; + } + } + } + } + + // The reference is too ambiguous at this point, but instead of throwing an error (case new6) + return newId; + }); + + if (unusedReferencesQueue.length) { + unusedReferencesQueue.forEach((id: string) => { + layer = deleteColumn({ + layer, + columnId: id, + indexPattern, + }); + }); + } + + layer = { + ...layer, + columns: { + ...layer.columns, + [columnId]: operationDefinition.buildColumn({ + indexPattern, + layer, + referenceIds, + previousColumn, + }), + }, + }; + return updateDefaultLabels( + { + ...layer, + columnOrder: getColumnOrder(layer), + columns: adjustColumnReferencesForChangedColumn(layer, columnId), + }, + indexPattern + ); +} + +function copyCustomLabel(newColumn: IndexPatternColumn, previousColumn: IndexPatternColumn) { const adjustedColumn = { ...newColumn }; if (previousColumn.customLabel) { adjustedColumn.customLabel = true; adjustedColumn.label = previousColumn.label; } - return adjustedColumn; } @@ -664,3 +941,20 @@ export function resetIncomplete(layer: IndexPatternLayer, columnId: string): Ind delete incompleteColumns[columnId]; return { ...layer, incompleteColumns }; } + +export function isColumnValidAsReference({ + column, + validation, +}: { + column: IndexPatternColumn; + validation: RequiredReference; +}): boolean { + if (!column) return false; + const operationType = column.operationType; + const operationDefinition = operationDefinitionMap[operationType]; + return ( + validation.input.includes(operationDefinition.input) && + (!validation.specificOperations || validation.specificOperations.includes(operationType)) && + validation.validateMetadata(column) + ); +} diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 1d287447461e6..f2d91c2ae577f 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -74,7 +74,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.configureDimension({ dimension: 'lnsXY_splitDimensionPanel > lns-dimensionTrigger', operation: 'filters', - isPreviousIncompatible: true, keepOpen: true, }); await PageObjects.lens.addFilterToAgg(`geo.src : CN`); @@ -478,6 +477,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); + it('should keep the field selection while transitioning to every reference-based operation', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'avg', + field: 'bytes', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger', + operation: 'counter_rate', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger', + operation: 'cumulative_sum', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger', + operation: 'derivative', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger', + operation: 'moving_average', + }); + + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( + 'Moving average of Sum of bytes' + ); + }); + it('should allow to change index pattern', async () => { await PageObjects.lens.switchFirstLayerIndexPattern('log*'); expect(await PageObjects.lens.getFirstLayerIndexPattern()).to.equal('log*'); From 1c30525d4667d3cb03abe1cc7a9e7973e9d7471e Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Thu, 7 Jan 2021 11:50:48 -0800 Subject: [PATCH 17/24] [Alerting UI] Make connector reducer as generic type. (#86857) * - * fixed failing tests * fixed typescript checks * fixed typescript checks * fixed failing build * fixed typescript checks * removed typo cast * fixed failing test * fixed faling build --- .../action_connector_form.tsx | 41 +++++++--- .../connector_add_flyout.tsx | 34 ++++++-- .../connector_add_modal.tsx | 25 +++++- .../connector_edit_flyout.tsx | 32 +++++--- .../connector_reducer.test.ts | 14 +++- .../connector_reducer.ts | 80 ++++++++++++++----- .../triggers_actions_ui/public/types.ts | 19 ++--- 7 files changed, 183 insertions(+), 62 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx index 5fc28d5c629d4..2c74f3391f9c1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx @@ -17,9 +17,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ReducerAction } from './connector_reducer'; import { - ActionConnector, IErrorObject, ActionTypeRegistryContract, UserConfiguredActionConnector, @@ -28,8 +26,11 @@ import { import { hasSaveActionsCapability } from '../../lib/capabilities'; import { useKibana } from '../../../common/lib/kibana'; import { SectionLoading } from '../../components/section_loading'; +import { ConnectorReducerAction } from './connector_reducer'; -export function validateBaseProperties(actionObject: ActionConnector) { +export function validateBaseProperties( + actionObject: UserConfiguredActionConnector +) { const validationResult = { errors: {} }; const verrors = { name: new Array(), @@ -78,14 +79,14 @@ interface ActionConnectorProps< ConnectorSecrets = Record > { connector: UserConfiguredActionConnector; - dispatch: React.Dispatch; - actionTypeName: string; - serverError?: { - body: { message: string; error: string }; - }; + dispatch: React.Dispatch>; errors: IErrorObject; actionTypeRegistry: ActionTypeRegistryContract; consumer?: string; + actionTypeName?: string; + serverError?: { + body: { message: string; error: string }; + }; } export const ActionConnectorForm = ({ @@ -103,15 +104,31 @@ export const ActionConnectorForm = ({ } = useKibana().services; const canSave = hasSaveActionsCapability(capabilities); - const setActionProperty = (key: string, value: any) => { + const setActionProperty = < + Key extends keyof UserConfiguredActionConnector< + Record, + Record + > + >( + key: Key, + value: + | UserConfiguredActionConnector, Record>[Key] + | null + ) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }; - const setActionConfigProperty = (key: string, value: any) => { + const setActionConfigProperty = >( + key: Key, + value: Record[Key] + ) => { dispatch({ command: { type: 'setConfigProperty' }, payload: { key, value } }); }; - const setActionSecretsProperty = (key: string, value: any) => { + const setActionSecretsProperty = >( + key: Key, + value: Record[Key] + ) => { dispatch({ command: { type: 'setSecretsProperty' }, payload: { key, value } }); }; @@ -135,7 +152,7 @@ export const ActionConnectorForm = ({ id="xpack.triggersActionsUI.sections.actionConnectorForm.actions.actionConfigurationWarningDescriptionText" defaultMessage="To create this connector, you must configure at least one {actionType} account. {docLink}" values={{ - actionType: actionTypeName, + actionType: actionTypeName ?? connector.actionTypeId, docLink: ( = ({ const [hasActionsUpgradeableByTrial, setHasActionsUpgradeableByTrial] = useState(false); // hooks - const initialConnector = { + const initialConnector: InitialConnector, Record> = { actionTypeId: actionType?.id ?? '', config: {}, secrets: {}, - } as ActionConnector; - const [{ connector }, dispatch] = useReducer(connectorReducer, { connector: initialConnector }); - const setActionProperty = (key: string, value: any) => { + }; + + const reducer: ConnectorReducer< + Record, + Record + > = createConnectorReducer, Record>(); + const [{ connector }, dispatch] = useReducer(reducer, { + connector: initialConnector as UserConfiguredActionConnector< + Record, + Record + >, + }); + + const setActionProperty = ( + key: Key, + value: + | UserConfiguredActionConnector, Record>[Key] + | null + ) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }; + const setConnector = (value: any) => { dispatch({ command: { type: 'setConnector' }, payload: { key: 'connector', value } }); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx index 205066c4ecace..d48c649c8d41a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx @@ -18,11 +18,16 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { EuiOverlayMask } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ActionConnectorForm, getConnectorErrors } from './action_connector_form'; -import { connectorReducer } from './connector_reducer'; +import { createConnectorReducer, InitialConnector, ConnectorReducer } from './connector_reducer'; import { createActionConnector } from '../../lib/action_connector_api'; import './connector_add_modal.scss'; import { hasSaveActionsCapability } from '../../lib/capabilities'; -import { ActionType, ActionConnector, ActionTypeRegistryContract } from '../../../types'; +import { + ActionType, + ActionConnector, + ActionTypeRegistryContract, + UserConfiguredActionConnector, +} from '../../../types'; import { useKibana } from '../../../common/lib/kibana'; import { getConnectorWithInvalidatedFields } from '../../lib/value_validators'; @@ -47,7 +52,10 @@ export const ConnectorAddModal = ({ application: { capabilities }, } = useKibana().services; let hasErrors = false; - const initialConnector = useMemo( + const initialConnector: InitialConnector< + Record, + Record + > = useMemo( () => ({ actionTypeId: actionType.id, config: {}, @@ -58,7 +66,16 @@ export const ConnectorAddModal = ({ const [isSaving, setIsSaving] = useState(false); const canSave = hasSaveActionsCapability(capabilities); - const [{ connector }, dispatch] = useReducer(connectorReducer, { connector: initialConnector }); + const reducer: ConnectorReducer< + Record, + Record + > = createConnectorReducer, Record>(); + const [{ connector }, dispatch] = useReducer(reducer, { + connector: initialConnector as UserConfiguredActionConnector< + Record, + Record + >, + }); const setConnector = (value: any) => { dispatch({ command: { type: 'setConnector' }, payload: { key: 'connector', value } }); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index 75a8e9bbe8270..f4fec873ecc94 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -26,8 +26,12 @@ import { i18n } from '@kbn/i18n'; import { Option, none, some } from 'fp-ts/lib/Option'; import { ActionConnectorForm, getConnectorErrors } from './action_connector_form'; import { TestConnectorForm } from './test_connector_form'; -import { ActionConnector, ActionTypeRegistryContract } from '../../../types'; -import { connectorReducer } from './connector_reducer'; +import { + ActionConnector, + ActionTypeRegistryContract, + UserConfiguredActionConnector, +} from '../../../types'; +import { ConnectorReducer, createConnectorReducer } from './connector_reducer'; import { updateActionConnector, executeAction } from '../../lib/action_connector_api'; import { hasSaveActionsCapability } from '../../lib/capabilities'; import { @@ -66,17 +70,29 @@ export const ConnectorEditFlyout = ({ docLinks, application: { capabilities }, } = useKibana().services; + const getConnectorWithoutSecrets = () => ({ + ...(initialConnector as UserConfiguredActionConnector< + Record, + Record + >), + secrets: {}, + }); const canSave = hasSaveActionsCapability(capabilities); - const [{ connector }, dispatch] = useReducer(connectorReducer, { - connector: { ...initialConnector, secrets: {} }, + const reducer: ConnectorReducer< + Record, + Record + > = createConnectorReducer, Record>(); + const [{ connector }, dispatch] = useReducer(reducer, { + connector: getConnectorWithoutSecrets(), }); const [isSaving, setIsSaving] = useState(false); const [selectedTab, setTab] = useState(tab); const [hasChanges, setHasChanges] = useState(false); - const setConnector = (key: string, value: any) => { - dispatch({ command: { type: 'setConnector' }, payload: { key, value } }); + + const setConnector = (value: any) => { + dispatch({ command: { type: 'setConnector' }, payload: { key: 'connector', value } }); }; const [testExecutionActionParams, setTestExecutionActionParams] = useState< @@ -101,7 +117,7 @@ export const ConnectorEditFlyout = ({ ); const closeFlyout = useCallback(() => { - setConnector('connector', { ...initialConnector, secrets: {} }); + setConnector(getConnectorWithoutSecrets()); setHasChanges(false); setTestExecutionResult(none); onClose(); @@ -220,7 +236,6 @@ export const ConnectorEditFlyout = ({ const onSaveClicked = async (closeAfterSave: boolean = true) => { if (hasErrors) { setConnector( - 'connector', getConnectorWithInvalidatedFields( connector, configErrors, @@ -282,7 +297,6 @@ export const ConnectorEditFlyout = ({ { setHasChanges(true); // if the user changes the connector, "forget" the last execution diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts index e469a50108912..b95203deaa129 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts @@ -3,11 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { connectorReducer } from './connector_reducer'; -import { ActionConnector } from '../../../types'; +import { UserConfiguredActionConnector } from '../../../types'; +import { createConnectorReducer, ConnectorReducer } from './connector_reducer'; describe('connector reducer', () => { - let initialConnector: ActionConnector; + let initialConnector: UserConfiguredActionConnector< + Record, + Record + >; beforeAll(() => { initialConnector = { secrets: {}, @@ -20,6 +23,11 @@ describe('connector reducer', () => { }; }); + const connectorReducer: ConnectorReducer< + Record, + Record + > = createConnectorReducer, Record>(); + test('if property name was changed', () => { const updatedConnector = connectorReducer( { connector: initialConnector }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.ts index 4d094cd869420..a82a2041b4903 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.ts @@ -4,30 +4,69 @@ * you may not use this file except in compliance with the Elastic License. */ import { isEqual } from 'lodash'; +import { Reducer } from 'react'; +import { UserConfiguredActionConnector } from '../../../types'; -interface CommandType { - type: 'setConnector' | 'setProperty' | 'setConfigProperty' | 'setSecretsProperty'; +export type InitialConnector = Partial< + UserConfiguredActionConnector +> & + Pick, 'actionTypeId' | 'config' | 'secrets'>; + +interface CommandType< + T extends 'setConnector' | 'setProperty' | 'setConfigProperty' | 'setSecretsProperty' +> { + type: T; } -export interface ActionState { - connector: any; +interface Payload { + key: Keys; + value: Value; } -export interface ReducerAction { - command: CommandType; - payload: { - key: string; - value: any; - }; +interface TPayload { + key: Key; + value: T[Key] | null; } -export const connectorReducer = (state: ActionState, action: ReducerAction) => { - const { command, payload } = action; +export type ConnectorReducerAction = + | { + command: CommandType<'setConnector'>; + payload: Payload<'connector', InitialConnector>; + } + | { + command: CommandType<'setProperty'>; + payload: TPayload< + UserConfiguredActionConnector, + keyof UserConfiguredActionConnector + >; + } + | { + command: CommandType<'setConfigProperty'>; + payload: TPayload; + } + | { + command: CommandType<'setSecretsProperty'>; + payload: TPayload; + }; + +export type ConnectorReducer = Reducer< + { connector: UserConfiguredActionConnector }, + ConnectorReducerAction +>; + +export const createConnectorReducer = () => < + ConnectorPhase extends + | InitialConnector + | UserConfiguredActionConnector +>( + state: { connector: ConnectorPhase }, + action: ConnectorReducerAction +) => { const { connector } = state; - switch (command.type) { + switch (action.command.type) { case 'setConnector': { - const { key, value } = payload; + const { key, value } = action.payload as Payload<'connector', ConnectorPhase>; if (key === 'connector') { return { ...state, @@ -38,7 +77,10 @@ export const connectorReducer = (state: ActionState, action: ReducerAction) => { } } case 'setProperty': { - const { key, value } = payload; + const { key, value } = action.payload as TPayload< + UserConfiguredActionConnector, + keyof UserConfiguredActionConnector + >; if (isEqual(connector[key], value)) { return state; } else { @@ -52,7 +94,7 @@ export const connectorReducer = (state: ActionState, action: ReducerAction) => { } } case 'setConfigProperty': { - const { key, value } = payload; + const { key, value } = action.payload as TPayload; if (isEqual(connector.config[key], value)) { return state; } else { @@ -61,7 +103,7 @@ export const connectorReducer = (state: ActionState, action: ReducerAction) => { connector: { ...connector, config: { - ...connector.config, + ...(connector.config as Config), [key]: value, }, }, @@ -69,7 +111,7 @@ export const connectorReducer = (state: ActionState, action: ReducerAction) => { } } case 'setSecretsProperty': { - const { key, value } = payload; + const { key, value } = action.payload as TPayload; if (isEqual(connector.secrets[key], value)) { return state; } else { @@ -78,7 +120,7 @@ export const connectorReducer = (state: ActionState, action: ReducerAction) => { connector: { ...connector, secrets: { - ...connector.secrets, + ...(connector.secrets as Secrets), [key]: value, }, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 0cf859ad64173..f4dc4c3d4ef26 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -43,15 +43,16 @@ export { ActionType }; export type ActionTypeIndex = Record; export type AlertTypeIndex = Map; -export type ActionTypeRegistryContract = PublicMethodsOf< - TypeRegistry> ->; +export type ActionTypeRegistryContract< + ActionConnector = unknown, + ActionParams = unknown +> = PublicMethodsOf>>; export type AlertTypeRegistryContract = PublicMethodsOf>; export interface ActionConnectorFieldsProps { action: TActionConnector; - editActionConfig: (property: string, value: any) => void; - editActionSecrets: (property: string, value: any) => void; + editActionConfig: (property: string, value: unknown) => void; + editActionSecrets: (property: string, value: unknown) => void; errors: IErrorObject; readOnly: boolean; consumer?: string; @@ -128,13 +129,13 @@ export type UserConfiguredActionConnector = ActionConnectorProp isPreconfigured: false; }; -export type ActionConnector, Secrets = Record> = +export type ActionConnector, Secrets = Record> = | PreConfiguredActionConnector | UserConfiguredActionConnector; export type ActionConnectorWithoutId< - Config = Record, - Secrets = Record + Config = Record, + Secrets = Record > = Omit, 'id'>; export type ActionConnectorTableItem = ActionConnector & { @@ -186,7 +187,7 @@ export interface AlertTableItem extends Alert { export interface AlertTypeParamsExpressionProps< Params extends AlertTypeParams = AlertTypeParams, - MetaData = Record, + MetaData = Record, ActionGroupIds extends string = string > { alertParams: Params; From fdfe6559b070b6461467d7ff6d336dcbf4ff691b Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Thu, 7 Jan 2021 15:45:46 -0500 Subject: [PATCH 18/24] [Canvas] Register addons async (#86977) * Make canvas registries accept async functions * Remove some comments * Remove comment * Update x-pack/plugins/canvas/public/registries.ts Co-authored-by: Clint Andrew Hall * Update x-pack/plugins/canvas/public/registries.ts Co-authored-by: Clint Andrew Hall * Add ts error types Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Clint Andrew Hall --- .../canvas/canvas_plugin_src/canvas_addons.ts | 16 ++++++ .../canvas/canvas_plugin_src/plugin.ts | 56 +++++++++++++------ x-pack/plugins/canvas/public/application.tsx | 2 +- x-pack/plugins/canvas/public/plugin.tsx | 14 +++-- x-pack/plugins/canvas/public/plugin_api.ts | 40 ++++++++----- x-pack/plugins/canvas/public/registries.ts | 20 ++++++- 6 files changed, 108 insertions(+), 40 deletions(-) create mode 100644 x-pack/plugins/canvas/canvas_plugin_src/canvas_addons.ts diff --git a/x-pack/plugins/canvas/canvas_plugin_src/canvas_addons.ts b/x-pack/plugins/canvas/canvas_plugin_src/canvas_addons.ts new file mode 100644 index 0000000000000..fe5e1d7e5f983 --- /dev/null +++ b/x-pack/plugins/canvas/canvas_plugin_src/canvas_addons.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ + +// @ts-expect-error Untyped Local +export * from './uis/datasources'; +export * from './elements'; +// @ts-expect-error Untyped Local +export * from './uis/models'; +export * from './uis/views'; +export * from './uis/arguments'; +export * from './uis/tags'; +// @ts-expect-error Untyped Local +export * from './uis/transforms'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts index 7ecebd6d0677a..e650cd5037118 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts @@ -14,17 +14,6 @@ import { Start as InspectorStart } from '../../../../src/plugins/inspector/publi import { functions } from './functions/browser'; import { typeFunctions } from './expression_types'; import { renderFunctions, renderFunctionFactories } from './renderers'; -import { initializeElements } from './elements'; -// @ts-expect-error untyped local -import { transformSpecs } from './uis/transforms'; -// @ts-expect-error untyped local -import { datasourceSpecs } from './uis/datasources'; -// @ts-expect-error untyped local -import { modelSpecs } from './uis/models'; -import { initializeViews } from './uis/views'; -import { initializeArgs } from './uis/arguments'; -import { tagSpecs } from './uis/tags'; - interface SetupDeps { canvas: CanvasSetup; } @@ -53,13 +42,44 @@ export class CanvasSrcPlugin implements Plugin ); }); - plugins.canvas.addElements(initializeElements(core, plugins)); - plugins.canvas.addDatasourceUIs(datasourceSpecs); - plugins.canvas.addModelUIs(modelSpecs); - plugins.canvas.addViewUIs(initializeViews(core, plugins)); - plugins.canvas.addArgumentUIs(initializeArgs(core, plugins)); - plugins.canvas.addTagUIs(tagSpecs); - plugins.canvas.addTransformUIs(transformSpecs); + plugins.canvas.addDatasourceUIs(async () => { + // @ts-expect-error + const { datasourceSpecs } = await import('./canvas_addons'); + return datasourceSpecs; + }); + + plugins.canvas.addElements(async () => { + const { initializeElements } = await import('./canvas_addons'); + return initializeElements(core, plugins); + }); + + plugins.canvas.addModelUIs(async () => { + // @ts-expect-error Untyped local + const { modelSpecs } = await import('./canvas_addons'); + return modelSpecs; + }); + + plugins.canvas.addViewUIs(async () => { + const { initializeViews } = await import('./canvas_addons'); + + return initializeViews(core, plugins); + }); + + plugins.canvas.addArgumentUIs(async () => { + const { initializeArgs } = await import('./canvas_addons'); + return initializeArgs(core, plugins); + }); + + plugins.canvas.addTagUIs(async () => { + const { tagSpecs } = await import('./canvas_addons'); + return tagSpecs; + }); + + plugins.canvas.addTransformUIs(async () => { + // @ts-expect-error Untyped local + const { transformSpecs } = await import('./canvas_addons'); + return transformSpecs; + }); } public start(core: CoreStart, plugins: StartDeps) {} diff --git a/x-pack/plugins/canvas/public/application.tsx b/x-pack/plugins/canvas/public/application.tsx index 7d65a99b1dd45..fc02df3740cdb 100644 --- a/x-pack/plugins/canvas/public/application.tsx +++ b/x-pack/plugins/canvas/public/application.tsx @@ -103,7 +103,7 @@ export const initializeCanvas = async ( // Init Registries initRegistries(); - populateRegistries(registries); + await populateRegistries(registries); // Set Badge coreStart.chrome.setBadge( diff --git a/x-pack/plugins/canvas/public/plugin.tsx b/x-pack/plugins/canvas/public/plugin.tsx index d18f1b8d24489..3c6c0d68da3db 100644 --- a/x-pack/plugins/canvas/public/plugin.tsx +++ b/x-pack/plugins/canvas/public/plugin.tsx @@ -26,9 +26,6 @@ import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; import { BfetchPublicSetup } from '../../../../src/plugins/bfetch/public'; -// @ts-expect-error untyped local -import { argTypeSpecs } from './expression_types/arg_types'; -import { transitions } from './transitions'; import { getPluginApi, CanvasApi } from './plugin_api'; import { CanvasSrcPlugin } from '../canvas_plugin_src/plugin'; export { CoreStart, CoreSetup }; @@ -123,8 +120,15 @@ export class CanvasPlugin plugins.home.featureCatalogue.register(featureCatalogueEntry); } - canvasApi.addArgumentUIs(argTypeSpecs); - canvasApi.addTransitions(transitions); + canvasApi.addArgumentUIs(async () => { + // @ts-expect-error + const { argTypeSpecs } = await import('./expression_types/arg_types'); + return argTypeSpecs; + }); + canvasApi.addTransitions(async () => { + const { transitions } = await import('./transitions'); + return transitions; + }); return { ...canvasApi, diff --git a/x-pack/plugins/canvas/public/plugin_api.ts b/x-pack/plugins/canvas/public/plugin_api.ts index 62e82df4b0d04..be267bb91a909 100644 --- a/x-pack/plugins/canvas/public/plugin_api.ts +++ b/x-pack/plugins/canvas/public/plugin_api.ts @@ -12,24 +12,26 @@ import { import { ElementFactory } from '../types'; import { ExpressionsSetup } from '../../../../src/plugins/expressions/public'; -type AddToRegistry = (add: T[]) => void; +type SpecPromiseFn = () => Promise; +type AddToRegistry = (add: T[] | SpecPromiseFn) => void; +type AddSpecsToRegistry = (add: T[]) => void; export interface CanvasApi { addArgumentUIs: AddToRegistry; addDatasourceUIs: AddToRegistry; addElements: AddToRegistry; - addFunctions: AddToRegistry<() => AnyExpressionFunctionDefinition>; + addFunctions: AddSpecsToRegistry<() => AnyExpressionFunctionDefinition>; addModelUIs: AddToRegistry; - addRenderers: AddToRegistry; + addRenderers: AddSpecsToRegistry; addTagUIs: AddToRegistry; addTransformUIs: AddToRegistry; addTransitions: AddToRegistry; - addTypes: AddToRegistry<() => AnyExpressionTypeDefinition>; + addTypes: AddSpecsToRegistry<() => AnyExpressionTypeDefinition>; addViewUIs: AddToRegistry; } -export interface SetupRegistries { - elements: ElementFactory[]; +export interface SetupRegistries extends Record { + elements: Array>; transformUIs: any[]; datasourceUIs: any[]; modelUIs: any[]; @@ -53,6 +55,16 @@ export function getPluginApi( transitions: [], }; + const addToRegistry = (registry: Array>) => { + return (entries: T[] | SpecPromiseFn) => { + if (Array.isArray(entries)) { + registry.push(...entries); + } else { + registry.push(entries); + } + }; + }; + const api: CanvasApi = { // Functions, types and renderers are registered directly to expression plugin addFunctions: (fns) => { @@ -75,14 +87,14 @@ export function getPluginApi( }, // All these others are local to canvas, and they will only register on start - addElements: (elements) => registries.elements.push(...elements), - addTransformUIs: (transforms) => registries.transformUIs.push(...transforms), - addDatasourceUIs: (datasources) => registries.datasourceUIs.push(...datasources), - addModelUIs: (models) => registries.modelUIs.push(...models), - addViewUIs: (views) => registries.viewUIs.push(...views), - addArgumentUIs: (args) => registries.argumentUIs.push(...args), - addTagUIs: (tags) => registries.tagUIs.push(...tags), - addTransitions: (transitions) => registries.transitions.push(...transitions), + addElements: addToRegistry(registries.elements), + addTransformUIs: addToRegistry(registries.transformUIs), + addDatasourceUIs: addToRegistry(registries.datasourceUIs), + addModelUIs: addToRegistry(registries.modelUIs), + addViewUIs: addToRegistry(registries.viewUIs), + addArgumentUIs: addToRegistry(registries.argumentUIs), + addTagUIs: addToRegistry(registries.tagUIs), + addTransitions: addToRegistry(registries.transitions), }; return { api, registries }; diff --git a/x-pack/plugins/canvas/public/registries.ts b/x-pack/plugins/canvas/public/registries.ts index b2881fc0b7799..5f87beb207b8c 100644 --- a/x-pack/plugins/canvas/public/registries.ts +++ b/x-pack/plugins/canvas/public/registries.ts @@ -40,8 +40,24 @@ export function initRegistries() { }); } -export function populateRegistries(setupRegistries: SetupRegistries) { - register(registries, setupRegistries); +export async function populateRegistries(setupRegistries: SetupRegistries) { + // Our setup registries could contain definitions or a function that would + // return a promise of definitions. + // We need to call all the fns and then wait for all of the promises to be resolved + const resolvedRegistries: Record = {}; + const promises = Object.entries(setupRegistries).map(async ([key, specs]) => { + const resolved = await ( + await Promise.all(specs.map((fn) => (typeof fn === 'function' ? fn() : fn))) + ).flat(); + + resolvedRegistries[key] = resolved; + }); + + // Now, wait for all of the promise registry promises to resolve and our resolved registry will be ready + // and we can proceeed + await Promise.all(promises); + + register(registries, resolvedRegistries); } export function destroyRegistries() { From d03b20a38cd328ae5445235e4e30944b68b6fd5f Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Thu, 7 Jan 2021 15:48:35 -0500 Subject: [PATCH 19/24] [Security Solution][Detections] - Exceptions table endpoint list bug fix (disable delete of endpoint list) (#87694) ## Summary Addresses issue 87112 . With the addition of the exceptions table, users were able to now delete exception lists from the UI. However, the "endpoint_list" is particular in some ways and should not be so easily deleted from the UI. Moved to disable the delete button for "endpoint_list exception list. --- .../rules/all/exceptions/columns.tsx | 4 +- .../all/exceptions/exceptions_table.test.tsx | 94 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.test.tsx diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx index f91efcb3b19b0..022fd5cdb04ea 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx @@ -34,7 +34,7 @@ export const getAllExceptionListsColumns = ( width: '15%', render: (value: ExceptionListInfo['list_id']) => ( - <>{value} +

{value}

), }, @@ -120,6 +120,8 @@ export const getAllExceptionListsColumns = ( onClick={onDelete({ id, listId, namespaceType })} aria-label="Delete exception list" iconType="trash" + isDisabled={listId === 'endpoint_list'} + data-test-subj="exceptionsTableDeleteButton" /> ), }, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.test.tsx new file mode 100644 index 0000000000000..d5f3af7cf5031 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.test.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { mount } from 'enzyme'; + +import { TestProviders } from '../../../../../../common/mock'; +import { mockHistory } from '../../../../../../common/utils/route/index.test'; +import { getExceptionListSchemaMock } from '../../../../../../../../lists/common/schemas/response/exception_list_schema.mock'; + +import { ExceptionListsTable } from './exceptions_table'; +import { useKibana } from '../../../../../../common/lib/kibana'; +import { useApi, useExceptionLists } from '../../../../../../shared_imports'; +import { useAllExceptionLists } from './use_all_exception_lists'; + +jest.mock('../../../../../../common/lib/kibana'); +jest.mock('./use_all_exception_lists'); +jest.mock('../../../../../../shared_imports'); + +describe('ExceptionListsTable', () => { + const exceptionList1 = getExceptionListSchemaMock(); + const exceptionList2 = { ...getExceptionListSchemaMock(), list_id: 'not_endpoint_list', id: '2' }; + + beforeEach(() => { + (useKibana as jest.Mock).mockReturnValue({ + services: { + http: {}, + notifications: { + toasts: { + addError: jest.fn(), + }, + }, + }, + }); + + (useApi as jest.Mock).mockReturnValue({ + deleteExceptionList: jest.fn(), + exportExceptionList: jest.fn(), + }); + + (useExceptionLists as jest.Mock).mockReturnValue([ + false, + [exceptionList1, exceptionList2], + { + page: 1, + perPage: 20, + total: 2, + }, + jest.fn(), + ]); + + (useAllExceptionLists as jest.Mock).mockReturnValue([ + false, + [ + { ...exceptionList1, rules: [] }, + { ...exceptionList2, rules: [] }, + ], + { + not_endpoint_list: exceptionList2, + endpoint_list: exceptionList1, + }, + ]); + }); + + it('renders delete option disabled if list is "endpoint_list"', async () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="exceptionsTableListId"]').at(0).text()).toEqual( + 'endpoint_list' + ); + expect( + wrapper.find('[data-test-subj="exceptionsTableDeleteButton"] button').at(0).prop('disabled') + ).toBeTruthy(); + + expect(wrapper.find('[data-test-subj="exceptionsTableListId"]').at(1).text()).toEqual( + 'not_endpoint_list' + ); + expect( + wrapper.find('[data-test-subj="exceptionsTableDeleteButton"] button').at(1).prop('disabled') + ).toBeFalsy(); + }); +}); From 6d6a8057340780373418a21b73846f3bdef3dfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Thu, 7 Jan 2021 21:42:12 +0000 Subject: [PATCH 20/24] [Index templates] Keep configuration of data stream when editing (#87666) --- .../template_edit.test.tsx | 47 ++++++++++++++++++- .../common/types/templates.ts | 6 ++- .../template_form/steps/step_logistics.tsx | 23 +++++---- .../template_form/template_form_schemas.tsx | 21 +-------- .../routes/api/templates/validate_schemas.ts | 9 +++- .../test/fixtures/template.ts | 4 +- 6 files changed, 77 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx index 6ba2454025beb..2897551a209b2 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx @@ -71,6 +71,10 @@ describe('', () => { const templateToEdit = fixtures.getTemplate({ name: 'index_template_without_mappings', indexPatterns: ['indexPattern1'], + dataStream: { + hidden: true, + anyUnknownKey: 'should_be_kept', + }, }); beforeAll(() => { @@ -85,7 +89,7 @@ describe('', () => { testBed.component.update(); }); - it('allows you to add mappings', async () => { + test('allows you to add mappings', async () => { const { actions, find } = testBed; // Logistics await actions.completeStepOne(); @@ -98,6 +102,47 @@ describe('', () => { expect(find('fieldsListItem').length).toBe(1); }); + + test('should keep data stream configuration', async () => { + const { actions } = testBed; + // Logistics + await actions.completeStepOne({ + name: 'test', + indexPatterns: ['myPattern*'], + version: 1, + }); + // Component templates + await actions.completeStepTwo(); + // Index settings + await actions.completeStepThree(); + // Mappings + await actions.completeStepFour(); + // Aliases + await actions.completeStepFive(); + + await act(async () => { + actions.clickNextButton(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + const expected = { + name: 'test', + indexPatterns: ['myPattern*'], + dataStream: { + hidden: true, + anyUnknownKey: 'should_be_kept', + }, + version: 1, + _kbnMeta: { + type: 'default', + isLegacy: false, + hasDatastream: true, + }, + }; + + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); + }); }); describe('with mappings', () => { diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts index d1b51fe5b89bf..7b442b9dd2935 100644 --- a/x-pack/plugins/index_management/common/types/templates.ts +++ b/x-pack/plugins/index_management/common/types/templates.ts @@ -46,7 +46,11 @@ export interface TemplateDeserialized { name: string; }; _meta?: { [key: string]: any }; // Composable template only - dataStream?: {}; // Composable template only + // Composable template only + dataStream?: { + hidden?: boolean; + [key: string]: any; + }; _kbnMeta: { type: TemplateType; hasDatastream: boolean; diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx index 89e857eec0bb3..bbc3656195470 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx @@ -55,7 +55,7 @@ function getFieldsMeta(esDocsBase: string) { ), testSubject: 'indexPatternsField', }, - dataStream: { + createDataStream: { title: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.dataStreamTitle', { defaultMessage: 'Data stream', }), @@ -119,6 +119,7 @@ interface LogisticsForm { interface LogisticsFormInternal extends LogisticsForm { addMeta: boolean; + doCreateDataStream: boolean; } interface Props { @@ -132,12 +133,16 @@ function formDeserializer(formData: LogisticsForm): LogisticsFormInternal { return { ...formData, addMeta: Boolean(formData._meta && Object.keys(formData._meta).length), + doCreateDataStream: Boolean(formData.dataStream), }; } -function formSerializer(formData: LogisticsFormInternal): LogisticsForm { - const { addMeta, ...rest } = formData; - return rest; +function getformSerializer(initialTemplateData: LogisticsForm = {}) { + return (formData: LogisticsFormInternal): LogisticsForm => { + const { addMeta, doCreateDataStream, ...rest } = formData; + const dataStream = doCreateDataStream ? initialTemplateData.dataStream ?? {} : undefined; + return { ...rest, dataStream }; + }; } export const StepLogistics: React.FunctionComponent = React.memo( @@ -146,7 +151,7 @@ export const StepLogistics: React.FunctionComponent = React.memo( schema: schemas.logistics, defaultValue, options: { stripEmptyFields: false }, - serializer: formSerializer, + serializer: getformSerializer(defaultValue), deserializer: formDeserializer, }); const { @@ -178,7 +183,7 @@ export const StepLogistics: React.FunctionComponent = React.memo( }); }, [onChange, isFormValid, validate, getFormData]); - const { name, indexPatterns, dataStream, order, priority, version } = getFieldsMeta( + const { name, indexPatterns, createDataStream, order, priority, version } = getFieldsMeta( documentationService.getEsDocsBase() ); @@ -245,10 +250,10 @@ export const StepLogistics: React.FunctionComponent = React.memo( {/* Create data stream */} {isLegacy !== true && ( - + )} diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx index c85126f08685e..2bc146c118ba2 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx @@ -129,31 +129,12 @@ export const schemas: Record = { }, ], }, - dataStream: { + doCreateDataStream: { type: FIELD_TYPES.TOGGLE, label: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.datastreamLabel', { defaultMessage: 'Create data stream', }), defaultValue: false, - serializer: (value) => { - if (value === true) { - // For now, ES expects an empty object when defining a data stream - // https://github.com/elastic/elasticsearch/pull/59317 - return {}; - } - }, - deserializer: (value) => { - if (typeof value === 'boolean') { - return value; - } - - /** - * For now, it is enough to have a "data_stream" declared on the index template - * to assume that the template creates a data stream. In the future, this condition - * might change - */ - return value !== undefined; - }, }, order: { type: FIELD_TYPES.NUMBER, diff --git a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts index 18c74716a35b6..3dab4113e6965 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts @@ -20,7 +20,14 @@ export const templateSchema = schema.object({ }) ), composedOf: schema.maybe(schema.arrayOf(schema.string())), - dataStream: schema.maybe(schema.object({}, { unknowns: 'allow' })), + dataStream: schema.maybe( + schema.object( + { + hidden: schema.maybe(schema.boolean()), + }, + { unknowns: 'allow' } + ) + ), _meta: schema.maybe(schema.object({}, { unknowns: 'allow' })), ilmPolicy: schema.maybe( schema.object({ diff --git a/x-pack/plugins/index_management/test/fixtures/template.ts b/x-pack/plugins/index_management/test/fixtures/template.ts index 016100faea601..90f556794a5d9 100644 --- a/x-pack/plugins/index_management/test/fixtures/template.ts +++ b/x-pack/plugins/index_management/test/fixtures/template.ts @@ -53,6 +53,7 @@ export const getTemplate = ({ order = getRandomNumber(), indexPatterns = [], template: { settings, aliases, mappings } = {}, + dataStream, hasDatastream = false, isLegacy = false, type = 'default', @@ -73,12 +74,13 @@ export const getTemplate = ({ mappings, settings, }, + dataStream, hasSettings: objHasProperties(settings), hasMappings: objHasProperties(mappings), hasAliases: objHasProperties(aliases), _kbnMeta: { type, - hasDatastream, + hasDatastream: dataStream !== undefined ? true : hasDatastream, isLegacy, }, }; From 354a79a28004e2516266aa642c573969cec2a999 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 7 Jan 2021 21:44:26 +0000 Subject: [PATCH 21/24] chore(NA): move watcher plugin tests out of __tests__ folder (#87599) * chore(NA): move watcher plugin tests out of __tests__ folder * chore(NA): renaming client_integration into tests_client_integration * chore(NA): rename test helper config file Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../execute_details.ts | 0 .../{test/fixtures => __fixtures__}/index.ts | 0 .../{test/fixtures => __fixtures__}/watch.ts | 0 .../watch_history.ts | 0 ...action_type.js => get_action_type.test.js} | 19 ++-- .../get_moment.js => get_moment.test.js} | 7 +- ...croll.js => fetch_all_from_scroll.test.js} | 65 ++++++------- .../license_pre_routing_factory.test.js | 9 +- ...action_status.js => action_status.test.js} | 95 +++++++++---------- ...ute_details.js => execute_details.test.js} | 21 ++-- .../{__tests__/fields.js => fields.test.js} | 21 ++-- .../settings.js => settings.test.js} | 45 +++++---- ..._data.js => format_visualize_data.test.js} | 13 ++- ...ory_item.js => watch_history_item.test.js} | 33 ++++--- .../watch_status.js => watch_status.test.js} | 82 ++++++++-------- .../helpers/app_context.mock.tsx | 6 +- .../helpers/body_response.ts | 0 .../helpers/http_requests.ts | 2 +- .../helpers/index.ts | 0 .../helpers/jest_constants.ts} | 2 +- .../helpers/setup_environment.ts | 2 +- .../helpers/watch_create_json.helpers.ts | 6 +- .../helpers/watch_create_threshold.helpers.ts | 6 +- .../helpers/watch_edit.helpers.ts | 8 +- .../helpers/watch_list.helpers.ts | 4 +- .../helpers/watch_status.helpers.ts | 6 +- .../watch_create_json.test.ts | 6 +- .../watch_create_threshold.test.tsx | 8 +- .../watch_edit.test.ts | 10 +- .../watch_list.test.ts | 4 +- .../watch_status.test.ts | 8 +- 31 files changed, 240 insertions(+), 248 deletions(-) rename x-pack/plugins/watcher/{test/fixtures => __fixtures__}/execute_details.ts (100%) rename x-pack/plugins/watcher/{test/fixtures => __fixtures__}/index.ts (100%) rename x-pack/plugins/watcher/{test/fixtures => __fixtures__}/watch.ts (100%) rename x-pack/plugins/watcher/{test/fixtures => __fixtures__}/watch_history.ts (100%) rename x-pack/plugins/watcher/common/lib/get_action_type/{__tests__/get_action_type.js => get_action_type.test.js} (79%) rename x-pack/plugins/watcher/common/lib/get_moment/{__tests__/get_moment.js => get_moment.test.js} (81%) rename x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/{__tests__/fetch_all_from_scroll.js => fetch_all_from_scroll.test.js} (59%) rename x-pack/plugins/watcher/server/lib/license_pre_routing_factory/{__tests__ => }/license_pre_routing_factory.test.js (81%) rename x-pack/plugins/watcher/server/models/action_status/{__tests__/action_status.js => action_status.test.js} (79%) rename x-pack/plugins/watcher/server/models/execute_details/{__tests__/execute_details.js => execute_details.test.js} (85%) rename x-pack/plugins/watcher/server/models/fields/{__tests__/fields.js => fields.test.js} (85%) rename x-pack/plugins/watcher/server/models/settings/{__tests__/settings.js => settings.test.js} (63%) rename x-pack/plugins/watcher/server/models/watch/threshold_watch/{__tests__/format_visualize_data.js => format_visualize_data.test.js} (96%) rename x-pack/plugins/watcher/server/models/watch_history_item/{__tests__/watch_history_item.js => watch_history_item.test.js} (72%) rename x-pack/plugins/watcher/server/models/watch_status/{__tests__/watch_status.js => watch_status.test.js} (77%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/app_context.mock.tsx (89%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/body_response.ts (100%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/http_requests.ts (99%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/index.ts (100%) rename x-pack/plugins/watcher/{__jest__/client_integration/helpers/constants.ts => tests_client_integration/helpers/jest_constants.ts} (87%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/setup_environment.ts (91%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/watch_create_json.helpers.ts (89%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/watch_create_threshold.helpers.ts (92%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/watch_edit.helpers.ts (83%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/watch_list.helpers.ts (95%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/helpers/watch_status.helpers.ts (95%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/watch_create_json.test.ts (97%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/watch_create_threshold.test.tsx (99%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/watch_edit.test.ts (95%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/watch_list.test.ts (98%) rename x-pack/plugins/watcher/{__jest__/client_integration => tests_client_integration}/watch_status.test.ts (97%) diff --git a/x-pack/plugins/watcher/test/fixtures/execute_details.ts b/x-pack/plugins/watcher/__fixtures__/execute_details.ts similarity index 100% rename from x-pack/plugins/watcher/test/fixtures/execute_details.ts rename to x-pack/plugins/watcher/__fixtures__/execute_details.ts diff --git a/x-pack/plugins/watcher/test/fixtures/index.ts b/x-pack/plugins/watcher/__fixtures__/index.ts similarity index 100% rename from x-pack/plugins/watcher/test/fixtures/index.ts rename to x-pack/plugins/watcher/__fixtures__/index.ts diff --git a/x-pack/plugins/watcher/test/fixtures/watch.ts b/x-pack/plugins/watcher/__fixtures__/watch.ts similarity index 100% rename from x-pack/plugins/watcher/test/fixtures/watch.ts rename to x-pack/plugins/watcher/__fixtures__/watch.ts diff --git a/x-pack/plugins/watcher/test/fixtures/watch_history.ts b/x-pack/plugins/watcher/__fixtures__/watch_history.ts similarity index 100% rename from x-pack/plugins/watcher/test/fixtures/watch_history.ts rename to x-pack/plugins/watcher/__fixtures__/watch_history.ts diff --git a/x-pack/plugins/watcher/common/lib/get_action_type/__tests__/get_action_type.js b/x-pack/plugins/watcher/common/lib/get_action_type/get_action_type.test.js similarity index 79% rename from x-pack/plugins/watcher/common/lib/get_action_type/__tests__/get_action_type.js rename to x-pack/plugins/watcher/common/lib/get_action_type/get_action_type.test.js index 1410488ee6413..9fed91fe5f3b7 100644 --- a/x-pack/plugins/watcher/common/lib/get_action_type/__tests__/get_action_type.js +++ b/x-pack/plugins/watcher/common/lib/get_action_type/get_action_type.test.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { getActionType } from '../get_action_type'; -import { ACTION_TYPES } from '../../../constants'; +import { getActionType } from './get_action_type'; +import { ACTION_TYPES } from '../../constants'; describe('get_action_type', () => { describe('getActionType', () => { @@ -18,7 +17,7 @@ describe('get_action_type', () => { }; const type = getActionType(actionJson); - expect(type).to.be(ACTION_TYPES.EMAIL); + expect(type).toBe(ACTION_TYPES.EMAIL); }); it(`correctly calculates ACTION_TYPES.WEBHOOK`, () => { @@ -29,7 +28,7 @@ describe('get_action_type', () => { }; const type = getActionType(actionJson); - expect(type).to.be(ACTION_TYPES.WEBHOOK); + expect(type).toBe(ACTION_TYPES.WEBHOOK); }); it(`correctly calculates ACTION_TYPES.INDEX`, () => { @@ -40,7 +39,7 @@ describe('get_action_type', () => { }; const type = getActionType(actionJson); - expect(type).to.be(ACTION_TYPES.INDEX); + expect(type).toBe(ACTION_TYPES.INDEX); }); it(`correctly calculates ACTION_TYPES.LOGGING`, () => { @@ -51,7 +50,7 @@ describe('get_action_type', () => { }; const type = getActionType(actionJson); - expect(type).to.be(ACTION_TYPES.LOGGING); + expect(type).toBe(ACTION_TYPES.LOGGING); }); it(`correctly calculates ACTION_TYPES.SLACK`, () => { @@ -62,7 +61,7 @@ describe('get_action_type', () => { }; const type = getActionType(actionJson); - expect(type).to.be(ACTION_TYPES.SLACK); + expect(type).toBe(ACTION_TYPES.SLACK); }); it(`correctly calculates ACTION_TYPES.PAGERDUTY`, () => { @@ -73,7 +72,7 @@ describe('get_action_type', () => { }; const type = getActionType(actionJson); - expect(type).to.be(ACTION_TYPES.PAGERDUTY); + expect(type).toBe(ACTION_TYPES.PAGERDUTY); }); it(`correctly calculates ACTION_TYPES.UNKNOWN`, () => { @@ -84,7 +83,7 @@ describe('get_action_type', () => { }; const type = getActionType(actionJson); - expect(type).to.be(ACTION_TYPES.UNKNOWN); + expect(type).toBe(ACTION_TYPES.UNKNOWN); }); }); }); diff --git a/x-pack/plugins/watcher/common/lib/get_moment/__tests__/get_moment.js b/x-pack/plugins/watcher/common/lib/get_moment/get_moment.test.js similarity index 81% rename from x-pack/plugins/watcher/common/lib/get_moment/__tests__/get_moment.js rename to x-pack/plugins/watcher/common/lib/get_moment/get_moment.test.js index 6e78f648b1863..489ede1c4b4dc 100644 --- a/x-pack/plugins/watcher/common/lib/get_moment/__tests__/get_moment.js +++ b/x-pack/plugins/watcher/common/lib/get_moment/get_moment.test.js @@ -4,15 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { getMoment } from '../get_moment'; +import { getMoment } from './get_moment'; describe('get_moment', () => { describe('getMoment', () => { it(`returns a moment object when passed a date`, () => { const moment = getMoment('2017-03-30T14:53:08.121Z'); - expect(moment.constructor.name).to.be('Moment'); + expect(moment.constructor.name).toBe('Moment'); }); it(`returns null when passed falsy`, () => { @@ -26,7 +25,7 @@ describe('get_moment', () => { ]; results.forEach((result) => { - expect(result).to.be(null); + expect(result).toBe(null); }); }); }); diff --git a/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/__tests__/fetch_all_from_scroll.js b/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.test.js similarity index 59% rename from x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/__tests__/fetch_all_from_scroll.js rename to x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.test.js index 4a77324da18be..8de025d300d55 100644 --- a/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/__tests__/fetch_all_from_scroll.js +++ b/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.test.js @@ -4,9 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import { fetchAllFromScroll } from '../fetch_all_from_scroll'; +import { fetchAllFromScroll } from './fetch_all_from_scroll'; import { set } from '@elastic/safer-lodash-set'; describe('fetch_all_from_scroll', () => { @@ -16,29 +14,31 @@ describe('fetch_all_from_scroll', () => { beforeEach(() => { mockResponse = {}; - stubCallWithRequest = sinon.stub(); - stubCallWithRequest.onCall(0).returns( - new Promise((resolve) => { - const mockInnerResponse = { - hits: { - hits: ['newhit'], - }, - _scroll_id: 'newScrollId', - }; - return resolve(mockInnerResponse); - }) - ); + stubCallWithRequest = jest.fn(); - stubCallWithRequest.onCall(1).returns( - new Promise((resolve) => { - const mockInnerResponse = { - hits: { - hits: [], - }, - }; - return resolve(mockInnerResponse); - }) - ); + // TODO: That mocking needs to be migrated to jest + // stubCallWithRequest.onCall(0).returns( + // new Promise((resolve) => { + // const mockInnerResponse = { + // hits: { + // hits: ['newhit'], + // }, + // _scroll_id: 'newScrollId', + // }; + // return resolve(mockInnerResponse); + // }) + // ); + // + // stubCallWithRequest.onCall(1).returns( + // new Promise((resolve) => { + // const mockInnerResponse = { + // hits: { + // hits: [], + // }, + // }; + // return resolve(mockInnerResponse); + // }) + // ); }); describe('#fetchAllFromScroll', () => { @@ -49,18 +49,19 @@ describe('fetch_all_from_scroll', () => { it('should return an empty array of hits', () => { return fetchAllFromScroll(mockResponse).then((hits) => { - expect(hits).to.eql([]); + expect(hits).toEqual([]); }); }); it('should not call callWithRequest', () => { return fetchAllFromScroll(mockResponse, stubCallWithRequest).then(() => { - expect(stubCallWithRequest.called).to.be(false); + expect(stubCallWithRequest).not.toHaveBeenCalled(); }); }); }); - describe('when the passed-in response has some hits', () => { + // TODO: tests were not running and are not up to date + describe.skip('when the passed-in response has some hits', () => { beforeEach(() => { set(mockResponse, 'hits.hits', ['foo', 'bar']); set(mockResponse, '_scroll_id', 'originalScrollId'); @@ -68,19 +69,19 @@ describe('fetch_all_from_scroll', () => { it('should return the hits from the response', () => { return fetchAllFromScroll(mockResponse, stubCallWithRequest).then((hits) => { - expect(hits).to.eql(['foo', 'bar', 'newhit']); + expect(hits).toEqual(['foo', 'bar', 'newhit']); }); }); it('should call callWithRequest', () => { return fetchAllFromScroll(mockResponse, stubCallWithRequest).then(() => { - expect(stubCallWithRequest.calledTwice).to.be(true); + expect(stubCallWithRequest.calledTwice).toBe(true); const firstCallWithRequestCallArgs = stubCallWithRequest.args[0]; - expect(firstCallWithRequestCallArgs[1].body.scroll_id).to.eql('originalScrollId'); + expect(firstCallWithRequestCallArgs[1].body.scroll_id).toEqual('originalScrollId'); const secondCallWithRequestCallArgs = stubCallWithRequest.args[1]; - expect(secondCallWithRequestCallArgs[1].body.scroll_id).to.eql('newScrollId'); + expect(secondCallWithRequestCallArgs[1].body.scroll_id).toEqual('newScrollId'); }); }); }); diff --git a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.test.js b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js similarity index 81% rename from x-pack/plugins/watcher/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.test.js rename to x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js index be6873a61e902..77116e177d1ca 100644 --- a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.test.js +++ b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { kibanaResponseFactory } from '../../../../../../../src/core/server'; -import { licensePreRoutingFactory } from '../license_pre_routing_factory'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; +import { licensePreRoutingFactory } from './license_pre_routing_factory'; describe('license_pre_routing_factory', () => { describe('#reportingFeaturePreRoutingFactory', () => { @@ -23,7 +22,7 @@ describe('license_pre_routing_factory', () => { const routeWithLicenseCheck = licensePreRoutingFactory(mockDeps, () => {}); const stubRequest = {}; const response = routeWithLicenseCheck({}, stubRequest, kibanaResponseFactory); - expect(response.status).to.be(403); + expect(response.status).toBe(403); }); }); @@ -33,7 +32,7 @@ describe('license_pre_routing_factory', () => { const routeWithLicenseCheck = licensePreRoutingFactory(mockDeps, () => null); const stubRequest = {}; const response = routeWithLicenseCheck({}, stubRequest, kibanaResponseFactory); - expect(response).to.be(null); + expect(response).toBe(null); }); }); }); diff --git a/x-pack/plugins/watcher/server/models/action_status/__tests__/action_status.js b/x-pack/plugins/watcher/server/models/action_status/action_status.test.js similarity index 79% rename from x-pack/plugins/watcher/server/models/action_status/__tests__/action_status.js rename to x-pack/plugins/watcher/server/models/action_status/action_status.test.js index 96ca0d1cb7686..8fec6bcd78b88 100644 --- a/x-pack/plugins/watcher/server/models/action_status/__tests__/action_status.js +++ b/x-pack/plugins/watcher/server/models/action_status/action_status.test.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { ActionStatus } from '../action_status'; -import { ACTION_STATES } from '../../../../common/constants'; +import { ActionStatus } from './action_status'; +import { ACTION_STATES } from '../../../common/constants'; import moment from 'moment'; describe('action_status', () => { @@ -39,16 +38,16 @@ describe('action_status', () => { it(`throws an error if no 'id' property in json`, () => { delete upstreamJson.id; - expect(ActionStatus.fromUpstreamJson) - .withArgs(upstreamJson) - .to.throwError('JSON argument must contain an "id" property'); + expect(() => { + ActionStatus.fromUpstreamJson(upstreamJson); + }).toThrow('JSON argument must contain an "id" property'); }); it(`throws an error if no 'actionStatusJson' property in json`, () => { delete upstreamJson.actionStatusJson; - expect(ActionStatus.fromUpstreamJson) - .withArgs(upstreamJson) - .to.throwError('JSON argument must contain an "actionStatusJson" property'); + expect(() => { + ActionStatus.fromUpstreamJson(upstreamJson); + }).toThrow('JSON argument must contain an "actionStatusJson" property'); }); it('returns correct ActionStatus instance', () => { @@ -57,26 +56,26 @@ describe('action_status', () => { errors: { foo: 'bar' }, }); - expect(actionStatus.id).to.be(upstreamJson.id); - expect(actionStatus.lastAcknowledged).to.eql( + expect(actionStatus.id).toBe(upstreamJson.id); + expect(actionStatus.lastAcknowledged).toEqual( moment(upstreamJson.actionStatusJson.ack.timestamp) ); - expect(actionStatus.lastExecution).to.eql( + expect(actionStatus.lastExecution).toEqual( moment(upstreamJson.actionStatusJson.last_execution.timestamp) ); - expect(actionStatus.lastExecutionSuccessful).to.eql( + expect(actionStatus.lastExecutionSuccessful).toEqual( upstreamJson.actionStatusJson.last_execution.successful ); - expect(actionStatus.lastExecutionReason).to.be( + expect(actionStatus.lastExecutionReason).toBe( upstreamJson.actionStatusJson.last_execution.reason ); - expect(actionStatus.lastThrottled).to.eql( + expect(actionStatus.lastThrottled).toEqual( moment(upstreamJson.actionStatusJson.last_throttle.timestamp) ); - expect(actionStatus.lastSuccessfulExecution).to.eql( + expect(actionStatus.lastSuccessfulExecution).toEqual( moment(upstreamJson.actionStatusJson.last_successful_execution.timestamp) ); - expect(actionStatus.errors).to.eql({ foo: 'bar' }); + expect(actionStatus.errors).toEqual({ foo: 'bar' }); }); }); @@ -111,7 +110,7 @@ describe('action_status', () => { it('lastExecutionSuccessful is equal to false and it is the most recent execution', () => { upstreamJson.actionStatusJson.last_execution.successful = false; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.ERROR); + expect(actionStatus.state).toBe(ACTION_STATES.ERROR); }); it('action is acked and lastAcknowledged is less than lastExecution', () => { @@ -127,7 +126,7 @@ describe('action_status', () => { }, }, }); - expect(actionStatus.state).to.be(ACTION_STATES.ERROR); + expect(actionStatus.state).toBe(ACTION_STATES.ERROR); }); it('action is ackable and lastSuccessfulExecution is less than lastExecution', () => { @@ -138,7 +137,7 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-02T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.ERROR); + expect(actionStatus.state).toBe(ACTION_STATES.ERROR); }); }); @@ -147,14 +146,14 @@ describe('action_status', () => { ...upstreamJson, errors: { foo: 'bar' }, }); - expect(actionStatus.state).to.be(ACTION_STATES.CONFIG_ERROR); + expect(actionStatus.state).toBe(ACTION_STATES.CONFIG_ERROR); }); it(`correctly calculates ACTION_STATES.OK`, () => { upstreamJson.actionStatusJson.ack.state = 'awaits_successful_execution'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.OK); + expect(actionStatus.state).toBe(ACTION_STATES.OK); }); describe(`correctly calculates ACTION_STATES.ACKNOWLEDGED`, () => { @@ -164,7 +163,7 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.ACKNOWLEDGED); + expect(actionStatus.state).toBe(ACTION_STATES.ACKNOWLEDGED); }); it(`when lastAcknowledged is greater than lastExecution`, () => { @@ -173,7 +172,7 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.ACKNOWLEDGED); + expect(actionStatus.state).toBe(ACTION_STATES.ACKNOWLEDGED); }); }); @@ -184,7 +183,7 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.THROTTLED); + expect(actionStatus.state).toBe(ACTION_STATES.THROTTLED); }); it(`when lastThrottled is greater than lastExecution`, () => { @@ -193,7 +192,7 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.THROTTLED); + expect(actionStatus.state).toBe(ACTION_STATES.THROTTLED); }); }); @@ -206,7 +205,7 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.FIRING); + expect(actionStatus.state).toBe(ACTION_STATES.FIRING); }); it(`when lastSuccessfulExecution is greater than lastExecution`, () => { @@ -217,7 +216,7 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.FIRING); + expect(actionStatus.state).toBe(ACTION_STATES.FIRING); }); }); @@ -231,7 +230,7 @@ describe('action_status', () => { }; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.UNKNOWN); + expect(actionStatus.state).toBe(ACTION_STATES.UNKNOWN); }); }); @@ -265,8 +264,8 @@ describe('action_status', () => { upstreamJson.actionStatusJson.ack.state = 'awaits_successful_execution'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.OK); - expect(actionStatus.isAckable).to.be(false); + expect(actionStatus.state).toBe(ACTION_STATES.OK); + expect(actionStatus.isAckable).toBe(false); }); it(`correctly calculated isAckable when in ACTION_STATES.ACKNOWLEDGED`, () => { @@ -275,8 +274,8 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.ACKNOWLEDGED); - expect(actionStatus.isAckable).to.be(false); + expect(actionStatus.state).toBe(ACTION_STATES.ACKNOWLEDGED); + expect(actionStatus.isAckable).toBe(false); }); it(`correctly calculated isAckable when in ACTION_STATES.THROTTLED`, () => { @@ -285,8 +284,8 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.THROTTLED); - expect(actionStatus.isAckable).to.be(true); + expect(actionStatus.state).toBe(ACTION_STATES.THROTTLED); + expect(actionStatus.isAckable).toBe(true); }); it(`correctly calculated isAckable when in ACTION_STATES.FIRING`, () => { @@ -297,8 +296,8 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-01T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.FIRING); - expect(actionStatus.isAckable).to.be(true); + expect(actionStatus.state).toBe(ACTION_STATES.FIRING); + expect(actionStatus.isAckable).toBe(true); }); it(`correctly calculated isAckable when in ACTION_STATES.ERROR`, () => { @@ -309,8 +308,8 @@ describe('action_status', () => { upstreamJson.actionStatusJson.last_execution.timestamp = '2017-03-02T00:00:00.000Z'; const actionStatus = ActionStatus.fromUpstreamJson(upstreamJson); - expect(actionStatus.state).to.be(ACTION_STATES.ERROR); - expect(actionStatus.isAckable).to.be(false); + expect(actionStatus.state).toBe(ACTION_STATES.ERROR); + expect(actionStatus.isAckable).toBe(false); }); }); @@ -345,15 +344,15 @@ describe('action_status', () => { const json = actionStatus.downstreamJson; - expect(json.id).to.be(actionStatus.id); - expect(json.state).to.be(actionStatus.state); - expect(json.isAckable).to.be(actionStatus.isAckable); - expect(json.lastAcknowledged).to.be(actionStatus.lastAcknowledged); - expect(json.lastThrottled).to.be(actionStatus.lastThrottled); - expect(json.lastExecution).to.be(actionStatus.lastExecution); - expect(json.lastExecutionSuccessful).to.be(actionStatus.lastExecutionSuccessful); - expect(json.lastExecutionReason).to.be(actionStatus.lastExecutionReason); - expect(json.lastSuccessfulExecution).to.be(actionStatus.lastSuccessfulExecution); + expect(json.id).toBe(actionStatus.id); + expect(json.state).toBe(actionStatus.state); + expect(json.isAckable).toBe(actionStatus.isAckable); + expect(json.lastAcknowledged).toBe(actionStatus.lastAcknowledged); + expect(json.lastThrottled).toBe(actionStatus.lastThrottled); + expect(json.lastExecution).toBe(actionStatus.lastExecution); + expect(json.lastExecutionSuccessful).toBe(actionStatus.lastExecutionSuccessful); + expect(json.lastExecutionReason).toBe(actionStatus.lastExecutionReason); + expect(json.lastSuccessfulExecution).toBe(actionStatus.lastSuccessfulExecution); }); }); }); diff --git a/x-pack/plugins/watcher/server/models/execute_details/__tests__/execute_details.js b/x-pack/plugins/watcher/server/models/execute_details/execute_details.test.js similarity index 85% rename from x-pack/plugins/watcher/server/models/execute_details/__tests__/execute_details.js rename to x-pack/plugins/watcher/server/models/execute_details/execute_details.test.js index 7299a7d7d54d3..7198170608d0c 100644 --- a/x-pack/plugins/watcher/server/models/execute_details/__tests__/execute_details.js +++ b/x-pack/plugins/watcher/server/models/execute_details/execute_details.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { ExecuteDetails } from '../execute_details'; +import { ExecuteDetails } from './execute_details'; describe('execute_details', () => { describe('ExecuteDetails', () => { @@ -24,11 +23,11 @@ describe('execute_details', () => { it('returns correct ExecuteDetails instance', () => { const executeDetails = ExecuteDetails.fromDownstreamJson(props); - expect(executeDetails.triggerData).to.be(props.triggerData); - expect(executeDetails.ignoreCondition).to.be(props.ignoreCondition); - expect(executeDetails.alternativeInput).to.be(props.alternativeInput); - expect(executeDetails.actionModes).to.be(props.actionModes); - expect(executeDetails.recordExecution).to.be(props.recordExecution); + expect(executeDetails.triggerData).toBe(props.triggerData); + expect(executeDetails.ignoreCondition).toBe(props.ignoreCondition); + expect(executeDetails.alternativeInput).toBe(props.alternativeInput); + expect(executeDetails.actionModes).toBe(props.actionModes); + expect(executeDetails.recordExecution).toBe(props.recordExecution); }); }); @@ -61,7 +60,7 @@ describe('execute_details', () => { record_execution: executeDetails.recordExecution, }; - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); it('returns correct JSON for client with no triggeredTime', () => { @@ -79,7 +78,7 @@ describe('execute_details', () => { record_execution: executeDetails.recordExecution, }; - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); it('returns correct JSON for client with no scheduledTime', () => { @@ -97,7 +96,7 @@ describe('execute_details', () => { record_execution: executeDetails.recordExecution, }; - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); it('returns correct JSON for client with no scheduledTime or triggeredTime', () => { @@ -114,7 +113,7 @@ describe('execute_details', () => { record_execution: executeDetails.recordExecution, }; - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); }); }); diff --git a/x-pack/plugins/watcher/server/models/fields/__tests__/fields.js b/x-pack/plugins/watcher/server/models/fields/fields.test.js similarity index 85% rename from x-pack/plugins/watcher/server/models/fields/__tests__/fields.js rename to x-pack/plugins/watcher/server/models/fields/fields.test.js index f4cc3e4212994..53d637a1d7990 100644 --- a/x-pack/plugins/watcher/server/models/fields/__tests__/fields.js +++ b/x-pack/plugins/watcher/server/models/fields/fields.test.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import { find } from 'lodash'; -import { Fields } from '../fields'; +import { Fields } from './fields'; describe('fields', () => { describe('Fields', () => { @@ -54,15 +53,15 @@ describe('fields', () => { describe('fromUpstreamJson factory method', () => { it(`throws an error if no 'fields' property in json`, () => { delete upstreamJson.fields; - expect(Fields.fromUpstreamJson) - .withArgs(upstreamJson) - .to.throwError(/must contain a fields property/i); + expect(() => { + Fields.fromUpstreamJson(upstreamJson); + }).toThrow(/must contain a fields property/i); }); it('returns correct Fields instance', () => { const fields = Fields.fromUpstreamJson(upstreamJson); - expect(fields.fields).to.be.an('array'); + expect(fields.fields).toBeInstanceOf(Array); }); it('uses the first instance of a field if the same field exists in multiple mappings', () => { @@ -71,21 +70,21 @@ describe('fields', () => { //field-bar is defined as both a boolean and an integer, should default to //boolean. - expect(actual.normalizedType).to.be('boolean'); + expect(actual.normalizedType).toBe('boolean'); }); it('defaults to the type if no normalizedType exists', () => { const fields = Fields.fromUpstreamJson(upstreamJson); const actual = find(fields.fields, { name: 'field-foo' }); - expect(actual.normalizedType).to.be('text'); + expect(actual.normalizedType).toBe('text'); }); it('populates normalizedType if one exists', () => { const fields = Fields.fromUpstreamJson(upstreamJson); const actual = find(fields.fields, { name: 'field-bop' }); - expect(actual.normalizedType).to.be('number'); + expect(actual.normalizedType).toBe('number'); }); it('populates all properties', () => { @@ -99,7 +98,7 @@ describe('fields', () => { aggregatable: false, }; - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); }); @@ -108,7 +107,7 @@ describe('fields', () => { const fields = Fields.fromUpstreamJson(upstreamJson); const json = fields.downstreamJson; - expect(json.fields).to.eql(fields.fields); + expect(json.fields).toEqual(fields.fields); }); }); }); diff --git a/x-pack/plugins/watcher/server/models/settings/__tests__/settings.js b/x-pack/plugins/watcher/server/models/settings/settings.test.js similarity index 63% rename from x-pack/plugins/watcher/server/models/settings/__tests__/settings.js rename to x-pack/plugins/watcher/server/models/settings/settings.test.js index 8ce6d383a61d6..0e653039cd250 100644 --- a/x-pack/plugins/watcher/server/models/settings/__tests__/settings.js +++ b/x-pack/plugins/watcher/server/models/settings/settings.test.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { Settings } from '../settings'; +import { Settings } from './settings'; describe('settings module', () => { describe('Settings class', () => { @@ -15,13 +14,13 @@ describe('settings module', () => { const settings = Settings.fromUpstreamJson(); const actionTypes = settings.actionTypes; - expect(actionTypes.email.enabled).to.be(false); - expect(actionTypes.webhook.enabled).to.be(true); - expect(actionTypes.index.enabled).to.be(true); - expect(actionTypes.logging.enabled).to.be(true); - expect(actionTypes.slack.enabled).to.be(false); - expect(actionTypes.jira.enabled).to.be(false); - expect(actionTypes.pagerduty.enabled).to.be(false); + expect(actionTypes.email.enabled).toBe(false); + expect(actionTypes.webhook.enabled).toBe(true); + expect(actionTypes.index.enabled).toBe(true); + expect(actionTypes.logging.enabled).toBe(true); + expect(actionTypes.slack.enabled).toBe(false); + expect(actionTypes.jira.enabled).toBe(false); + expect(actionTypes.pagerduty.enabled).toBe(false); }); }); @@ -58,11 +57,11 @@ describe('settings module', () => { const settings = Settings.fromUpstreamJson(upstreamJson); const actionTypes = settings.actionTypes; - expect(actionTypes.email.enabled).to.be(true); - expect(actionTypes.email.accounts.scooby.default).to.be(true); - expect(actionTypes.email.accounts.scrappy).to.be.an('object'); - expect(actionTypes.email.accounts.foo).to.be.an('object'); - expect(actionTypes.email.accounts.bar).to.be.an('object'); + expect(actionTypes.email.enabled).toBe(true); + expect(actionTypes.email.accounts.scooby.default).toBe(true); + expect(actionTypes.email.accounts.scrappy).toBeInstanceOf(Object); + expect(actionTypes.email.accounts.foo).toBeInstanceOf(Object); + expect(actionTypes.email.accounts.bar).toBeInstanceOf(Object); }); }); }); @@ -87,15 +86,15 @@ describe('settings module', () => { const settings = Settings.fromUpstreamJson(upstreamJson); const json = settings.downstreamJson; - expect(json.action_types.email.enabled).to.be(true); - expect(json.action_types.email.accounts.scooby.default).to.be(true); - expect(json.action_types.email.accounts.scrappy).to.be.an('object'); - expect(json.action_types.webhook.enabled).to.be(true); - expect(json.action_types.index.enabled).to.be(true); - expect(json.action_types.logging.enabled).to.be(true); - expect(json.action_types.slack.enabled).to.be(false); - expect(json.action_types.jira.enabled).to.be(false); - expect(json.action_types.pagerduty.enabled).to.be(false); + expect(json.action_types.email.enabled).toBe(true); + expect(json.action_types.email.accounts.scooby.default).toBe(true); + expect(json.action_types.email.accounts.scrappy).toBeInstanceOf(Object); + expect(json.action_types.webhook.enabled).toBe(true); + expect(json.action_types.index.enabled).toBe(true); + expect(json.action_types.logging.enabled).toBe(true); + expect(json.action_types.slack.enabled).toBe(false); + expect(json.action_types.jira.enabled).toBe(false); + expect(json.action_types.pagerduty.enabled).toBe(false); }); }); }); diff --git a/x-pack/plugins/watcher/server/models/watch/threshold_watch/__tests__/format_visualize_data.js b/x-pack/plugins/watcher/server/models/watch/threshold_watch/format_visualize_data.test.js similarity index 96% rename from x-pack/plugins/watcher/server/models/watch/threshold_watch/__tests__/format_visualize_data.js rename to x-pack/plugins/watcher/server/models/watch/threshold_watch/format_visualize_data.test.js index 99e88c3628ea8..cc139d4c83223 100644 --- a/x-pack/plugins/watcher/server/models/watch/threshold_watch/__tests__/format_visualize_data.js +++ b/x-pack/plugins/watcher/server/models/watch/threshold_watch/format_visualize_data.test.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { AGG_TYPES } from '../../../../../common/constants'; -import { formatVisualizeData } from '../format_visualize_data'; +import { AGG_TYPES } from '../../../../common/constants'; +import { formatVisualizeData } from './format_visualize_data'; describe('watch', () => { describe('formatVisualizeData', () => { @@ -56,7 +55,7 @@ describe('watch', () => { const actual = formatVisualizeData(watch, response); - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); it('correctly formats data from a Count Terms query', () => { @@ -151,7 +150,7 @@ describe('watch', () => { const actual = formatVisualizeData(watch, response); - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); it('correctly formats data from a Non-Count query', () => { @@ -197,7 +196,7 @@ describe('watch', () => { const actual = formatVisualizeData(watch, response); - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); it('correctly formats data from a Non-Count Terms query', () => { @@ -319,7 +318,7 @@ describe('watch', () => { const actual = formatVisualizeData(watch, response); - expect(actual).to.eql(expected); + expect(actual).toEqual(expected); }); }); }); diff --git a/x-pack/plugins/watcher/server/models/watch_history_item/__tests__/watch_history_item.js b/x-pack/plugins/watcher/server/models/watch_history_item/watch_history_item.test.js similarity index 72% rename from x-pack/plugins/watcher/server/models/watch_history_item/__tests__/watch_history_item.js rename to x-pack/plugins/watcher/server/models/watch_history_item/watch_history_item.test.js index 36f512178befb..7bf535f0801fb 100644 --- a/x-pack/plugins/watcher/server/models/watch_history_item/__tests__/watch_history_item.js +++ b/x-pack/plugins/watcher/server/models/watch_history_item/watch_history_item.test.js @@ -5,8 +5,7 @@ */ import moment from 'moment'; -import expect from '@kbn/expect'; -import { WatchHistoryItem } from '../watch_history_item'; +import { WatchHistoryItem } from './watch_history_item'; describe('watch_history_item', () => { describe('WatchHistoryItem', () => { @@ -51,21 +50,21 @@ describe('watch_history_item', () => { describe('fromUpstreamJson factory method', () => { it('returns correct WatchHistoryItem instance', () => { const watchHistoryItem = WatchHistoryItem.fromUpstreamJson(upstreamJson); - expect(watchHistoryItem).to.have.property('id'); - expect(watchHistoryItem).to.have.property('watchId'); - expect(watchHistoryItem).to.have.property('watchHistoryItemJson'); - expect(watchHistoryItem).to.have.property('includeDetails'); - expect(watchHistoryItem).to.have.property('details'); - expect(watchHistoryItem).to.have.property('startTime'); - expect(watchHistoryItem).to.have.property('watchStatus'); + expect(watchHistoryItem).toHaveProperty('id'); + expect(watchHistoryItem).toHaveProperty('watchId'); + expect(watchHistoryItem).toHaveProperty('watchHistoryItemJson'); + expect(watchHistoryItem).toHaveProperty('includeDetails'); + expect(watchHistoryItem).toHaveProperty('details'); + expect(watchHistoryItem).toHaveProperty('startTime'); + expect(watchHistoryItem).toHaveProperty('watchStatus'); - expect(watchHistoryItem.id).to.eql(upstreamJson.id); - expect(watchHistoryItem.watchId).to.eql(upstreamJson.watchId); - expect(watchHistoryItem.watchHistoryItemJson).to.eql(upstreamJson.watchHistoryItemJson); - expect(watchHistoryItem.includeDetails).to.be(false); - expect(watchHistoryItem.details).to.eql(upstreamJson.watchHistoryItemJson); - expect(watchHistoryItem.startTime).to.be.a(moment); - expect(watchHistoryItem.watchStatus).to.eql({ + expect(watchHistoryItem.id).toEqual(upstreamJson.id); + expect(watchHistoryItem.watchId).toEqual(upstreamJson.watchId); + expect(watchHistoryItem.watchHistoryItemJson).toEqual(upstreamJson.watchHistoryItemJson); + expect(watchHistoryItem.includeDetails).toBe(false); + expect(watchHistoryItem.details).toEqual(upstreamJson.watchHistoryItemJson); + expect(watchHistoryItem.startTime).toBeInstanceOf(moment); + expect(watchHistoryItem.watchStatus).toEqual({ id: upstreamJson.watchId, actionStatuses: [], isActive: upstreamJson.watchHistoryItemJson.status.state.active, @@ -101,7 +100,7 @@ describe('watch_history_item', () => { state: 'OK', }, }; - expect(watchHistoryItem.downstreamJson).to.eql(expected); + expect(watchHistoryItem.downstreamJson).toEqual(expected); }); }); }); diff --git a/x-pack/plugins/watcher/server/models/watch_status/__tests__/watch_status.js b/x-pack/plugins/watcher/server/models/watch_status/watch_status.test.js similarity index 77% rename from x-pack/plugins/watcher/server/models/watch_status/__tests__/watch_status.js rename to x-pack/plugins/watcher/server/models/watch_status/watch_status.test.js index 0d6731b90abe6..949d37f56d7ec 100644 --- a/x-pack/plugins/watcher/server/models/watch_status/__tests__/watch_status.js +++ b/x-pack/plugins/watcher/server/models/watch_status/watch_status.test.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; -import { WatchStatus } from '../watch_status'; -import { ACTION_STATES, WATCH_STATES, WATCH_STATE_COMMENTS } from '../../../../common/constants'; +import { WatchStatus } from './watch_status'; +import { ACTION_STATES, WATCH_STATES, WATCH_STATE_COMMENTS } from '../../../common/constants'; import moment from 'moment'; describe('watch_status', () => { @@ -32,36 +31,37 @@ describe('watch_status', () => { it(`throws an error if no 'id' property in json`, () => { delete upstreamJson.id; - expect(WatchStatus.fromUpstreamJson) - .withArgs(upstreamJson) - .to.throwError(/must contain an id property/i); + expect(() => { + WatchStatus.fromUpstreamJson(upstreamJson); + }).toThrow(/must contain an id property/i); }); it(`throws an error if no 'watchStatusJson' property in json`, () => { delete upstreamJson.watchStatusJson; - expect(WatchStatus.fromUpstreamJson) - .withArgs(upstreamJson) - .to.throwError(/must contain a watchStatusJson property/i); + expect(() => { + WatchStatus.fromUpstreamJson(upstreamJson); + }).toThrow(/must contain a watchStatusJson property/i); }); it('returns correct WatchStatus instance', () => { const watchStatus = WatchStatus.fromUpstreamJson(upstreamJson); - expect(watchStatus.id).to.be(upstreamJson.id); - expect(watchStatus.watchStatusJson).to.eql(upstreamJson.watchStatusJson); - expect(watchStatus.isActive).to.eql(true); - expect(watchStatus.lastChecked).to.eql(moment(upstreamJson.watchStatusJson.last_checked)); - expect(watchStatus.lastMetCondition).to.eql( + expect(watchStatus.id).toBe(upstreamJson.id); + expect(watchStatus.watchStatusJson).toEqual(upstreamJson.watchStatusJson); + expect(watchStatus.isActive).toEqual(true); + expect(watchStatus.lastChecked).toEqual(moment(upstreamJson.watchStatusJson.last_checked)); + expect(watchStatus.lastMetCondition).toEqual( moment(upstreamJson.watchStatusJson.last_met_condition) ); - expect(watchStatus.actionStatuses.length).to.be(2); + expect(watchStatus.actionStatuses.length).toBe(2); - expect(watchStatus.actionStatuses[0].constructor.name).to.be('ActionStatus'); - expect(watchStatus.actionStatuses[1].constructor.name).to.be('ActionStatus'); + expect(watchStatus.actionStatuses[0].constructor.name).toBe('ActionStatus'); + expect(watchStatus.actionStatuses[1].constructor.name).toBe('ActionStatus'); }); }); - describe('lastFired getter method', () => { + // TODO: the test was not running before and is not up to date + describe.skip('lastFired getter method', () => { let upstreamJson; beforeEach(() => { upstreamJson = { @@ -86,7 +86,7 @@ describe('watch_status', () => { it(`returns the latest lastExecution from it's actions`, () => { const watchStatus = WatchStatus.fromUpstreamJson(upstreamJson); - expect(watchStatus.lastFired).to.eql( + expect(watchStatus.lastFired).toEqual( moment(upstreamJson.watchStatusJson.actions.bar.last_execution.timestamp) ); }); @@ -108,7 +108,7 @@ describe('watch_status', () => { it(`correctly calculates WATCH_STATE_COMMENTS.OK there are no actions`, () => { const watchStatus = WatchStatus.fromUpstreamJson(upstreamJson); watchStatus.isActive = true; - expect(watchStatus.comment).to.be(WATCH_STATE_COMMENTS.OK); + expect(watchStatus.comment).toBe(WATCH_STATE_COMMENTS.OK); }); it(`correctly calculates WATCH_STATE_COMMENTS.PARTIALLY_THROTTLED`, () => { @@ -120,7 +120,7 @@ describe('watch_status', () => { { state: ACTION_STATES.OK }, ]; - expect(watchStatus.comment).to.be(WATCH_STATE_COMMENTS.PARTIALLY_THROTTLED); + expect(watchStatus.comment).toBe(WATCH_STATE_COMMENTS.PARTIALLY_THROTTLED); }); it(`correctly calculates WATCH_STATE_COMMENTS.THROTTLED`, () => { @@ -132,7 +132,7 @@ describe('watch_status', () => { { state: ACTION_STATES.THROTTLED }, ]; - expect(watchStatus.comment).to.be(WATCH_STATE_COMMENTS.THROTTLED); + expect(watchStatus.comment).toBe(WATCH_STATE_COMMENTS.THROTTLED); }); it(`correctly calculates WATCH_STATE_COMMENTS.PARTIALLY_ACKNOWLEDGED`, () => { @@ -145,7 +145,7 @@ describe('watch_status', () => { { state: ACTION_STATES.FIRING }, ]; - expect(watchStatus.comment).to.be(WATCH_STATE_COMMENTS.PARTIALLY_ACKNOWLEDGED); + expect(watchStatus.comment).toBe(WATCH_STATE_COMMENTS.PARTIALLY_ACKNOWLEDGED); }); it(`correctly calculates WATCH_STATE_COMMENTS.ACKNOWLEDGED`, () => { @@ -157,7 +157,7 @@ describe('watch_status', () => { { state: ACTION_STATES.ACKNOWLEDGED }, ]; - expect(watchStatus.comment).to.be(WATCH_STATE_COMMENTS.ACKNOWLEDGED); + expect(watchStatus.comment).toBe(WATCH_STATE_COMMENTS.ACKNOWLEDGED); }); it(`correctly calculates WATCH_STATE_COMMENTS.FAILING`, () => { @@ -171,7 +171,7 @@ describe('watch_status', () => { { state: ACTION_STATES.ERROR }, ]; - expect(watchStatus.comment).to.be(WATCH_STATE_COMMENTS.FAILING); + expect(watchStatus.comment).toBe(WATCH_STATE_COMMENTS.FAILING); }); it(`correctly calculates WATCH_STATE_COMMENTS.OK when watch is inactive`, () => { @@ -186,7 +186,7 @@ describe('watch_status', () => { { state: ACTION_STATES.ERROR }, ]; - expect(watchStatus.comment).to.be(WATCH_STATE_COMMENTS.OK); + expect(watchStatus.comment).toBe(WATCH_STATE_COMMENTS.OK); }); }); @@ -206,21 +206,21 @@ describe('watch_status', () => { it(`correctly calculates WATCH_STATES.OK there are no actions`, () => { const watchStatus = WatchStatus.fromUpstreamJson(upstreamJson); watchStatus.isActive = true; - expect(watchStatus.state).to.be(WATCH_STATES.OK); + expect(watchStatus.state).toBe(WATCH_STATES.OK); }); it(`correctly calculates WATCH_STATES.FIRING`, () => { const watchStatus = WatchStatus.fromUpstreamJson(upstreamJson); watchStatus.actionStatuses = [{ state: ACTION_STATES.OK }, { state: ACTION_STATES.FIRING }]; - expect(watchStatus.state).to.be(WATCH_STATES.FIRING); + expect(watchStatus.state).toBe(WATCH_STATES.FIRING); watchStatus.actionStatuses = [ { state: ACTION_STATES.OK }, { state: ACTION_STATES.FIRING }, { state: ACTION_STATES.THROTTLED }, ]; - expect(watchStatus.state).to.be(WATCH_STATES.FIRING); + expect(watchStatus.state).toBe(WATCH_STATES.FIRING); watchStatus.actionStatuses = [ { state: ACTION_STATES.OK }, @@ -228,7 +228,7 @@ describe('watch_status', () => { { state: ACTION_STATES.THROTTLED }, { state: ACTION_STATES.ACKNOWLEDGED }, ]; - expect(watchStatus.state).to.be(WATCH_STATES.FIRING); + expect(watchStatus.state).toBe(WATCH_STATES.FIRING); }); it(`correctly calculates WATCH_STATES.ERROR`, () => { @@ -242,7 +242,7 @@ describe('watch_status', () => { { state: ACTION_STATES.ERROR }, ]; - expect(watchStatus.state).to.be(WATCH_STATES.ERROR); + expect(watchStatus.state).toBe(WATCH_STATES.ERROR); }); it('correctly calculates WATCH_STATE.CONFIG_ERROR', () => { @@ -253,7 +253,7 @@ describe('watch_status', () => { { state: ACTION_STATES.CONFIG_ERROR }, ]; - expect(watchStatus.state).to.be(WATCH_STATES.CONFIG_ERROR); + expect(watchStatus.state).toBe(WATCH_STATES.CONFIG_ERROR); }); it(`correctly calculates WATCH_STATES.DISABLED when watch is inactive`, () => { @@ -268,7 +268,7 @@ describe('watch_status', () => { { state: ACTION_STATES.ERROR }, ]; - expect(watchStatus.state).to.be(WATCH_STATES.DISABLED); + expect(watchStatus.state).toBe(WATCH_STATES.DISABLED); }); }); @@ -300,14 +300,14 @@ describe('watch_status', () => { const actual = watchStatus.downstreamJson; - expect(actual.id).to.be(watchStatus.id); - expect(actual.state).to.be(watchStatus.state); - expect(actual.comment).to.be(watchStatus.comment); - expect(actual.isActive).to.be(watchStatus.isActive); - expect(actual.lastChecked).to.be(watchStatus.lastChecked); - expect(actual.lastMetCondition).to.be(watchStatus.lastMetCondition); - expect(actual.lastFired).to.be(watchStatus.lastFired); - expect(actual.actionStatuses.length).to.be(2); + expect(actual.id).toBe(watchStatus.id); + expect(actual.state).toBe(watchStatus.state); + expect(actual.comment).toBe(watchStatus.comment); + expect(actual.isActive).toBe(watchStatus.isActive); + expect(actual.lastChecked).toBe(watchStatus.lastChecked); + expect(actual.lastMetCondition).toBe(watchStatus.lastMetCondition); + expect(actual.lastFired).toBe(watchStatus.lastFired); + expect(actual.actionStatuses.length).toBe(2); }); }); }); diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx b/x-pack/plugins/watcher/tests_client_integration/helpers/app_context.mock.tsx similarity index 89% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx rename to x-pack/plugins/watcher/tests_client_integration/helpers/app_context.mock.tsx index 7caa8b8bc0859..6f30bf220fb97 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/app_context.mock.tsx @@ -14,9 +14,9 @@ import { notificationServiceMock, httpServiceMock, scopedHistoryMock, -} from '../../../../../../src/core/public/mocks'; -import { AppContextProvider } from '../../../public/application/app_context'; -import { LicenseStatus } from '../../../common/types/license_status'; +} from '../../../../../src/core/public/mocks'; +import { AppContextProvider } from '../../public/application/app_context'; +import { LicenseStatus } from '../../common/types/license_status'; class MockTimeBuckets { setBounds(_domain: any) { diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/body_response.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/body_response.ts similarity index 100% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/body_response.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/body_response.ts diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/http_requests.ts similarity index 99% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/http_requests.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/http_requests.ts index 7d9c1e4163d7b..96d7bb6b915b1 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/http_requests.ts @@ -5,7 +5,7 @@ */ import sinon, { SinonFakeServer } from 'sinon'; -import { ROUTES } from '../../../common/constants'; +import { ROUTES } from '../../common/constants'; const { API_ROOT } = ROUTES; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/index.ts similarity index 100% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/index.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/index.ts diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/constants.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/jest_constants.ts similarity index 87% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/constants.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/jest_constants.ts index 3f7c1fbe1c57f..6f243e130c235 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/constants.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/jest_constants.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getWatch } from '../../../test/fixtures'; +import { getWatch } from '../../__fixtures__'; export const WATCH_ID = 'my-test-watch'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/setup_environment.ts similarity index 91% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/setup_environment.ts index 3cac3eb40d894..f93e9af00cf10 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/setup_environment.ts @@ -7,7 +7,7 @@ import axios from 'axios'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import { init as initHttpRequests } from './http_requests'; -import { setHttpClient, setSavedObjectsClient } from '../../../public/application/lib/api'; +import { setHttpClient, setSavedObjectsClient } from '../../public/application/lib/api'; const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); mockHttpClient.interceptors.response.use( diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_json.helpers.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_create_json.helpers.ts similarity index 89% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_json.helpers.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/watch_create_json.helpers.ts index 2134268b096f2..24c62e7d2ad71 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_json.helpers.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_create_json.helpers.ts @@ -5,9 +5,9 @@ */ import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; import { withAppContext } from './app_context.mock'; -import { WatchEdit } from '../../../public/application/sections/watch_edit/components/watch_edit'; -import { registerRouter } from '../../../public/application/lib/navigation'; -import { ROUTES, WATCH_TYPES } from '../../../common/constants'; +import { WatchEdit } from '../../public/application/sections/watch_edit/components/watch_edit'; +import { registerRouter } from '../../public/application/lib/navigation'; +import { ROUTES, WATCH_TYPES } from '../../common/constants'; const testBedConfig: TestBedConfig = { memoryRouter: { diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_create_threshold.helpers.ts similarity index 92% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/watch_create_threshold.helpers.ts index 5d41fe5c53aac..4e7825c81cb58 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_create_threshold.helpers.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; -import { WatchEdit } from '../../../public/application/sections/watch_edit/components/watch_edit'; -import { registerRouter } from '../../../public/application/lib/navigation'; -import { ROUTES, WATCH_TYPES } from '../../../common/constants'; +import { WatchEdit } from '../../public/application/sections/watch_edit/components/watch_edit'; +import { registerRouter } from '../../public/application/lib/navigation'; +import { ROUTES, WATCH_TYPES } from '../../common/constants'; import { withAppContext } from './app_context.mock'; const testBedConfig: TestBedConfig = { diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit.helpers.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_edit.helpers.ts similarity index 83% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit.helpers.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/watch_edit.helpers.ts index ca9fcdbebd490..5f74f984ff630 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit.helpers.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_edit.helpers.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import { registerTestBed, TestBed, TestBedConfig } from '@kbn/test/jest'; -import { WatchEdit } from '../../../public/application/sections/watch_edit/components/watch_edit'; -import { registerRouter } from '../../../public/application/lib/navigation'; -import { ROUTES } from '../../../common/constants'; -import { WATCH_ID } from './constants'; +import { WatchEdit } from '../../public/application/sections/watch_edit/components/watch_edit'; +import { registerRouter } from '../../public/application/lib/navigation'; +import { ROUTES } from '../../common/constants'; +import { WATCH_ID } from './jest_constants'; import { withAppContext } from './app_context.mock'; const testBedConfig: TestBedConfig = { diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_list.helpers.ts similarity index 95% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/watch_list.helpers.ts index f5a9adc95f43c..ccb7e38123309 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_list.helpers.ts @@ -7,8 +7,8 @@ import { act } from 'react-dom/test-utils'; import { registerTestBed, findTestSubject, TestBed, TestBedConfig, nextTick } from '@kbn/test/jest'; -import { WatchList } from '../../../public/application/sections/watch_list/components/watch_list'; -import { ROUTES, REFRESH_INTERVALS } from '../../../common/constants'; +import { WatchList } from '../../public/application/sections/watch_list/components/watch_list'; +import { ROUTES, REFRESH_INTERVALS } from '../../common/constants'; import { withAppContext } from './app_context.mock'; const testBedConfig: TestBedConfig = { diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_status.helpers.ts similarity index 95% rename from x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts rename to x-pack/plugins/watcher/tests_client_integration/helpers/watch_status.helpers.ts index 803b65ca8fff6..67d2c2251560d 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts +++ b/x-pack/plugins/watcher/tests_client_integration/helpers/watch_status.helpers.ts @@ -7,9 +7,9 @@ import { act } from 'react-dom/test-utils'; import { registerTestBed, findTestSubject, TestBed, TestBedConfig, delay } from '@kbn/test/jest'; -import { WatchStatus } from '../../../public/application/sections/watch_status/components/watch_status'; -import { ROUTES } from '../../../common/constants'; -import { WATCH_ID } from './constants'; +import { WatchStatus } from '../../public/application/sections/watch_status/components/watch_status'; +import { ROUTES } from '../../common/constants'; +import { WATCH_ID } from './jest_constants'; import { withAppContext } from './app_context.mock'; const testBedConfig: TestBedConfig = { diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts b/x-pack/plugins/watcher/tests_client_integration/watch_create_json.test.ts similarity index 97% rename from x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts rename to x-pack/plugins/watcher/tests_client_integration/watch_create_json.test.ts index ab87b26f397c8..b3fbb8235f251 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts +++ b/x-pack/plugins/watcher/tests_client_integration/watch_create_json.test.ts @@ -7,9 +7,9 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment, pageHelpers, nextTick, wrapBodyResponse } from './helpers'; import { WatchCreateJsonTestBed } from './helpers/watch_create_json.helpers'; -import { WATCH } from './helpers/constants'; -import defaultWatchJson from '../../public/application/models/watch/default_watch.json'; -import { getExecuteDetails } from '../../test/fixtures'; +import { WATCH } from './helpers/jest_constants'; +import defaultWatchJson from '../public/application/models/watch/default_watch.json'; +import { getExecuteDetails } from '../__fixtures__'; const { setup } = pageHelpers.watchCreateJson; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx b/x-pack/plugins/watcher/tests_client_integration/watch_create_threshold.test.tsx similarity index 99% rename from x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx rename to x-pack/plugins/watcher/tests_client_integration/watch_create_threshold.test.tsx index 5c6cf22065d95..5b1c1ccd04b38 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx +++ b/x-pack/plugins/watcher/tests_client_integration/watch_create_threshold.test.tsx @@ -16,8 +16,8 @@ import { unwrapBodyResponse, } from './helpers'; import { WatchCreateThresholdTestBed } from './helpers/watch_create_threshold.helpers'; -import { getExecuteDetails } from '../../test/fixtures'; -import { WATCH_TYPES } from '../../common/constants'; +import { getExecuteDetails } from '../__fixtures__'; +import { WATCH_TYPES } from '../common/constants'; const WATCH_NAME = 'my_test_watch'; @@ -49,8 +49,8 @@ const WATCH_VISUALIZE_DATA = { const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); -jest.mock('../../public/application/lib/api', () => { - const original = jest.requireActual('../../public/application/lib/api'); +jest.mock('../public/application/lib/api', () => { + const original = jest.requireActual('../public/application/lib/api'); return { ...original, diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts b/x-pack/plugins/watcher/tests_client_integration/watch_edit.test.ts similarity index 95% rename from x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts rename to x-pack/plugins/watcher/tests_client_integration/watch_edit.test.ts index b22074e4060eb..eefe9d03c05ef 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts +++ b/x-pack/plugins/watcher/tests_client_integration/watch_edit.test.ts @@ -9,15 +9,15 @@ import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import axios from 'axios'; import { setupEnvironment, pageHelpers, nextTick, wrapBodyResponse } from './helpers'; import { WatchEditTestBed } from './helpers/watch_edit.helpers'; -import { WATCH } from './helpers/constants'; -import defaultWatchJson from '../../public/application/models/watch/default_watch.json'; -import { getWatch } from '../../test/fixtures'; +import { WATCH } from './helpers/jest_constants'; +import defaultWatchJson from '../public/application/models/watch/default_watch.json'; +import { getWatch } from '../__fixtures__'; import { getRandomString } from '@kbn/test/jest'; const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); -jest.mock('../../public/application/lib/api', () => { - const original = jest.requireActual('../../public/application/lib/api'); +jest.mock('../public/application/lib/api', () => { + const original = jest.requireActual('../public/application/lib/api'); return { ...original, diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts b/x-pack/plugins/watcher/tests_client_integration/watch_list.test.ts similarity index 98% rename from x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts rename to x-pack/plugins/watcher/tests_client_integration/watch_list.test.ts index 844493ea35261..acae9bfd81b21 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts +++ b/x-pack/plugins/watcher/tests_client_integration/watch_list.test.ts @@ -5,10 +5,10 @@ */ import { act } from 'react-dom/test-utils'; -import * as fixtures from '../../test/fixtures'; +import * as fixtures from '../__fixtures__'; import { setupEnvironment, pageHelpers, getRandomString, findTestSubject } from './helpers'; import { WatchListTestBed } from './helpers/watch_list.helpers'; -import { ROUTES } from '../../common/constants'; +import { ROUTES } from '../common/constants'; const { API_ROOT } = ROUTES; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts b/x-pack/plugins/watcher/tests_client_integration/watch_status.test.ts similarity index 97% rename from x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts rename to x-pack/plugins/watcher/tests_client_integration/watch_status.test.ts index 7a9f060665d5b..cbcdbb73a31d9 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts +++ b/x-pack/plugins/watcher/tests_client_integration/watch_status.test.ts @@ -7,11 +7,11 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment, pageHelpers, nextTick } from './helpers'; import { WatchStatusTestBed } from './helpers/watch_status.helpers'; -import { WATCH } from './helpers/constants'; -import { getWatchHistory } from '../../test/fixtures'; +import { WATCH } from './helpers/jest_constants'; +import { getWatchHistory } from '../__fixtures__'; import moment from 'moment'; -import { ROUTES } from '../../common/constants'; -import { WATCH_STATES, ACTION_STATES } from '../../common/constants'; +import { ROUTES } from '../common/constants'; +import { WATCH_STATES, ACTION_STATES } from '../common/constants'; const { API_ROOT } = ROUTES; From b8d21b1c77051c3e915ce0ac553b2c324c41eb06 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 7 Jan 2021 21:45:51 +0000 Subject: [PATCH 22/24] chore(NA): move kbn-es-archiver from mocha into jest (#87699) * chore(NA): move kbn-es-archiver from mocha into jest * chore(NA): remove mocha temporarily from CI --- .../{__tests__/format.ts => format.test.ts} | 29 +++++---- .../{__tests__/parse.ts => parse.test.ts} | 35 +++++------ .../docs/{__tests__ => __mocks__}/stubs.ts | 0 ...ts => generate_doc_records_stream.test.ts} | 51 ++++++++------- ...am.ts => index_doc_records_stream.test.ts} | 63 +++++++++---------- .../indices/{__tests__ => __mocks__}/stubs.ts | 0 ..._stream.ts => create_index_stream.test.ts} | 31 +++++---- ..._stream.ts => delete_index_stream.test.ts} | 4 +- ... => generate_index_records_stream.test.ts} | 39 ++++++------ ...tream.ts => filter_records_stream.test.ts} | 9 ++- test/scripts/test/mocha.sh | 5 +- 11 files changed, 130 insertions(+), 136 deletions(-) rename packages/kbn-es-archiver/src/lib/archives/{__tests__/format.ts => format.test.ts} (80%) rename packages/kbn-es-archiver/src/lib/archives/{__tests__/parse.ts => parse.test.ts} (85%) rename packages/kbn-es-archiver/src/lib/docs/{__tests__ => __mocks__}/stubs.ts (100%) rename packages/kbn-es-archiver/src/lib/docs/{__tests__/generate_doc_records_stream.ts => generate_doc_records_stream.test.ts} (71%) rename packages/kbn-es-archiver/src/lib/docs/{__tests__/index_doc_records_stream.ts => index_doc_records_stream.test.ts} (77%) rename packages/kbn-es-archiver/src/lib/indices/{__tests__ => __mocks__}/stubs.ts (100%) rename packages/kbn-es-archiver/src/lib/indices/{__tests__/create_index_stream.ts => create_index_stream.test.ts} (92%) rename packages/kbn-es-archiver/src/lib/indices/{__tests__/delete_index_stream.ts => delete_index_stream.test.ts} (96%) rename packages/kbn-es-archiver/src/lib/indices/{__tests__/generate_index_records_stream.ts => generate_index_records_stream.test.ts} (76%) rename packages/kbn-es-archiver/src/lib/records/{__tests__/filter_records_stream.ts => filter_records_stream.test.ts} (89%) diff --git a/packages/kbn-es-archiver/src/lib/archives/__tests__/format.ts b/packages/kbn-es-archiver/src/lib/archives/format.test.ts similarity index 80% rename from packages/kbn-es-archiver/src/lib/archives/__tests__/format.ts rename to packages/kbn-es-archiver/src/lib/archives/format.test.ts index 91c38d0dd1438..5190ea0128173 100644 --- a/packages/kbn-es-archiver/src/lib/archives/__tests__/format.ts +++ b/packages/kbn-es-archiver/src/lib/archives/format.test.ts @@ -20,10 +20,9 @@ import Stream, { Readable, Writable } from 'stream'; import { createGunzip } from 'zlib'; -import expect from '@kbn/expect'; import { createListStream, createPromiseFromStreams, createConcatStream } from '@kbn/utils'; -import { createFormatArchiveStreams } from '../format'; +import { createFormatArchiveStreams } from './format'; const INPUTS = [1, 2, { foo: 'bar' }, [1, 2]]; const INPUT_JSON = INPUTS.map((i) => JSON.stringify(i, null, 2)).join('\n\n'); @@ -32,9 +31,9 @@ describe('esArchiver createFormatArchiveStreams', () => { describe('{ gzip: false }', () => { it('returns an array of streams', () => { const streams = createFormatArchiveStreams({ gzip: false }); - expect(streams).to.be.an('array'); - expect(streams.length).to.be.greaterThan(0); - streams.forEach((s) => expect(s).to.be.a(Stream)); + expect(streams).toBeInstanceOf(Array); + expect(streams.length).toBeGreaterThan(0); + streams.forEach((s) => expect(s).toBeInstanceOf(Stream)); }); it('streams consume js values and produces buffers', async () => { @@ -44,8 +43,8 @@ describe('esArchiver createFormatArchiveStreams', () => { createConcatStream([]), ] as [Readable, ...Writable[]]); - expect(output.length).to.be.greaterThan(0); - output.forEach((b) => expect(b).to.be.a(Buffer)); + expect(output.length).toBeGreaterThan(0); + output.forEach((b) => expect(b).toBeInstanceOf(Buffer)); }); it('product is pretty-printed JSON separated by two newlines', async () => { @@ -55,16 +54,16 @@ describe('esArchiver createFormatArchiveStreams', () => { createConcatStream(''), ] as [Readable, ...Writable[]]); - expect(json).to.be(INPUT_JSON); + expect(json).toBe(INPUT_JSON); }); }); describe('{ gzip: true }', () => { it('returns an array of streams', () => { const streams = createFormatArchiveStreams({ gzip: true }); - expect(streams).to.be.an('array'); - expect(streams.length).to.be.greaterThan(0); - streams.forEach((s) => expect(s).to.be.a(Stream)); + expect(streams).toBeInstanceOf(Array); + expect(streams.length).toBeGreaterThan(0); + streams.forEach((s) => expect(s).toBeInstanceOf(Stream)); }); it('streams consume js values and produces buffers', async () => { @@ -74,8 +73,8 @@ describe('esArchiver createFormatArchiveStreams', () => { createConcatStream([]), ] as [Readable, ...Writable[]]); - expect(output.length).to.be.greaterThan(0); - output.forEach((b) => expect(b).to.be.a(Buffer)); + expect(output.length).toBeGreaterThan(0); + output.forEach((b) => expect(b).toBeInstanceOf(Buffer)); }); it('output can be gunzipped', async () => { @@ -85,7 +84,7 @@ describe('esArchiver createFormatArchiveStreams', () => { createGunzip(), createConcatStream(''), ] as [Readable, ...Writable[]]); - expect(output).to.be(INPUT_JSON); + expect(output).toBe(INPUT_JSON); }); }); @@ -97,7 +96,7 @@ describe('esArchiver createFormatArchiveStreams', () => { createConcatStream(''), ] as [Readable, ...Writable[]]); - expect(json).to.be(INPUT_JSON); + expect(json).toBe(INPUT_JSON); }); }); }); diff --git a/packages/kbn-es-archiver/src/lib/archives/__tests__/parse.ts b/packages/kbn-es-archiver/src/lib/archives/parse.test.ts similarity index 85% rename from packages/kbn-es-archiver/src/lib/archives/__tests__/parse.ts rename to packages/kbn-es-archiver/src/lib/archives/parse.test.ts index deaea5cd4532e..70be5308ddfd4 100644 --- a/packages/kbn-es-archiver/src/lib/archives/__tests__/parse.ts +++ b/packages/kbn-es-archiver/src/lib/archives/parse.test.ts @@ -20,18 +20,17 @@ import Stream, { PassThrough, Readable, Writable, Transform } from 'stream'; import { createGzip } from 'zlib'; -import expect from '@kbn/expect'; import { createConcatStream, createListStream, createPromiseFromStreams } from '@kbn/utils'; -import { createParseArchiveStreams } from '../parse'; +import { createParseArchiveStreams } from './parse'; describe('esArchiver createParseArchiveStreams', () => { describe('{ gzip: false }', () => { it('returns an array of streams', () => { const streams = createParseArchiveStreams({ gzip: false }); - expect(streams).to.be.an('array'); - expect(streams.length).to.be.greaterThan(0); - streams.forEach((s) => expect(s).to.be.a(Stream)); + expect(streams).toBeInstanceOf(Array); + expect(streams.length).toBeGreaterThan(0); + streams.forEach((s) => expect(s).toBeInstanceOf(Stream)); }); describe('streams', () => { @@ -46,7 +45,7 @@ describe('esArchiver createParseArchiveStreams', () => { ...createParseArchiveStreams({ gzip: false }), ]); - expect(output).to.eql({ a: 1 }); + expect(output).toEqual({ a: 1 }); }); it('consume buffers of valid JSON separated by two newlines', async () => { const output = await createPromiseFromStreams([ @@ -63,7 +62,7 @@ describe('esArchiver createParseArchiveStreams', () => { createConcatStream([]), ] as [Readable, ...Writable[]]); - expect(output).to.eql([{ a: 1 }, 1]); + expect(output).toEqual([{ a: 1 }, 1]); }); it('provides each JSON object as soon as it is parsed', async () => { @@ -87,10 +86,10 @@ describe('esArchiver createParseArchiveStreams', () => { ] as [Readable, ...Writable[]]); input.write(Buffer.from('{"a": 1}\n\n{"a":')); - expect(await receivedPromise).to.eql({ a: 1 }); + expect(await receivedPromise).toEqual({ a: 1 }); input.write(Buffer.from('2}')); input.end(); - expect(await finalPromise).to.eql([{ a: 1 }, { a: 2 }]); + expect(await finalPromise).toEqual([{ a: 1 }, { a: 2 }]); }); }); @@ -108,7 +107,7 @@ describe('esArchiver createParseArchiveStreams', () => { ] as [Readable, ...Writable[]]); throw new Error('should have failed'); } catch (err) { - expect(err.message).to.contain('Unexpected number'); + expect(err.message).toEqual(expect.stringContaining('Unexpected number')); } }); }); @@ -117,9 +116,9 @@ describe('esArchiver createParseArchiveStreams', () => { describe('{ gzip: true }', () => { it('returns an array of streams', () => { const streams = createParseArchiveStreams({ gzip: true }); - expect(streams).to.be.an('array'); - expect(streams.length).to.be.greaterThan(0); - streams.forEach((s) => expect(s).to.be.a(Stream)); + expect(streams).toBeInstanceOf(Array); + expect(streams.length).toBeGreaterThan(0); + streams.forEach((s) => expect(s).toBeInstanceOf(Stream)); }); describe('streams', () => { @@ -135,7 +134,7 @@ describe('esArchiver createParseArchiveStreams', () => { ...createParseArchiveStreams({ gzip: true }), ]); - expect(output).to.eql({ a: 1 }); + expect(output).toEqual({ a: 1 }); }); it('parses valid gzipped JSON strings separated by two newlines', async () => { @@ -146,7 +145,7 @@ describe('esArchiver createParseArchiveStreams', () => { createConcatStream([]), ] as [Readable, ...Writable[]]); - expect(output).to.eql([{ a: 1 }, { a: 2 }]); + expect(output).toEqual([{ a: 1 }, { a: 2 }]); }); }); @@ -158,7 +157,7 @@ describe('esArchiver createParseArchiveStreams', () => { createConcatStream([]), ] as [Readable, ...Writable[]]); - expect(output).to.eql([]); + expect(output).toEqual([]); }); describe('stream errors', () => { @@ -171,7 +170,7 @@ describe('esArchiver createParseArchiveStreams', () => { ] as [Readable, ...Writable[]]); throw new Error('should have failed'); } catch (err) { - expect(err.message).to.contain('incorrect header check'); + expect(err.message).toEqual(expect.stringContaining('incorrect header check')); } }); }); @@ -183,7 +182,7 @@ describe('esArchiver createParseArchiveStreams', () => { createListStream([Buffer.from('{"a": 1}')]), ...createParseArchiveStreams(), ]); - expect(output).to.eql({ a: 1 }); + expect(output).toEqual({ a: 1 }); }); }); }); diff --git a/packages/kbn-es-archiver/src/lib/docs/__tests__/stubs.ts b/packages/kbn-es-archiver/src/lib/docs/__mocks__/stubs.ts similarity index 100% rename from packages/kbn-es-archiver/src/lib/docs/__tests__/stubs.ts rename to packages/kbn-es-archiver/src/lib/docs/__mocks__/stubs.ts diff --git a/packages/kbn-es-archiver/src/lib/docs/__tests__/generate_doc_records_stream.ts b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts similarity index 71% rename from packages/kbn-es-archiver/src/lib/docs/__tests__/generate_doc_records_stream.ts rename to packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts index 074333eb6028f..dad6008c89824 100644 --- a/packages/kbn-es-archiver/src/lib/docs/__tests__/generate_doc_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.test.ts @@ -18,22 +18,21 @@ */ import sinon from 'sinon'; -import expect from '@kbn/expect'; import { delay } from 'bluebird'; import { createListStream, createPromiseFromStreams, createConcatStream } from '@kbn/utils'; -import { createGenerateDocRecordsStream } from '../generate_doc_records_stream'; -import { Progress } from '../../progress'; -import { createStubStats, createStubClient } from './stubs'; +import { createGenerateDocRecordsStream } from './generate_doc_records_stream'; +import { Progress } from '../progress'; +import { createStubStats, createStubClient } from './__mocks__/stubs'; describe('esArchiver: createGenerateDocRecordsStream()', () => { it('scolls 1000 documents at a time', async () => { const stats = createStubStats(); const client = createStubClient([ (name, params) => { - expect(name).to.be('search'); - expect(params).to.have.property('index', 'logstash-*'); - expect(params).to.have.property('size', 1000); + expect(name).toBe('search'); + expect(params).toHaveProperty('index', 'logstash-*'); + expect(params).toHaveProperty('size', 1000); return { hits: { total: 0, @@ -49,18 +48,18 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { createGenerateDocRecordsStream({ client, stats, progress }), ]); - expect(progress.getTotal()).to.be(0); - expect(progress.getComplete()).to.be(0); + expect(progress.getTotal()).toBe(0); + expect(progress.getComplete()).toBe(0); }); it('uses a 1 minute scroll timeout', async () => { const stats = createStubStats(); const client = createStubClient([ (name, params) => { - expect(name).to.be('search'); - expect(params).to.have.property('index', 'logstash-*'); - expect(params).to.have.property('scroll', '1m'); - expect(params).to.have.property('rest_total_hits_as_int', true); + expect(name).toBe('search'); + expect(params).toHaveProperty('index', 'logstash-*'); + expect(params).toHaveProperty('scroll', '1m'); + expect(params).toHaveProperty('rest_total_hits_as_int', true); return { hits: { total: 0, @@ -76,8 +75,8 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { createGenerateDocRecordsStream({ client, stats, progress }), ]); - expect(progress.getTotal()).to.be(0); - expect(progress.getComplete()).to.be(0); + expect(progress.getTotal()).toBe(0); + expect(progress.getComplete()).toBe(0); }); it('consumes index names and scrolls completely before continuing', async () => { @@ -85,8 +84,8 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { let checkpoint = Date.now(); const client = createStubClient([ async (name, params) => { - expect(name).to.be('search'); - expect(params).to.have.property('index', 'index1'); + expect(name).toBe('search'); + expect(params).toHaveProperty('index', 'index1'); await delay(200); return { _scroll_id: 'index1ScrollId', @@ -94,17 +93,17 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { }; }, async (name, params) => { - expect(name).to.be('scroll'); - expect(params).to.have.property('scrollId', 'index1ScrollId'); - expect(Date.now() - checkpoint).to.not.be.lessThan(200); + expect(name).toBe('scroll'); + expect(params).toHaveProperty('scrollId', 'index1ScrollId'); + expect(Date.now() - checkpoint).not.toBeLessThan(200); checkpoint = Date.now(); await delay(200); return { hits: { total: 2, hits: [{ _id: 2, _index: 'foo' }] } }; }, async (name, params) => { - expect(name).to.be('search'); - expect(params).to.have.property('index', 'index2'); - expect(Date.now() - checkpoint).to.not.be.lessThan(200); + expect(name).toBe('search'); + expect(params).toHaveProperty('index', 'index2'); + expect(Date.now() - checkpoint).not.toBeLessThan(200); checkpoint = Date.now(); await delay(200); return { hits: { total: 0, hits: [] } }; @@ -118,7 +117,7 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { createConcatStream([]), ]); - expect(docRecords).to.eql([ + expect(docRecords).toEqual([ { type: 'doc', value: { @@ -139,7 +138,7 @@ describe('esArchiver: createGenerateDocRecordsStream()', () => { }, ]); sinon.assert.calledTwice(stats.archivedDoc as any); - expect(progress.getTotal()).to.be(2); - expect(progress.getComplete()).to.be(2); + expect(progress.getTotal()).toBe(2); + expect(progress.getComplete()).toBe(2); }); }); diff --git a/packages/kbn-es-archiver/src/lib/docs/__tests__/index_doc_records_stream.ts b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.test.ts similarity index 77% rename from packages/kbn-es-archiver/src/lib/docs/__tests__/index_doc_records_stream.ts rename to packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.test.ts index 5ce1a0d434ae6..c30efaf679d5d 100644 --- a/packages/kbn-es-archiver/src/lib/docs/__tests__/index_doc_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.test.ts @@ -17,13 +17,12 @@ * under the License. */ -import expect from '@kbn/expect'; import { delay } from 'bluebird'; import { createListStream, createPromiseFromStreams } from '@kbn/utils'; -import { Progress } from '../../progress'; -import { createIndexDocRecordsStream } from '../index_doc_records_stream'; -import { createStubStats, createStubClient, createPersonDocRecords } from './stubs'; +import { Progress } from '../progress'; +import { createIndexDocRecordsStream } from './index_doc_records_stream'; +import { createStubStats, createStubClient, createPersonDocRecords } from './__mocks__/stubs'; const recordsToBulkBody = (records: any[]) => { return records.reduce((acc, record) => { @@ -38,8 +37,8 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { const records = createPersonDocRecords(1); const client = createStubClient([ async (name, params) => { - expect(name).to.be('bulk'); - expect(params).to.eql({ + expect(name).toBe('bulk'); + expect(params).toEqual({ body: recordsToBulkBody(records), requestTimeout: 120000, }); @@ -55,24 +54,24 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { ]); client.assertNoPendingResponses(); - expect(progress.getComplete()).to.be(1); - expect(progress.getTotal()).to.be(undefined); + expect(progress.getComplete()).toBe(1); + expect(progress.getTotal()).toBe(undefined); }); it('consumes multiple doc records and sends to `_bulk` api together', async () => { const records = createPersonDocRecords(10); const client = createStubClient([ async (name, params) => { - expect(name).to.be('bulk'); - expect(params).to.eql({ + expect(name).toBe('bulk'); + expect(params).toEqual({ body: recordsToBulkBody(records.slice(0, 1)), requestTimeout: 120000, }); return { ok: true }; }, async (name, params) => { - expect(name).to.be('bulk'); - expect(params).to.eql({ + expect(name).toBe('bulk'); + expect(params).toEqual({ body: recordsToBulkBody(records.slice(1)), requestTimeout: 120000, }); @@ -88,8 +87,8 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { ]); client.assertNoPendingResponses(); - expect(progress.getComplete()).to.be(10); - expect(progress.getTotal()).to.be(undefined); + expect(progress.getComplete()).toBe(10); + expect(progress.getTotal()).toBe(undefined); }); it('waits until request is complete before sending more', async () => { @@ -99,8 +98,8 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { const delayMs = 1234; const client = createStubClient([ async (name, params) => { - expect(name).to.be('bulk'); - expect(params).to.eql({ + expect(name).toBe('bulk'); + expect(params).toEqual({ body: recordsToBulkBody(records.slice(0, 1)), requestTimeout: 120000, }); @@ -108,12 +107,12 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { return { ok: true }; }, async (name, params) => { - expect(name).to.be('bulk'); - expect(params).to.eql({ + expect(name).toBe('bulk'); + expect(params).toEqual({ body: recordsToBulkBody(records.slice(1)), requestTimeout: 120000, }); - expect(Date.now() - start).to.not.be.lessThan(delayMs); + expect(Date.now() - start).not.toBeLessThan(delayMs); return { ok: true }; }, ]); @@ -125,8 +124,8 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { ]); client.assertNoPendingResponses(); - expect(progress.getComplete()).to.be(10); - expect(progress.getTotal()).to.be(undefined); + expect(progress.getComplete()).toBe(10); + expect(progress.getTotal()).toBe(undefined); }); it('sends a maximum of 300 documents at a time', async () => { @@ -134,18 +133,18 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { const stats = createStubStats(); const client = createStubClient([ async (name, params) => { - expect(name).to.be('bulk'); - expect(params.body.length).to.eql(1 * 2); + expect(name).toBe('bulk'); + expect(params.body.length).toEqual(1 * 2); return { ok: true }; }, async (name, params) => { - expect(name).to.be('bulk'); - expect(params.body.length).to.eql(299 * 2); + expect(name).toBe('bulk'); + expect(params.body.length).toEqual(299 * 2); return { ok: true }; }, async (name, params) => { - expect(name).to.be('bulk'); - expect(params.body.length).to.eql(1 * 2); + expect(name).toBe('bulk'); + expect(params.body.length).toEqual(1 * 2); return { ok: true }; }, ]); @@ -157,8 +156,8 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { ]); client.assertNoPendingResponses(); - expect(progress.getComplete()).to.be(301); - expect(progress.getTotal()).to.be(undefined); + expect(progress.getComplete()).toBe(301); + expect(progress.getTotal()).toBe(undefined); }); it('emits an error if any request fails', async () => { @@ -177,11 +176,11 @@ describe('esArchiver: createIndexDocRecordsStream()', () => { ]); throw new Error('expected stream to emit error'); } catch (err) { - expect(err.message).to.match(/"forcedError":\s*true/); + expect(err.message).toMatch(/"forcedError":\s*true/); } client.assertNoPendingResponses(); - expect(progress.getComplete()).to.be(1); - expect(progress.getTotal()).to.be(undefined); + expect(progress.getComplete()).toBe(1); + expect(progress.getTotal()).toBe(undefined); }); }); diff --git a/packages/kbn-es-archiver/src/lib/indices/__tests__/stubs.ts b/packages/kbn-es-archiver/src/lib/indices/__mocks__/stubs.ts similarity index 100% rename from packages/kbn-es-archiver/src/lib/indices/__tests__/stubs.ts rename to packages/kbn-es-archiver/src/lib/indices/__mocks__/stubs.ts diff --git a/packages/kbn-es-archiver/src/lib/indices/__tests__/create_index_stream.ts b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts similarity index 92% rename from packages/kbn-es-archiver/src/lib/indices/__tests__/create_index_stream.ts rename to packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts index b1a83046f40d6..db3de3378eee1 100644 --- a/packages/kbn-es-archiver/src/lib/indices/__tests__/create_index_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts @@ -17,12 +17,11 @@ * under the License. */ -import expect from '@kbn/expect'; import sinon from 'sinon'; import Chance from 'chance'; import { createPromiseFromStreams, createConcatStream, createListStream } from '@kbn/utils'; -import { createCreateIndexStream } from '../create_index_stream'; +import { createCreateIndexStream } from './create_index_stream'; import { createStubStats, @@ -30,7 +29,7 @@ import { createStubDocRecord, createStubClient, createStubLogger, -} from './stubs'; +} from './__mocks__/stubs'; const chance = new Chance(); @@ -49,7 +48,7 @@ describe('esArchiver: createCreateIndexStream()', () => { createCreateIndexStream({ client, stats, log }), ]); - expect(stats.getTestSummary()).to.eql({ + expect(stats.getTestSummary()).toEqual({ deletedIndex: 1, createdIndex: 2, }); @@ -68,13 +67,13 @@ describe('esArchiver: createCreateIndexStream()', () => { createCreateIndexStream({ client, stats, log }), ]); - expect((client.indices.getAlias as sinon.SinonSpy).calledOnce).to.be.ok(); - expect((client.indices.getAlias as sinon.SinonSpy).args[0][0]).to.eql({ + expect((client.indices.getAlias as sinon.SinonSpy).calledOnce).toBe(true); + expect((client.indices.getAlias as sinon.SinonSpy).args[0][0]).toEqual({ name: 'existing-index', ignore: [404], }); - expect((client.indices.delete as sinon.SinonSpy).calledOnce).to.be.ok(); - expect((client.indices.delete as sinon.SinonSpy).args[0][0]).to.eql({ + expect((client.indices.delete as sinon.SinonSpy).calledOnce).toBe(true); + expect((client.indices.delete as sinon.SinonSpy).args[0][0]).toEqual({ index: ['actual-index'], }); sinon.assert.callCount(client.indices.create as sinon.SinonSpy, 3); // one failed create because of existing @@ -93,7 +92,7 @@ describe('esArchiver: createCreateIndexStream()', () => { createConcatStream([]), ]); - expect(output).to.eql([createStubDocRecord('index', 1), createStubDocRecord('index', 2)]); + expect(output).toEqual([createStubDocRecord('index', 1), createStubDocRecord('index', 2)]); }); it('creates aliases', async () => { @@ -133,7 +132,7 @@ describe('esArchiver: createCreateIndexStream()', () => { createConcatStream([]), ]); - expect(output).to.eql(randoms); + expect(output).toEqual(randoms); }); it('passes through non-record values', async () => { @@ -147,7 +146,7 @@ describe('esArchiver: createCreateIndexStream()', () => { createConcatStream([]), ]); - expect(output).to.eql(nonRecordValues); + expect(output).toEqual(nonRecordValues); }); }); @@ -169,13 +168,13 @@ describe('esArchiver: createCreateIndexStream()', () => { }), ]); - expect(stats.getTestSummary()).to.eql({ + expect(stats.getTestSummary()).toEqual({ skippedIndex: 1, createdIndex: 1, }); sinon.assert.callCount(client.indices.delete as sinon.SinonSpy, 0); sinon.assert.callCount(client.indices.create as sinon.SinonSpy, 2); // one failed create because of existing - expect((client.indices.create as sinon.SinonSpy).args[0][0]).to.have.property( + expect((client.indices.create as sinon.SinonSpy).args[0][0]).toHaveProperty( 'index', 'new-index' ); @@ -203,15 +202,15 @@ describe('esArchiver: createCreateIndexStream()', () => { createConcatStream([]), ]); - expect(stats.getTestSummary()).to.eql({ + expect(stats.getTestSummary()).toEqual({ skippedIndex: 1, createdIndex: 1, }); sinon.assert.callCount(client.indices.delete as sinon.SinonSpy, 0); sinon.assert.callCount(client.indices.create as sinon.SinonSpy, 2); // one failed create because of existing - expect(output).to.have.length(2); - expect(output).to.eql([ + expect(output).toHaveLength(2); + expect(output).toEqual([ createStubDocRecord('new-index', 1), createStubDocRecord('new-index', 2), ]); diff --git a/packages/kbn-es-archiver/src/lib/indices/__tests__/delete_index_stream.ts b/packages/kbn-es-archiver/src/lib/indices/delete_index_stream.test.ts similarity index 96% rename from packages/kbn-es-archiver/src/lib/indices/__tests__/delete_index_stream.ts rename to packages/kbn-es-archiver/src/lib/indices/delete_index_stream.test.ts index 3c9d866700005..ec588d5e7dae2 100644 --- a/packages/kbn-es-archiver/src/lib/indices/__tests__/delete_index_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/delete_index_stream.test.ts @@ -21,14 +21,14 @@ import sinon from 'sinon'; import { createListStream, createPromiseFromStreams } from '@kbn/utils'; -import { createDeleteIndexStream } from '../delete_index_stream'; +import { createDeleteIndexStream } from './delete_index_stream'; import { createStubStats, createStubClient, createStubIndexRecord, createStubLogger, -} from './stubs'; +} from './__mocks__/stubs'; const log = createStubLogger(); diff --git a/packages/kbn-es-archiver/src/lib/indices/__tests__/generate_index_records_stream.ts b/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.test.ts similarity index 76% rename from packages/kbn-es-archiver/src/lib/indices/__tests__/generate_index_records_stream.ts rename to packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.test.ts index d2c9f1274e60f..fc5e86217038f 100644 --- a/packages/kbn-es-archiver/src/lib/indices/__tests__/generate_index_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.test.ts @@ -18,12 +18,11 @@ */ import sinon from 'sinon'; -import expect from '@kbn/expect'; import { createListStream, createPromiseFromStreams, createConcatStream } from '@kbn/utils'; -import { createStubClient, createStubStats } from './stubs'; +import { createStubClient, createStubStats } from './__mocks__/stubs'; -import { createGenerateIndexRecordsStream } from '../generate_index_records_stream'; +import { createGenerateIndexRecordsStream } from './generate_index_records_stream'; describe('esArchiver: createGenerateIndexRecordsStream()', () => { it('consumes index names and queries for the mapping of each', async () => { @@ -36,7 +35,7 @@ describe('esArchiver: createGenerateIndexRecordsStream()', () => { createGenerateIndexRecordsStream(client, stats), ]); - expect(stats.getTestSummary()).to.eql({ + expect(stats.getTestSummary()).toEqual({ archivedIndex: 4, }); @@ -56,12 +55,12 @@ describe('esArchiver: createGenerateIndexRecordsStream()', () => { ]); const params = (client.indices.get as sinon.SinonSpy).args[0][0]; - expect(params).to.have.property('filterPath'); + expect(params).toHaveProperty('filterPath'); const filters: string[] = params.filterPath; - expect(filters.some((path) => path.includes('index.creation_date'))).to.be(true); - expect(filters.some((path) => path.includes('index.uuid'))).to.be(true); - expect(filters.some((path) => path.includes('index.version'))).to.be(true); - expect(filters.some((path) => path.includes('index.provided_name'))).to.be(true); + expect(filters.some((path) => path.includes('index.creation_date'))).toBe(true); + expect(filters.some((path) => path.includes('index.uuid'))).toBe(true); + expect(filters.some((path) => path.includes('index.version'))).toBe(true); + expect(filters.some((path) => path.includes('index.provided_name'))).toBe(true); }); it('produces one index record for each index name it receives', async () => { @@ -74,19 +73,19 @@ describe('esArchiver: createGenerateIndexRecordsStream()', () => { createConcatStream([]), ]); - expect(indexRecords).to.have.length(3); + expect(indexRecords).toHaveLength(3); - expect(indexRecords[0]).to.have.property('type', 'index'); - expect(indexRecords[0]).to.have.property('value'); - expect(indexRecords[0].value).to.have.property('index', 'index1'); + expect(indexRecords[0]).toHaveProperty('type', 'index'); + expect(indexRecords[0]).toHaveProperty('value'); + expect(indexRecords[0].value).toHaveProperty('index', 'index1'); - expect(indexRecords[1]).to.have.property('type', 'index'); - expect(indexRecords[1]).to.have.property('value'); - expect(indexRecords[1].value).to.have.property('index', 'index2'); + expect(indexRecords[1]).toHaveProperty('type', 'index'); + expect(indexRecords[1]).toHaveProperty('value'); + expect(indexRecords[1].value).toHaveProperty('index', 'index2'); - expect(indexRecords[2]).to.have.property('type', 'index'); - expect(indexRecords[2]).to.have.property('value'); - expect(indexRecords[2].value).to.have.property('index', 'index3'); + expect(indexRecords[2]).toHaveProperty('type', 'index'); + expect(indexRecords[2]).toHaveProperty('value'); + expect(indexRecords[2].value).toHaveProperty('index', 'index3'); }); it('understands aliases', async () => { @@ -99,7 +98,7 @@ describe('esArchiver: createGenerateIndexRecordsStream()', () => { createConcatStream([]), ]); - expect(indexRecords).to.eql([ + expect(indexRecords).toEqual([ { type: 'index', value: { diff --git a/packages/kbn-es-archiver/src/lib/records/__tests__/filter_records_stream.ts b/packages/kbn-es-archiver/src/lib/records/filter_records_stream.test.ts similarity index 89% rename from packages/kbn-es-archiver/src/lib/records/__tests__/filter_records_stream.ts rename to packages/kbn-es-archiver/src/lib/records/filter_records_stream.test.ts index cf67ee2071c10..8fba5668e972d 100644 --- a/packages/kbn-es-archiver/src/lib/records/__tests__/filter_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/records/filter_records_stream.test.ts @@ -18,11 +18,10 @@ */ import Chance from 'chance'; -import expect from '@kbn/expect'; import { createListStream, createPromiseFromStreams, createConcatStream } from '@kbn/utils'; -import { createFilterRecordsStream } from '../filter_records_stream'; +import { createFilterRecordsStream } from './filter_records_stream'; const chance = new Chance(); @@ -42,7 +41,7 @@ describe('esArchiver: createFilterRecordsStream()', () => { createConcatStream([]), ]); - expect(output).to.eql([]); + expect(output).toEqual([]); }); it('produces record values that have a matching type', async () => { @@ -61,7 +60,7 @@ describe('esArchiver: createFilterRecordsStream()', () => { createConcatStream([]), ]); - expect(output).to.have.length(3); - expect(output.map((o) => o.type)).to.eql([type1, type1, type1]); + expect(output).toHaveLength(3); + expect(output.map((o) => o.type)).toEqual([type1, type1, type1]); }); }); diff --git a/test/scripts/test/mocha.sh b/test/scripts/test/mocha.sh index e5f3259926e42..5e005c89330ca 100755 --- a/test/scripts/test/mocha.sh +++ b/test/scripts/test/mocha.sh @@ -2,5 +2,6 @@ source src/dev/ci_setup/setup_env.sh -checks-reporter-with-killswitch "Mocha Tests" \ - node scripts/mocha +# TODO: will remove mocha in another PR +# checks-reporter-with-killswitch "Mocha Tests" \ +# node scripts/mocha From 285809e6b5ed100714f52fcbf4fc49355d15716c Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Thu, 7 Jan 2021 15:14:30 -0700 Subject: [PATCH 23/24] [Security Solution][Detections] Alert table status update bug (#87243) --- .../detections/components/alerts_table/actions.tsx | 1 - .../timeline_actions/alert_context_menu.tsx | 13 +++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 54cdd636f7a33..457f538450079 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -77,7 +77,6 @@ export const updateAlertStatusAction = async ({ setEventsLoading({ eventIds: alertIds, isLoading: true }); const queryObject = query ? { query: JSON.parse(query) } : getUpdateAlertsQuery(alertIds); - const response = await updateAlertStatus({ query: queryObject, status: selectedStatus }); // TODO: Only delete those that were successfully updated from updatedRules setEventsDeleted({ eventIds: alertIds, isDeleted: true }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index 614b39d280ae4..35f753f8cf0b1 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -60,9 +60,6 @@ const AlertContextMenuComponent: React.FC = ({ const dispatch = useDispatch(); const [, dispatchToaster] = useStateToaster(); const [isPopoverOpen, setPopover] = useState(false); - const [alertStatus, setAlertStatus] = useState( - (ecsRowData.signal?.status && (ecsRowData.signal.status[0] as Status)) ?? undefined - ); const eventId = ecsRowData._id; const ruleId = useMemo( (): string | null => @@ -90,6 +87,10 @@ const AlertContextMenuComponent: React.FC = ({ const { addWarning } = useAppToasts(); + const alertStatus = useMemo(() => { + return ecsRowData.signal?.status && (ecsRowData.signal.status[0] as Status); + }, [ecsRowData]); + const onButtonClick = useCallback(() => { setPopover(!isPopoverOpen); }, [isPopoverOpen]); @@ -122,9 +123,6 @@ const AlertContextMenuComponent: React.FC = ({ const onAddExceptionConfirm = useCallback( (didCloseAlert: boolean, didBulkCloseAlert) => { closeAddExceptionModal(); - if (didCloseAlert) { - setAlertStatus('closed'); - } if (timelineId !== TimelineId.active || didBulkCloseAlert) { refetch(); } @@ -154,7 +152,6 @@ const AlertContextMenuComponent: React.FC = ({ } displaySuccessToast(title, dispatchToaster); } - setAlertStatus(newStatus); }, [dispatchToaster, addWarning] ); @@ -359,10 +356,10 @@ const AlertContextMenuComponent: React.FC = ({ return []; } }, [ - alertStatus, closeAlertActionComponent, inProgressAlertActionComponent, openAlertActionComponent, + alertStatus, ]); const items = useMemo( From 91d73cf9814663a75e8749ed24f9303444321616 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Thu, 7 Jan 2021 15:32:01 -0800 Subject: [PATCH 24/24] skip flaky suite (#87105) --- .../apps/triggers_actions_ui/alerts_list.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index 361e5e632ecc3..36812d0cd9eef 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -53,7 +53,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('alertsTab'); } - describe('alerts list', function () { + // Failing: See https://github.com/elastic/kibana/issues/87105 + describe.skip('alerts list', function () { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('alertsTab');