diff --git a/NOTICE.txt b/NOTICE.txt index 230e511746022..955c3127fa955 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Kibana source code with Kibana X-Pack source code -Copyright 2012-2019 Elasticsearch B.V. +Copyright 2012-2020 Elasticsearch B.V. --- Pretty handling of logarithmic axes. diff --git a/src/core/public/application/integration_tests/router.test.tsx b/src/core/public/application/integration_tests/router.test.tsx index ffc10820a9c37..10544c348afb0 100644 --- a/src/core/public/application/integration_tests/router.test.tsx +++ b/src/core/public/application/integration_tests/router.test.tsx @@ -18,7 +18,7 @@ */ import React from 'react'; -import { createMemoryHistory, History } from 'history'; +import { createMemoryHistory, History, createHashHistory } from 'history'; import { AppRouter, AppNotFound } from '../ui'; import { EitherApp, MockedMounterMap, MockedMounterTuple } from '../test_types'; @@ -27,7 +27,15 @@ import { createRenderer, createAppMounter, createLegacyAppMounter } from './util describe('AppContainer', () => { let mounters: MockedMounterMap; let history: History; - let navigate: ReturnType; + let update: ReturnType; + + const navigate = (path: string) => { + history.push(path); + return update(); + }; + + const mockMountersToMounters = () => + new Map([...mounters].map(([appId, { mounter }]) => [appId, mounter])); beforeEach(() => { mounters = new Map([ @@ -38,14 +46,14 @@ describe('AppContainer', () => { createAppMounter('app3', '
App 3
', '/custom/path'), ] as Array>); history = createMemoryHistory(); - navigate = createRenderer(, history.push); + update = createRenderer(); }); it('calls mount handler and returned unmount function when navigating between apps', async () => { const dom1 = await navigate('/app/app1'); const app1 = mounters.get('app1')!; - expect(app1.mount).toHaveBeenCalled(); + expect(app1.mounter.mount).toHaveBeenCalled(); expect(dom1?.html()).toMatchInlineSnapshot(` "
basename: /app/app1 @@ -53,11 +61,11 @@ describe('AppContainer', () => {
" `); - const app1Unmount = await app1.mount.mock.results[0].value; + const app1Unmount = await app1.mounter.mount.mock.results[0].value; const dom2 = await navigate('/app/app2'); expect(app1Unmount).toHaveBeenCalled(); - expect(mounters.get('app2')!.mount).toHaveBeenCalled(); + expect(mounters.get('app2')!.mounter.mount).toHaveBeenCalled(); expect(dom2?.html()).toMatchInlineSnapshot(` "
basename: /app/app2 @@ -70,29 +78,77 @@ describe('AppContainer', () => { mounters.set(...createAppMounter('spaces', '
Custom Space
', '/spaces/fake-login')); mounters.set(...createAppMounter('login', '
Login Page
', '/fake-login')); history = createMemoryHistory(); - navigate = createRenderer(, history.push); + update = createRenderer(); await navigate('/fake-login'); - expect(mounters.get('spaces')!.mount).not.toHaveBeenCalled(); - expect(mounters.get('login')!.mount).toHaveBeenCalled(); + expect(mounters.get('spaces')!.mounter.mount).not.toHaveBeenCalled(); + expect(mounters.get('login')!.mounter.mount).toHaveBeenCalled(); }); it('should not mount when partial route path has higher specificity', async () => { mounters.set(...createAppMounter('login', '
Login Page
', '/fake-login')); mounters.set(...createAppMounter('spaces', '
Custom Space
', '/spaces/fake-login')); history = createMemoryHistory(); - navigate = createRenderer(, history.push); + update = createRenderer(); await navigate('/spaces/fake-login'); - expect(mounters.get('spaces')!.mount).toHaveBeenCalled(); - expect(mounters.get('login')!.mount).not.toHaveBeenCalled(); + expect(mounters.get('spaces')!.mounter.mount).toHaveBeenCalled(); + expect(mounters.get('login')!.mounter.mount).not.toHaveBeenCalled(); + }); + + it('should not remount when changing pages within app', async () => { + const { mounter, unmount } = mounters.get('app1')!; + await navigate('/app/app1/page1'); + expect(mounter.mount).toHaveBeenCalledTimes(1); + + // Navigating to page within app does not trigger re-render + await navigate('/app/app1/page2'); + expect(mounter.mount).toHaveBeenCalledTimes(1); + expect(unmount).not.toHaveBeenCalled(); + }); + + it('should not remount when going back within app', async () => { + const { mounter, unmount } = mounters.get('app1')!; + await navigate('/app/app1/page1'); + expect(mounter.mount).toHaveBeenCalledTimes(1); + + // Hitting back button within app does not trigger re-render + await navigate('/app/app1/page2'); + history.goBack(); + await update(); + expect(mounter.mount).toHaveBeenCalledTimes(1); + expect(unmount).not.toHaveBeenCalled(); + }); + + it('should not remount when when changing pages within app using hash history', async () => { + history = createHashHistory(); + update = createRenderer(); + + const { mounter, unmount } = mounters.get('app1')!; + await navigate('/app/app1/page1'); + expect(mounter.mount).toHaveBeenCalledTimes(1); + + // Changing hash history does not trigger re-render + await navigate('/app/app1/page2'); + expect(mounter.mount).toHaveBeenCalledTimes(1); + expect(unmount).not.toHaveBeenCalled(); + }); + + it('should unmount when changing between apps', async () => { + const { mounter, unmount } = mounters.get('app1')!; + await navigate('/app/app1/page1'); + expect(mounter.mount).toHaveBeenCalledTimes(1); + + // Navigating to other app triggers unmount + await navigate('/app/app2/page1'); + expect(unmount).toHaveBeenCalledTimes(1); }); it('calls legacy mount handler', async () => { await navigate('/app/legacyApp1'); - expect(mounters.get('legacyApp1')!.mount.mock.calls[0]).toMatchInlineSnapshot(` + expect(mounters.get('legacyApp1')!.mounter.mount.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { "appBasePath": "/app/legacyApp1", @@ -104,7 +160,7 @@ describe('AppContainer', () => { it('handles legacy apps with subapps', async () => { await navigate('/app/baseApp'); - expect(mounters.get('baseApp:legacyApp2')!.mount.mock.calls[0]).toMatchInlineSnapshot(` + expect(mounters.get('baseApp:legacyApp2')!.mounter.mount.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { "appBasePath": "/app/baseApp", diff --git a/src/core/public/application/integration_tests/utils.tsx b/src/core/public/application/integration_tests/utils.tsx index b8ade4d1d8787..6367d1fa12697 100644 --- a/src/core/public/application/integration_tests/utils.tsx +++ b/src/core/public/application/integration_tests/utils.tsx @@ -26,19 +26,13 @@ import { App, LegacyApp, AppMountParameters } from '../types'; import { MockedMounter, MockedMounterTuple } from '../test_types'; type Dom = ReturnType | null; -type Renderer = (item: string) => Dom | Promise; +type Renderer = () => Dom | Promise; -export const createRenderer = ( - element: ReactElement | null, - callback?: (item: string) => void | Promise -): Renderer => { +export const createRenderer = (element: ReactElement | null): Renderer => { const dom: Dom = element && mount({element}); - return item => + return () => new Promise(async resolve => { - if (callback) { - await callback(item); - } if (dom) { dom.update(); } @@ -50,19 +44,26 @@ export const createAppMounter = ( appId: string, html: string, appRoute = `/app/${appId}` -): MockedMounterTuple => [ - appId, - { - appRoute, - appBasePath: appRoute, - mount: jest.fn(async ({ appBasePath: basename, element }: AppMountParameters) => { - Object.assign(element, { - innerHTML: `
\nbasename: ${basename}\nhtml: ${html}\n
`, - }); - return jest.fn(() => Object.assign(element, { innerHTML: '' })); - }), - }, -]; +): MockedMounterTuple => { + const unmount = jest.fn(); + return [ + appId, + { + mounter: { + appRoute, + appBasePath: appRoute, + mount: jest.fn(async ({ appBasePath: basename, element }: AppMountParameters) => { + Object.assign(element, { + innerHTML: `
\nbasename: ${basename}\nhtml: ${html}\n
`, + }); + unmount.mockImplementation(() => Object.assign(element, { innerHTML: '' })); + return unmount; + }), + }, + unmount, + }, + ]; +}; export const createLegacyAppMounter = ( appId: string, @@ -70,9 +71,12 @@ export const createLegacyAppMounter = ( ): MockedMounterTuple => [ appId, { - appRoute: `/app/${appId.split(':')[0]}`, - appBasePath: `/app/${appId.split(':')[0]}`, - unmountBeforeMounting: true, - mount: legacyMount, + mounter: { + appRoute: `/app/${appId.split(':')[0]}`, + appBasePath: `/app/${appId.split(':')[0]}`, + unmountBeforeMounting: true, + mount: legacyMount, + }, + unmount: jest.fn(), }, ]; diff --git a/src/core/public/application/test_types.ts b/src/core/public/application/test_types.ts index f5fb639eaa32c..3d992cb950eb4 100644 --- a/src/core/public/application/test_types.ts +++ b/src/core/public/application/test_types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { App, LegacyApp, Mounter } from './types'; +import { App, LegacyApp, Mounter, AppUnmount } from './types'; import { ApplicationService } from './application_service'; /** @internal */ @@ -25,11 +25,19 @@ export type ApplicationServiceContract = PublicMethodsOf; /** @internal */ export type EitherApp = App | LegacyApp; /** @internal */ +export type MockedUnmount = jest.Mocked; +/** @internal */ export type MockedMounter = jest.Mocked>>; /** @internal */ -export type MockedMounterTuple = [string, MockedMounter]; +export type MockedMounterTuple = [ + string, + { mounter: MockedMounter; unmount: MockedUnmount } +]; /** @internal */ -export type MockedMounterMap = Map>; +export type MockedMounterMap = Map< + string, + { mounter: MockedMounter; unmount: MockedUnmount } +>; /** @internal */ export type MockLifecycle< T extends keyof ApplicationService, diff --git a/src/core/public/application/ui/app_container.tsx b/src/core/public/application/ui/app_container.tsx index 96ee91c7c21fb..153582e805fa1 100644 --- a/src/core/public/application/ui/app_container.tsx +++ b/src/core/public/application/ui/app_container.tsx @@ -65,7 +65,7 @@ export const AppContainer: FunctionComponent = ({ mounter, appId }: Props mount(); return unmount; - }); + }, [mounter]); return ( diff --git a/src/core/server/rendering/views/styles.tsx b/src/core/server/rendering/views/styles.tsx index 40261321dcffc..f41627bcfe07f 100644 --- a/src/core/server/rendering/views/styles.tsx +++ b/src/core/server/rendering/views/styles.tsx @@ -78,7 +78,7 @@ export const Styles: FunctionComponent = ({ darkMode }) => { background-repeat: no-repeat; background-size: contain; /* SVG optimized according to http://codepen.io/tigt/post/optimizing-svgs-in-data-uris */ - background-image: url'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMCIgaGVpZ2h0PSIzOSIgdmlld0JveD0iMCAwIDMwIDM5Ij4gIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgIDxwb2x5Z29uIGZpbGw9IiNGMDRFOTgiIHBvaW50cz0iMCAwIDAgMzQuNTQ3IDI5LjkyMiAuMDIiLz4gICAgPHBhdGggZmlsbD0iIzM0Mzc0MSIgZD0iTTAsMTQuNCBMMCwzNC41NDY4IEwxNC4yODcyLDE4LjA2MTIgQzEwLjA0MTYsMTUuNzM4IDUuMTgwNCwxNC40IDAsMTQuNCIvPiAgICA8cGF0aCBmaWxsPSIjMDBCRkIzIiBkPSJNMTcuMzc0MiwxOS45OTY4IEwyLjcyMSwzNi45MDQ4IEwxLjQzMzQsMzguMzg5MiBMMjkuMjYzOCwzOC4zODkyIEMyNy43NjE0LDMwLjgzODggMjMuNDA0MiwyNC4zMjY0IDE3LjM3NDIsMTkuOTk2OCIvPiAgPC9nPjwvc3ZnPg=='); + background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMCIgaGVpZ2h0PSIzOSIgdmlld0JveD0iMCAwIDMwIDM5Ij4gIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgIDxwb2x5Z29uIGZpbGw9IiNGMDRFOTgiIHBvaW50cz0iMCAwIDAgMzQuNTQ3IDI5LjkyMiAuMDIiLz4gICAgPHBhdGggZmlsbD0iIzM0Mzc0MSIgZD0iTTAsMTQuNCBMMCwzNC41NDY4IEwxNC4yODcyLDE4LjA2MTIgQzEwLjA0MTYsMTUuNzM4IDUuMTgwNCwxNC40IDAsMTQuNCIvPiAgICA8cGF0aCBmaWxsPSIjMDBCRkIzIiBkPSJNMTcuMzc0MiwxOS45OTY4IEwyLjcyMSwzNi45MDQ4IEwxLjQzMzQsMzguMzg5MiBMMjkuMjYzOCwzOC4zODkyIEMyNy43NjE0LDMwLjgzODggMjMuNDA0MiwyNC4zMjY0IDE3LjM3NDIsMTkuOTk2OCIvPiAgPC9nPjwvc3ZnPg=='); } .kibanaWelcomeTitle { diff --git a/src/legacy/core_plugins/tile_map/index.ts b/src/legacy/core_plugins/tile_map/index.ts index 298675e75b0d7..27f019318a82b 100644 --- a/src/legacy/core_plugins/tile_map/index.ts +++ b/src/legacy/core_plugins/tile_map/index.ts @@ -30,7 +30,14 @@ const tileMapPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPlu uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({}), + injectDefaultVars: server => { + const serverConfig = server.config(); + const mapConfig: Record = serverConfig.get('map'); + + return { + emsTileLayerId: mapConfig.emsTileLayerId, + }; + }, }, config(Joi: any) { return Joi.object({ diff --git a/src/legacy/core_plugins/vis_type_vega/index.ts b/src/legacy/core_plugins/vis_type_vega/index.ts index 153cd6afb3ccc..52c253c6ac0b5 100644 --- a/src/legacy/core_plugins/vis_type_vega/index.ts +++ b/src/legacy/core_plugins/vis_type_vega/index.ts @@ -33,9 +33,15 @@ const vegaPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPlugin uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({ - enableExternalUrls: server.config().get('vega.enableExternalUrls'), - }), + injectDefaultVars: server => { + const serverConfig = server.config(); + const mapConfig: Record = serverConfig.get('map'); + + return { + emsTileLayerId: mapConfig.emsTileLayerId, + enableExternalUrls: serverConfig.get('vega.enableExternalUrls'), + }; + }, }, init: (server: Legacy.Server) => ({}), config(Joi: any) { diff --git a/test/functional/apps/home/_newsfeed.ts b/test/functional/apps/home/_newsfeed.ts index 35d7ac8adefa5..0019b817b72d8 100644 --- a/test/functional/apps/home/_newsfeed.ts +++ b/test/functional/apps/home/_newsfeed.ts @@ -24,7 +24,8 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const globalNav = getService('globalNav'); const PageObjects = getPageObjects(['common', 'newsfeed']); - describe('Newsfeed', () => { + // Failing: https://github.com/elastic/kibana/issues/53860 + describe.skip('Newsfeed', () => { before(async () => { await PageObjects.newsfeed.resetPage(); }); diff --git a/vars/githubPr.groovy b/vars/githubPr.groovy index 09a166192bf7a..ce164ab98ab1e 100644 --- a/vars/githubPr.groovy +++ b/vars/githubPr.groovy @@ -35,7 +35,7 @@ def withDefaultPrComments(closure) { def message = getNextCommentMessage(info) postComment(message) - if (lastComment) { + if (lastComment && lastComment.user.login == 'kibanamachine') { deleteComment(lastComment.id) } } @@ -49,7 +49,7 @@ def isPr() { def getLatestBuildComment() { return getComments() .reverse() - .find { it.user.login == 'elasticmachine' && it.body =~ /