diff --git a/docs/development/core/public/kibana-plugin-public.app.md b/docs/development/core/public/kibana-plugin-public.app.md index c500c080a5feba..edab4f88497f64 100644 --- a/docs/development/core/public/kibana-plugin-public.app.md +++ b/docs/development/core/public/kibana-plugin-public.app.md @@ -17,5 +17,5 @@ export interface App extends AppBase | Property | Type | Description | | --- | --- | --- | | [chromeless](./kibana-plugin-public.app.chromeless.md) | boolean | Hide the UI chrome when the application is mounted. Defaults to false. Takes precedence over chrome service visibility settings. | -| [mount](./kibana-plugin-public.app.mount.md) | (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise<AppUnmount> | A mount function called when the user navigates to this app's route. | +| [mount](./kibana-plugin-public.app.mount.md) | AppMount | AppMountDeprecated | A mount function called when the user navigates to this app's route. May have signature of [AppMount](./kibana-plugin-public.appmount.md) or [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md). | diff --git a/docs/development/core/public/kibana-plugin-public.app.mount.md b/docs/development/core/public/kibana-plugin-public.app.mount.md index dda06b035db4a0..151fb7baeb138f 100644 --- a/docs/development/core/public/kibana-plugin-public.app.mount.md +++ b/docs/development/core/public/kibana-plugin-public.app.mount.md @@ -4,10 +4,15 @@ ## App.mount property -A mount function called when the user navigates to this app's route. +A mount function called when the user navigates to this app's route. May have signature of [AppMount](./kibana-plugin-public.appmount.md) or [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md). Signature: ```typescript -mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise; +mount: AppMount | AppMountDeprecated; ``` + +## Remarks + +When function has two arguments, it will be called with a [context](./kibana-plugin-public.appmountcontext.md) as the first argument. This behavior is \*\*deprecated\*\*, and consumers should instead use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). + diff --git a/docs/development/core/public/kibana-plugin-public.applicationsetup.md b/docs/development/core/public/kibana-plugin-public.applicationsetup.md index b53873bc0fb8ae..a63de399c2ecb4 100644 --- a/docs/development/core/public/kibana-plugin-public.applicationsetup.md +++ b/docs/development/core/public/kibana-plugin-public.applicationsetup.md @@ -16,5 +16,5 @@ export interface ApplicationSetup | Method | Description | | --- | --- | | [register(app)](./kibana-plugin-public.applicationsetup.register.md) | Register an mountable application to the system. | -| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationsetup.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. | +| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationsetup.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). | diff --git a/docs/development/core/public/kibana-plugin-public.applicationsetup.registermountcontext.md b/docs/development/core/public/kibana-plugin-public.applicationsetup.registermountcontext.md index f264ba500ed6ef..275ba431bc7e7c 100644 --- a/docs/development/core/public/kibana-plugin-public.applicationsetup.registermountcontext.md +++ b/docs/development/core/public/kibana-plugin-public.applicationsetup.registermountcontext.md @@ -4,12 +4,16 @@ ## ApplicationSetup.registerMountContext() method -Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. +> Warning: This API is now obsolete. +> +> + +Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). Signature: ```typescript -registerMountContext(contextName: T, provider: IContextProvider): void; +registerMountContext(contextName: T, provider: IContextProvider): void; ``` ## Parameters @@ -17,7 +21,7 @@ registerMountContext(contextName: T, provider: | Parameter | Type | Description | | --- | --- | --- | | contextName | T | The key of [AppMountContext](./kibana-plugin-public.appmountcontext.md) this provider's return value should be attached to. | -| provider | IContextProvider<App['mount'], T> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function | +| provider | IContextProvider<AppMountDeprecated, T> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function | Returns: diff --git a/docs/development/core/public/kibana-plugin-public.applicationstart.md b/docs/development/core/public/kibana-plugin-public.applicationstart.md index 2a60ff449e44ea..4baa4565ff7b06 100644 --- a/docs/development/core/public/kibana-plugin-public.applicationstart.md +++ b/docs/development/core/public/kibana-plugin-public.applicationstart.md @@ -23,5 +23,5 @@ export interface ApplicationStart | --- | --- | | [getUrlForApp(appId, options)](./kibana-plugin-public.applicationstart.geturlforapp.md) | Returns a relative URL to a given app, including the global base path. | | [navigateToApp(appId, options)](./kibana-plugin-public.applicationstart.navigatetoapp.md) | Navigiate to a given app | -| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationstart.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. | +| [registerMountContext(contextName, provider)](./kibana-plugin-public.applicationstart.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). | diff --git a/docs/development/core/public/kibana-plugin-public.applicationstart.registermountcontext.md b/docs/development/core/public/kibana-plugin-public.applicationstart.registermountcontext.md index 62821fcbb92bad..c15a23fe82b21d 100644 --- a/docs/development/core/public/kibana-plugin-public.applicationstart.registermountcontext.md +++ b/docs/development/core/public/kibana-plugin-public.applicationstart.registermountcontext.md @@ -4,12 +4,16 @@ ## ApplicationStart.registerMountContext() method -Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. +> Warning: This API is now obsolete. +> +> + +Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). Signature: ```typescript -registerMountContext(contextName: T, provider: IContextProvider): void; +registerMountContext(contextName: T, provider: IContextProvider): void; ``` ## Parameters @@ -17,7 +21,7 @@ registerMountContext(contextName: T, provider: | Parameter | Type | Description | | --- | --- | --- | | contextName | T | The key of [AppMountContext](./kibana-plugin-public.appmountcontext.md) this provider's return value should be attached to. | -| provider | IContextProvider<App['mount'], T> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function | +| provider | IContextProvider<AppMountDeprecated, T> | A [IContextProvider](./kibana-plugin-public.icontextprovider.md) function | Returns: diff --git a/docs/development/core/public/kibana-plugin-public.appmount.md b/docs/development/core/public/kibana-plugin-public.appmount.md new file mode 100644 index 00000000000000..25faa7be30b68f --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appmount.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppMount](./kibana-plugin-public.appmount.md) + +## AppMount type + +A mount function called when the user navigates to this app's route. + +Signature: + +```typescript +export declare type AppMount = (params: AppMountParameters) => AppUnmount | Promise; +``` diff --git a/docs/development/core/public/kibana-plugin-public.appmountcontext.md b/docs/development/core/public/kibana-plugin-public.appmountcontext.md index 68a1c27b118366..2f8c0553d0b382 100644 --- a/docs/development/core/public/kibana-plugin-public.appmountcontext.md +++ b/docs/development/core/public/kibana-plugin-public.appmountcontext.md @@ -4,7 +4,11 @@ ## AppMountContext interface -The context object received when applications are mounted to the DOM. +> Warning: This API is now obsolete. +> +> + +The context object received when applications are mounted to the DOM. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). Signature: diff --git a/docs/development/core/public/kibana-plugin-public.appmountdeprecated.md b/docs/development/core/public/kibana-plugin-public.appmountdeprecated.md new file mode 100644 index 00000000000000..936642abcc97a3 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appmountdeprecated.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md) + +## AppMountDeprecated type + +> Warning: This API is now obsolete. +> +> + +A mount function called when the user navigates to this app's route. + +Signature: + +```typescript +export declare type AppMountDeprecated = (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise; +``` + +## Remarks + +When function has two arguments, it will be called with a [context](./kibana-plugin-public.appmountcontext.md) as the first argument. This behavior is \*\*deprecated\*\*, and consumers should instead use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). + diff --git a/docs/development/core/public/kibana-plugin-public.coresetup.context.md b/docs/development/core/public/kibana-plugin-public.coresetup.context.md index e56ecb92074c48..f2a891c6c674eb 100644 --- a/docs/development/core/public/kibana-plugin-public.coresetup.context.md +++ b/docs/development/core/public/kibana-plugin-public.coresetup.context.md @@ -4,6 +4,10 @@ ## CoreSetup.context property +> Warning: This API is now obsolete. +> +> + [ContextSetup](./kibana-plugin-public.contextsetup.md) Signature: diff --git a/docs/development/core/public/kibana-plugin-public.coresetup.getstartservices.md b/docs/development/core/public/kibana-plugin-public.coresetup.getstartservices.md new file mode 100644 index 00000000000000..b89d98b0a9ed53 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.coresetup.getstartservices.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [CoreSetup](./kibana-plugin-public.coresetup.md) > [getStartServices](./kibana-plugin-public.coresetup.getstartservices.md) + +## CoreSetup.getStartServices() method + +Allows plugins to get access to APIs available in start inside async handlers, such as [App.mount](./kibana-plugin-public.app.mount.md). Promise will not resolve until Core and plugin dependencies have completed `start`. + +Signature: + +```typescript +getStartServices(): Promise<[CoreStart, TPluginsStart]>; +``` +Returns: + +`Promise<[CoreStart, TPluginsStart]>` + diff --git a/docs/development/core/public/kibana-plugin-public.coresetup.md b/docs/development/core/public/kibana-plugin-public.coresetup.md index 8314bde7b95f01..7d75782df2e321 100644 --- a/docs/development/core/public/kibana-plugin-public.coresetup.md +++ b/docs/development/core/public/kibana-plugin-public.coresetup.md @@ -9,7 +9,7 @@ Core services exposed to the `Plugin` setup lifecycle Signature: ```typescript -export interface CoreSetup +export interface CoreSetup ``` ## Properties @@ -24,3 +24,9 @@ export interface CoreSetup | [notifications](./kibana-plugin-public.coresetup.notifications.md) | NotificationsSetup | [NotificationsSetup](./kibana-plugin-public.notificationssetup.md) | | [uiSettings](./kibana-plugin-public.coresetup.uisettings.md) | IUiSettingsClient | [IUiSettingsClient](./kibana-plugin-public.iuisettingsclient.md) | +## Methods + +| Method | Description | +| --- | --- | +| [getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers, such as [App.mount](./kibana-plugin-public.app.mount.md). Promise will not resolve until Core and plugin dependencies have completed start. | + diff --git a/docs/development/core/public/kibana-plugin-public.legacycoresetup.md b/docs/development/core/public/kibana-plugin-public.legacycoresetup.md index f704bc65d12a52..a753300437c1c8 100644 --- a/docs/development/core/public/kibana-plugin-public.legacycoresetup.md +++ b/docs/development/core/public/kibana-plugin-public.legacycoresetup.md @@ -13,7 +13,7 @@ Setup interface exposed to the legacy platform via the `ui/new_platform` module. Signature: ```typescript -export interface LegacyCoreSetup extends CoreSetup +export interface LegacyCoreSetup extends CoreSetup ``` ## Properties diff --git a/docs/development/core/public/kibana-plugin-public.md b/docs/development/core/public/kibana-plugin-public.md index 22b6f7faf2daab..c599e1eaa14fe9 100644 --- a/docs/development/core/public/kibana-plugin-public.md +++ b/docs/development/core/public/kibana-plugin-public.md @@ -26,7 +26,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AppBase](./kibana-plugin-public.appbase.md) | | | [ApplicationSetup](./kibana-plugin-public.applicationsetup.md) | | | [ApplicationStart](./kibana-plugin-public.applicationstart.md) | | -| [AppMountContext](./kibana-plugin-public.appmountcontext.md) | The context object received when applications are mounted to the DOM. | +| [AppMountContext](./kibana-plugin-public.appmountcontext.md) | The context object received when applications are mounted to the DOM. Deprecated, use [CoreSetup.getStartServices()](./kibana-plugin-public.coresetup.getstartservices.md). | | [AppMountParameters](./kibana-plugin-public.appmountparameters.md) | | | [Capabilities](./kibana-plugin-public.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. | | [ChromeBadge](./kibana-plugin-public.chromebadge.md) | | @@ -98,6 +98,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | Type Alias | Description | | --- | --- | +| [AppMount](./kibana-plugin-public.appmount.md) | A mount function called when the user navigates to this app's route. | +| [AppMountDeprecated](./kibana-plugin-public.appmountdeprecated.md) | A mount function called when the user navigates to this app's route. | | [AppUnmount](./kibana-plugin-public.appunmount.md) | A function called when an application should be unmounted from the page. This function should be synchronous. | | [ChromeBreadcrumb](./kibana-plugin-public.chromebreadcrumb.md) | | | [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-public.chromehelpextensionmenucustomlink.md) | | diff --git a/docs/development/core/public/kibana-plugin-public.plugin.setup.md b/docs/development/core/public/kibana-plugin-public.plugin.setup.md index 56855b02cfbad4..f058bc8d86fbce 100644 --- a/docs/development/core/public/kibana-plugin-public.plugin.setup.md +++ b/docs/development/core/public/kibana-plugin-public.plugin.setup.md @@ -7,14 +7,14 @@ Signature: ```typescript -setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; +setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| core | CoreSetup | | +| core | CoreSetup<TPluginsStart> | | | plugins | TPluginsSetup | | Returns: diff --git a/src/core/public/application/application_service.test.tsx b/src/core/public/application/application_service.test.tsx index 19a208aeefb372..32634572466a67 100644 --- a/src/core/public/application/application_service.test.tsx +++ b/src/core/public/application/application_service.test.tsx @@ -32,9 +32,9 @@ describe('#setup()', () => { const service = new ApplicationService(); const context = contextServiceMock.createSetupContract(); const setup = service.setup({ context }); - setup.register(Symbol(), { id: 'app1' } as any); + setup.register(Symbol(), { id: 'app1', mount: jest.fn() } as any); expect(() => - setup.register(Symbol(), { id: 'app1' } as any) + setup.register(Symbol(), { id: 'app1', mount: jest.fn() } as any) ).toThrowErrorMatchingInlineSnapshot( `"An application is already registered with the id \\"app1\\""` ); @@ -51,6 +51,18 @@ describe('#setup()', () => { setup.register(Symbol(), { id: 'app1' } as any) ).toThrowErrorMatchingInlineSnapshot(`"Applications cannot be registered after \\"setup\\""`); }); + + it('logs a warning when registering a deprecated app mount', async () => { + const consoleWarnSpy = jest.spyOn(console, 'warn'); + const service = new ApplicationService(); + const context = contextServiceMock.createSetupContract(); + const setup = service.setup({ context }); + setup.register(Symbol(), { id: 'app1', mount: (ctx: any, params: any) => {} } as any); + expect(consoleWarnSpy).toHaveBeenCalledWith( + `App [app1] is using deprecated mount context. Use core.getStartServices() instead.` + ); + consoleWarnSpy.mockRestore(); + }); }); describe('registerLegacyApp', () => { @@ -100,7 +112,7 @@ describe('#start()', () => { const service = new ApplicationService(); const context = contextServiceMock.createSetupContract(); const setup = service.setup({ context }); - setup.register(Symbol(), { id: 'app1' } as any); + setup.register(Symbol(), { id: 'app1', mount: jest.fn() } as any); setup.registerLegacyApp({ id: 'app2' } as any); const http = httpServiceMock.createStartContract(); @@ -108,12 +120,13 @@ describe('#start()', () => { const startContract = await service.start({ http, injectedMetadata }); expect(startContract.availableApps).toMatchInlineSnapshot(` - Map { - "app1" => Object { - "id": "app1", - }, - } - `); + Map { + "app1" => Object { + "id": "app1", + "mount": [MockFunction], + }, + } + `); expect(startContract.availableLegacyApps).toMatchInlineSnapshot(` Map { "app2" => Object { @@ -127,14 +140,15 @@ describe('#start()', () => { const service = new ApplicationService(); const context = contextServiceMock.createSetupContract(); const setup = service.setup({ context }); - setup.register(Symbol(), { id: 'app1' } as any); + const app1 = { id: 'app1', mount: jest.fn() }; + setup.register(Symbol(), app1 as any); const http = httpServiceMock.createStartContract(); const injectedMetadata = injectedMetadataServiceMock.createStartContract(); await service.start({ http, injectedMetadata }); expect(MockCapabilitiesService.start).toHaveBeenCalledWith({ - apps: new Map([['app1', { id: 'app1' }]]), + apps: new Map([['app1', app1]]), legacyApps: new Map(), http, }); diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index 45ca7f3fe7f7b9..df00c84028e6f4 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -29,7 +29,8 @@ import { ContextSetup, IContextContainer } from '../context'; import { App, LegacyApp, - AppMounter, + AppMount, + AppMountDeprecated, InternalApplicationSetup, InternalApplicationStart, } from './types'; @@ -50,7 +51,7 @@ interface StartDeps { interface AppBox { app: App; - mount: AppMounter; + mount: AppMount; } /** @@ -61,7 +62,7 @@ export class ApplicationService { private readonly apps$ = new BehaviorSubject>(new Map()); private readonly legacyApps$ = new BehaviorSubject>(new Map()); private readonly capabilities = new CapabilitiesService(); - private mountContext?: IContextContainer; + private mountContext?: IContextContainer; public setup({ context }: SetupDeps): InternalApplicationSetup { this.mountContext = context.createContextContainer(); @@ -75,10 +76,21 @@ export class ApplicationService { throw new Error(`Applications cannot be registered after "setup"`); } - const appBox: AppBox = { - app, - mount: this.mountContext!.createHandler(plugin, app.mount), - }; + let appBox: AppBox; + if (isAppMountDeprecated(app.mount)) { + // eslint-disable-next-line no-console + console.warn( + `App [${app.id}] is using deprecated mount context. Use core.getStartServices() instead.` + ); + + appBox = { + app, + mount: this.mountContext!.createHandler(plugin, app.mount), + }; + } else { + appBox = { app, mount: app.mount }; + } + this.apps$.next(new Map([...this.apps$.value.entries(), [app.id, appBox]])); }, registerLegacyApp: (app: LegacyApp) => { @@ -146,7 +158,7 @@ export class ApplicationService { } // Filter only available apps and map to just the mount function. - const appMounters = new Map( + const appMounts = new Map( [...this.apps$.value] .filter(([id]) => availableApps.has(id)) .map(([id, { mount }]) => [id, mount]) @@ -154,7 +166,7 @@ export class ApplicationService { return ( path ? `/app/${appId}/${path.replace(/^\//, '')}` // Remove preceding slash from path if present : `/app/${appId}`; + +function isAppMountDeprecated(mount: (...args: any[]) => any): mount is AppMountDeprecated { + // Mount functions with two arguments are assumed to expect deprecated `context` object. + return mount.length === 2; +} diff --git a/src/core/public/application/index.ts b/src/core/public/application/index.ts index ae25b54cf07a84..9c4427c772a5ea 100644 --- a/src/core/public/application/index.ts +++ b/src/core/public/application/index.ts @@ -22,6 +22,8 @@ export { Capabilities } from './capabilities'; export { App, AppBase, + AppMount, + AppMountDeprecated, AppUnmount, AppMountContext, AppMountParameters, diff --git a/src/core/public/application/integration_tests/router.test.tsx b/src/core/public/application/integration_tests/router.test.tsx index 593858851d3872..81aef5204c7e29 100644 --- a/src/core/public/application/integration_tests/router.test.tsx +++ b/src/core/public/application/integration_tests/router.test.tsx @@ -24,7 +24,7 @@ import { BehaviorSubject } from 'rxjs'; import { I18nProvider } from '@kbn/i18n/react'; -import { AppMounter, LegacyApp, AppMountParameters } from '../types'; +import { AppMount, LegacyApp, AppMountParameters } from '../types'; import { httpServiceMock } from '../../http/http_service.mock'; import { AppRouter, AppNotFound } from '../ui'; @@ -35,7 +35,7 @@ const createMountHandler = (htmlString: string) => }); describe('AppContainer', () => { - let apps: Map, Parameters>>; + let apps: Map, Parameters>>; let legacyApps: Map; let history: History; let router: ReactWrapper; diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index a031ab60704134..72460b07900da2 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -75,12 +75,14 @@ export interface AppBase { */ export interface App extends AppBase { /** - * A mount function called when the user navigates to this app's route. - * @param context The mount context for this app. - * @param targetDomElement An HTMLElement to mount the application onto. - * @returns An unmounting function that will be called to unmount the application. + * A mount function called when the user navigates to this app's route. May have signature of {@link AppMount} or + * {@link AppMountDeprecated}. + * + * @remarks + * When function has two arguments, it will be called with a {@link AppMountContext | context} as the first argument. + * This behavior is **deprecated**, and consumers should instead use {@link CoreSetup.getStartServices}. */ - mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise; + mount: AppMount | AppMountDeprecated; /** * Hide the UI chrome when the application is mounted. Defaults to `false`. @@ -97,7 +99,39 @@ export interface LegacyApp extends AppBase { } /** - * The context object received when applications are mounted to the DOM. + * A mount function called when the user navigates to this app's route. + * + * @param params {@link AppMountParameters} + * @returns An unmounting function that will be called to unmount the application. See {@link AppUnmount}. + * + * @public + */ +export type AppMount = (params: AppMountParameters) => AppUnmount | Promise; + +/** + * A mount function called when the user navigates to this app's route. + * + * @remarks + * When function has two arguments, it will be called with a {@link AppMountContext | context} as the first argument. + * This behavior is **deprecated**, and consumers should instead use {@link CoreSetup.getStartServices}. + * + * @param context The mount context for this app. Deprecated, use {@link CoreSetup.getStartServices}. + * @param params {@link AppMountParameters} + * @returns An unmounting function that will be called to unmount the application. See {@link AppUnmount}. + * + * @deprecated + * @public + */ +export type AppMountDeprecated = ( + context: AppMountContext, + params: AppMountParameters +) => AppUnmount | Promise; + +/** + * The context object received when applications are mounted to the DOM. Deprecated, use + * {@link CoreSetup.getStartServices}. + * + * @deprecated * @public */ export interface AppMountContext { @@ -192,9 +226,6 @@ export interface AppMountParameters { */ export type AppUnmount = () => void; -/** @internal */ -export type AppMounter = (params: AppMountParameters) => Promise; - /** @public */ export interface ApplicationSetup { /** @@ -205,14 +236,15 @@ export interface ApplicationSetup { /** * Register a context provider for application mounting. Will only be available to applications that depend on the - * plugin that registered this context. + * plugin that registered this context. Deprecated, use {@link CoreSetup.getStartServices}. * + * @deprecated * @param contextName - The key of {@link AppMountContext} this provider's return value should be attached to. * @param provider - A {@link IContextProvider} function */ registerMountContext( contextName: T, - provider: IContextProvider + provider: IContextProvider ): void; } @@ -234,8 +266,9 @@ export interface InternalApplicationSetup { /** * Register a context provider for application mounting. Will only be available to applications that depend on the - * plugin that registered this context. + * plugin that registered this context. Deprecated, use {@link CoreSetup.getStartServices}. * + * @deprecated * @param pluginOpaqueId - The opaque ID of the plugin that is registering the context. * @param contextName - The key of {@link AppMountContext} this provider's return value should be attached to. * @param provider - A {@link IContextProvider} function @@ -243,7 +276,7 @@ export interface InternalApplicationSetup { registerMountContext( pluginOpaqueId: PluginOpaqueId, contextName: T, - provider: IContextProvider + provider: IContextProvider ): void; } @@ -272,15 +305,16 @@ export interface ApplicationStart { /** * Register a context provider for application mounting. Will only be available to applications that depend on the - * plugin that registered this context. + * plugin that registered this context. Deprecated, use {@link CoreSetup.getStartServices}. * + * @deprecated * @param pluginOpaqueId - The opaque ID of the plugin that is registering the context. * @param contextName - The key of {@link AppMountContext} this provider's return value should be attached to. * @param provider - A {@link IContextProvider} function */ registerMountContext( contextName: T, - provider: IContextProvider + provider: IContextProvider ): void; } @@ -301,8 +335,9 @@ export interface InternalApplicationStart /** * Register a context provider for application mounting. Will only be available to applications that depend on the - * plugin that registered this context. + * plugin that registered this context. Deprecated, use {@link CoreSetup.getStartServices}. * + * @deprecated * @param pluginOpaqueId - The opaque ID of the plugin that is registering the context. * @param contextName - The key of {@link AppMountContext} this provider's return value should be attached to. * @param provider - A {@link IContextProvider} function @@ -310,7 +345,7 @@ export interface InternalApplicationStart registerMountContext( pluginOpaqueId: PluginOpaqueId, contextName: T, - provider: IContextProvider + provider: IContextProvider ): void; // Internal APIs diff --git a/src/core/public/application/ui/app_container.tsx b/src/core/public/application/ui/app_container.tsx index 876cd3aa3a3d3a..9c2bb30e795032 100644 --- a/src/core/public/application/ui/app_container.tsx +++ b/src/core/public/application/ui/app_container.tsx @@ -21,12 +21,12 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { Subject } from 'rxjs'; -import { LegacyApp, AppMounter, AppUnmount } from '../types'; +import { LegacyApp, AppMount, AppUnmount } from '../types'; import { HttpStart } from '../../http'; import { AppNotFound } from './app_not_found_screen'; interface Props extends RouteComponentProps<{ appId: string }> { - apps: ReadonlyMap; + apps: ReadonlyMap; legacyApps: ReadonlyMap; basePath: HttpStart['basePath']; currentAppId$: Subject; diff --git a/src/core/public/application/ui/app_router.tsx b/src/core/public/application/ui/app_router.tsx index b574bf16278e2a..67701a33dabf41 100644 --- a/src/core/public/application/ui/app_router.tsx +++ b/src/core/public/application/ui/app_router.tsx @@ -22,12 +22,12 @@ import React from 'react'; import { Router, Route } from 'react-router-dom'; import { Subject } from 'rxjs'; -import { LegacyApp, AppMounter } from '../types'; +import { LegacyApp, AppMount } from '../types'; import { AppContainer } from './app_container'; import { HttpStart } from '../../http'; interface Props { - apps: ReadonlyMap; + apps: ReadonlyMap; legacyApps: ReadonlyMap; basePath: HttpStart['basePath']; currentAppId$: Subject; diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 4818484b00819c..abc4c144356e8e 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -64,7 +64,7 @@ export interface CoreContext { } /** @internal */ -export interface InternalCoreSetup extends Omit { +export interface InternalCoreSetup extends Omit { application: InternalApplicationSetup; injectedMetadata: InjectedMetadataSetup; } @@ -253,11 +253,11 @@ export class CoreSystem { docLinks, http, i18n, + injectedMetadata: pick(injectedMetadata, ['getInjectedVar']), notifications, overlays, savedObjects, uiSettings, - injectedMetadata: pick(injectedMetadata, ['getInjectedVar']), })); const core: InternalCoreStart = { diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 035cbcca86ac72..f53bf44bcdcfd0 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -79,7 +79,17 @@ import { export { CoreContext, CoreSystem } from './core_system'; export { RecursiveReadonly } from '../utils'; -export { App, AppBase, AppUnmount, AppMountContext, AppMountParameters } from './application'; +export { + ApplicationSetup, + ApplicationStart, + App, + AppBase, + AppMount, + AppMountDeprecated, + AppUnmount, + AppMountContext, + AppMountParameters, +} from './application'; export { SavedObjectsBatchResponse, @@ -145,10 +155,13 @@ export { MountPoint, UnmountCallback } from './types'; * navigation in the generated docs until there's a fix for * https://github.com/Microsoft/web-build-tools/issues/1237 */ -export interface CoreSetup { +export interface CoreSetup { /** {@link ApplicationSetup} */ application: ApplicationSetup; - /** {@link ContextSetup} */ + /** + * {@link ContextSetup} + * @deprecated + */ context: ContextSetup; /** {@link FatalErrorsSetup} */ fatalErrors: FatalErrorsSetup; @@ -167,6 +180,13 @@ export interface CoreSetup { injectedMetadata: { getInjectedVar: (name: string, defaultValue?: any) => unknown; }; + + /** + * Allows plugins to get access to APIs available in start inside async + * handlers, such as {@link App.mount}. Promise will not resolve until Core + * and plugin dependencies have completed `start`. + */ + getStartServices(): Promise<[CoreStart, TPluginsStart]>; } /** @@ -218,7 +238,7 @@ export interface CoreStart { * @public * @deprecated */ -export interface LegacyCoreSetup extends CoreSetup { +export interface LegacyCoreSetup extends CoreSetup { /** @deprecated */ injectedMetadata: InjectedMetadataSetup; } @@ -239,8 +259,6 @@ export interface LegacyCoreStart extends CoreStart { } export { - ApplicationSetup, - ApplicationStart, Capabilities, ChromeBadge, ChromeBrand, diff --git a/src/core/public/legacy/legacy_service.test.ts b/src/core/public/legacy/legacy_service.test.ts index 37e07af0a7da58..9dd24f9e4a7a3c 100644 --- a/src/core/public/legacy/legacy_service.test.ts +++ b/src/core/public/legacy/legacy_service.test.ts @@ -169,6 +169,20 @@ describe('#start()', () => { expect(mockUiNewPlatformStart).toHaveBeenCalledWith(expect.any(Object), {}); }); + it('resolves getStartServices with core and plugin APIs', async () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + }); + + legacyPlatform.setup(defaultSetupDeps); + legacyPlatform.start(defaultStartDeps); + + const { getStartServices } = mockUiNewPlatformSetup.mock.calls[0][0]; + const [coreStart, pluginsStart] = await getStartServices(); + expect(coreStart).toEqual(expect.any(Object)); + expect(pluginsStart).toBe(defaultStartDeps.plugins); + }); + describe('useLegacyTestHarness = false', () => { it('passes the targetDomElement to ui/chrome', () => { const legacyPlatform = new LegacyPlatformService({ diff --git a/src/core/public/legacy/legacy_service.ts b/src/core/public/legacy/legacy_service.ts index 22e315f9e1b030..a4fdd86de53112 100644 --- a/src/core/public/legacy/legacy_service.ts +++ b/src/core/public/legacy/legacy_service.ts @@ -18,6 +18,8 @@ */ import angular from 'angular'; +import { first } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { InternalCoreSetup, InternalCoreStart } from '../core_system'; import { LegacyCoreSetup, LegacyCoreStart, MountPoint } from '../'; @@ -55,6 +57,8 @@ export class LegacyPlatformService { public readonly legacyId = Symbol(); private bootstrapModule?: BootstrapModule; private targetDomElement?: HTMLElement; + private readonly startDependencies$ = new Subject<[LegacyCoreStart, object]>(); + private readonly startDependencies = this.startDependencies$.pipe(first()).toPromise(); constructor(private readonly params: LegacyPlatformParams) {} @@ -75,6 +79,7 @@ export class LegacyPlatformService { const legacyCore: LegacyCoreSetup = { ...core, + getStartServices: () => this.startDependencies, application: { register: notSupported(`core.application.register()`), registerMountContext: notSupported(`core.application.registerMountContext()`), @@ -120,6 +125,8 @@ export class LegacyPlatformService { }, }; + this.startDependencies$.next([legacyCore, plugins]); + // Inject parts of the new platform into parts of the legacy platform // so that legacy APIs/modules can mimic their new platform counterparts require('ui/new_platform').__start__(legacyCore, plugins); diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index 644df259b8e242..43c8aa6f1d6b96 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -46,6 +46,9 @@ function createCoreSetupMock({ basePath = '' } = {}) { application: applicationServiceMock.createSetupContract(), context: contextServiceMock.createSetupContract(), fatalErrors: fatalErrorsServiceMock.createSetupContract(), + getStartServices: jest.fn, object]>, []>(() => + Promise.resolve([createCoreStartMock({ basePath }), {}]) + ), http: httpServiceMock.createSetupContract({ basePath }), notifications: notificationServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), @@ -75,6 +78,7 @@ function createCoreStartMock({ basePath = '' } = {}) { return mock; } + function pluginInitializerContextMock() { const mock: PluginInitializerContext = { opaqueId: Symbol(), diff --git a/src/core/public/plugins/plugin.test.ts b/src/core/public/plugins/plugin.test.ts index 85de5c6620cc16..111ee93dd699b3 100644 --- a/src/core/public/plugins/plugin.test.ts +++ b/src/core/public/plugins/plugin.test.ts @@ -106,6 +106,33 @@ describe('PluginWrapper', () => { expect(mockPlugin.start).toHaveBeenCalledWith(context, deps); }); + test("`start` resolves `startDependencies` Promise after plugin's start", async () => { + expect.assertions(2); + + let startDependenciesResolved = false; + mockPluginLoader.mockResolvedValueOnce(() => ({ + setup: jest.fn(), + start: async () => { + // Add small delay to ensure startDependencies is not resolved until after the plugin instance's start resolves. + await new Promise(resolve => setTimeout(resolve, 10)); + expect(startDependenciesResolved).toBe(false); + }, + })); + await plugin.load(addBasePath); + await plugin.setup({} as any, {} as any); + const context = { any: 'thing' } as any; + const deps = { otherDep: 'value' }; + + // Add promise callback prior to calling `start` to ensure calls in `setup` will not resolve before `start` is + // called. + const startDependenciesCheck = plugin.startDependencies.then(res => { + startDependenciesResolved = true; + expect(res).toEqual([context, deps]); + }); + await plugin.start(context, deps); + await startDependenciesCheck; + }); + test('`stop` fails if plugin is not setup up', async () => { expect(() => plugin.stop()).toThrowErrorMatchingInlineSnapshot( `"Plugin \\"plugin-a\\" can't be stopped since it isn't set up."` diff --git a/src/core/public/plugins/plugin.ts b/src/core/public/plugins/plugin.ts index 05268bbfcdd05d..e880627e352c88 100644 --- a/src/core/public/plugins/plugin.ts +++ b/src/core/public/plugins/plugin.ts @@ -17,6 +17,8 @@ * under the License. */ +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; import { DiscoveredPlugin, PluginOpaqueId } from '../../server'; import { PluginInitializerContext } from './plugin_context'; import { loadPluginBundle } from './plugin_loader'; @@ -33,7 +35,7 @@ export interface Plugin< TPluginsSetup extends object = object, TPluginsStart extends object = object > { - setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; + setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; stop?(): void; } @@ -70,6 +72,9 @@ export class PluginWrapper< private initializer?: PluginInitializer; private instance?: Plugin; + private readonly startDependencies$ = new Subject<[CoreStart, TPluginsStart]>(); + public readonly startDependencies = this.startDependencies$.pipe(first()).toPromise(); + constructor( public readonly discoveredPlugin: DiscoveredPlugin, public readonly opaqueId: PluginOpaqueId, @@ -100,7 +105,7 @@ export class PluginWrapper< * @param plugins The dictionary where the key is the dependency name and the value * is the contract returned by the dependency's `setup` function. */ - public async setup(setupContext: CoreSetup, plugins: TPluginsSetup) { + public async setup(setupContext: CoreSetup, plugins: TPluginsSetup) { this.instance = await this.createPluginInstance(); return await this.instance.setup(setupContext, plugins); @@ -118,7 +123,11 @@ export class PluginWrapper< throw new Error(`Plugin "${this.name}" can't be started since it isn't set up.`); } - return await this.instance.start(startContext, plugins); + const startContract = await this.instance.start(startContext, plugins); + + this.startDependencies$.next([startContext, plugins]); + + return startContract; } /** diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts index f77ddd8f2f6967..848f46605d4deb 100644 --- a/src/core/public/plugins/plugin_context.ts +++ b/src/core/public/plugins/plugin_context.ts @@ -107,6 +107,7 @@ export function createPluginSetupContext< injectedMetadata: { getInjectedVar: deps.injectedMetadata.getInjectedVar, }, + getStartServices: () => plugin.startDependencies, }; } diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 2983d7583cb493..281778f9420dd6 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -98,6 +98,7 @@ describe('PluginsService', () => { mockSetupContext = { ...mockSetupDeps, application: expect.any(Object), + getStartServices: expect.any(Function), }; mockStartDeps = { application: applicationServiceMock.createInternalStartContract(), diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 157f0bab466b0c..b745c23d52212c 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -19,7 +19,7 @@ import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/type // @public export interface App extends AppBase { chromeless?: boolean; - mount: (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise; + mount: AppMount | AppMountDeprecated; } // @public (undocumented) @@ -37,7 +37,8 @@ export interface AppBase { // @public (undocumented) export interface ApplicationSetup { register(app: App): void; - registerMountContext(contextName: T, provider: IContextProvider): void; + // @deprecated + registerMountContext(contextName: T, provider: IContextProvider): void; } // @public (undocumented) @@ -50,10 +51,14 @@ export interface ApplicationStart { path?: string; state?: any; }): void; - registerMountContext(contextName: T, provider: IContextProvider): void; + // @deprecated + registerMountContext(contextName: T, provider: IContextProvider): void; } // @public +export type AppMount = (params: AppMountParameters) => AppUnmount | Promise; + +// @public @deprecated export interface AppMountContext { core: { application: Pick; @@ -71,6 +76,9 @@ export interface AppMountContext { }; } +// @public @deprecated +export type AppMountDeprecated = (context: AppMountContext, params: AppMountParameters) => AppUnmount | Promise; + // @public (undocumented) export interface AppMountParameters { appBasePath: string; @@ -275,13 +283,14 @@ export interface CoreContext { } // @public -export interface CoreSetup { +export interface CoreSetup { // (undocumented) application: ApplicationSetup; - // (undocumented) + // @deprecated (undocumented) context: ContextSetup; // (undocumented) fatalErrors: FatalErrorsSetup; + getStartServices(): Promise<[CoreStart, TPluginsStart]>; // (undocumented) http: HttpSetup; // @deprecated @@ -653,7 +662,7 @@ export interface IUiSettingsClient { } // @public @deprecated -export interface LegacyCoreSetup extends CoreSetup { +export interface LegacyCoreSetup extends CoreSetup { // Warning: (ae-forgotten-export) The symbol "InjectedMetadataSetup" needs to be exported by the entry point index.d.ts // // @deprecated (undocumented) @@ -749,7 +758,7 @@ export interface PackageInfo { // @public export interface Plugin { // (undocumented) - setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; + setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; // (undocumented) start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; // (undocumented) diff --git a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts index e5bfd88ea76378..cd3e0d2fd9f89c 100644 --- a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts +++ b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts @@ -17,7 +17,7 @@ * under the License. */ -import { App, AppUnmount } from 'kibana/public'; +import { App, AppUnmount, AppMountDeprecated } from 'kibana/public'; import { UIRoutes } from 'ui/routes'; import { ILocationService, IScope } from 'angular'; import { npStart } from 'ui/new_platform'; @@ -68,7 +68,10 @@ export class LocalApplicationService { isUnmounted = true; }); (async () => { - unmountHandler = await app.mount({ core: npStart.core }, { element, appBasePath: '' }); + const params = { element, appBasePath: '' }; + unmountHandler = isAppMountDeprecated(app.mount) + ? await app.mount({ core: npStart.core }, params) + : await app.mount(params); // immediately unmount app if scope got destroyed in the meantime if (isUnmounted) { unmountHandler(); @@ -90,3 +93,8 @@ export class LocalApplicationService { } export const localApplicationService = new LocalApplicationService(); + +function isAppMountDeprecated(mount: (...args: any[]) => any): mount is AppMountDeprecated { + // Mount functions with two arguments are assumed to expect deprecated `context` object. + return mount.length === 2; +} diff --git a/src/legacy/ui/public/new_platform/new_platform.test.ts b/src/legacy/ui/public/new_platform/new_platform.test.ts index e5d5cd0a877764..cd1af311d4eff0 100644 --- a/src/legacy/ui/public/new_platform/new_platform.test.ts +++ b/src/legacy/ui/public/new_platform/new_platform.test.ts @@ -58,6 +58,26 @@ describe('ui/new_platform', () => { const scopeMock = { $on: jest.fn() }; const elementMock = [document.createElement('div')]; + controller(scopeMock, elementMock); + expect(mountMock).toHaveBeenCalledWith({ + element: elementMock[0], + appBasePath: '/test/base/path/app/test', + }); + }); + + test('controller calls deprecated context app.mount when invoked', () => { + const unmountMock = jest.fn(); + // Two arguments changes how this is called. + const mountMock = jest.fn((context, params) => unmountMock); + legacyAppRegister({ + id: 'test', + title: 'Test', + mount: mountMock, + }); + const controller = setRootControllerMock.mock.calls[0][1]; + const scopeMock = { $on: jest.fn() }; + const elementMock = [document.createElement('div')]; + controller(scopeMock, elementMock); expect(mountMock).toHaveBeenCalledWith(expect.any(Object), { element: elementMock[0], diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index c0b2d6d9132578..d80d11e1b1bddc 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -20,7 +20,7 @@ import { IScope } from 'angular'; import { IUiActionsStart, IUiActionsSetup } from 'src/plugins/ui_actions/public'; import { IEmbeddableStart, IEmbeddableSetup } from 'src/plugins/embeddable/public'; -import { LegacyCoreSetup, LegacyCoreStart, App } from '../../../../core/public'; +import { LegacyCoreSetup, LegacyCoreStart, App, AppMountDeprecated } from '../../../../core/public'; import { Plugin as DataPlugin } from '../../../../plugins/data/public'; import { Plugin as ExpressionsPlugin } from '../../../../plugins/expressions/public'; import { @@ -111,13 +111,18 @@ export const legacyAppRegister = (app: App) => { // Root controller cannot return a Promise so use an internal async function and call it immediately (async () => { - const unmount = await app.mount( - { core: npStart.core }, - { element, appBasePath: npSetup.core.http.basePath.prepend(`/app/${app.id}`) } - ); + const params = { element, appBasePath: npSetup.core.http.basePath.prepend(`/app/${app.id}`) }; + const unmount = isAppMountDeprecated(app.mount) + ? await app.mount({ core: npStart.core }, params) + : await app.mount(params); $scope.$on('$destroy', () => { unmount(); }); })(); }); }; + +function isAppMountDeprecated(mount: (...args: any[]) => any): mount is AppMountDeprecated { + // Mount functions with two arguments are assumed to expect deprecated `context` object. + return mount.length === 2; +} diff --git a/src/plugins/dev_tools/public/application.tsx b/src/plugins/dev_tools/public/application.tsx index b3c6bb592f378c..be142f2cc74e65 100644 --- a/src/plugins/dev_tools/public/application.tsx +++ b/src/plugins/dev_tools/public/application.tsx @@ -25,7 +25,7 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { useEffect, useRef } from 'react'; -import { AppMountContext } from 'kibana/public'; +import { AppMountContext, AppMountDeprecated } from 'kibana/public'; import { DevTool } from './plugin'; interface DevToolsWrapperProps { @@ -91,10 +91,10 @@ function DevToolsWrapper({ if (mountedTool.current) { mountedTool.current.unmountHandler(); } - const unmountHandler = await activeDevTool.mount(appMountContext, { - element, - appBasePath: '', - }); + const params = { element, appBasePath: '' }; + const unmountHandler = isAppMountDeprecated(activeDevTool.mount) + ? await activeDevTool.mount(appMountContext, params) + : await activeDevTool.mount(params); mountedTool.current = { devTool: activeDevTool, mountpoint: element, @@ -182,3 +182,8 @@ export function renderApp( return () => ReactDOM.unmountComponentAtNode(element); } + +function isAppMountDeprecated(mount: (...args: any[]) => any): mount is AppMountDeprecated { + // Mount functions with two arguments are assumed to expect deprecated `context` object. + return mount.length === 2; +} diff --git a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx index 56cc1cb4ab425d..5c8e1d03d5a4ac 100644 --- a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx +++ b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx @@ -24,6 +24,7 @@ declare global { interface Window { corePluginB?: string; hasAccessToInjectedMetadata?: boolean; + receivedStartServices?: boolean; env?: PluginInitializerContext['env']; } } @@ -40,6 +41,9 @@ export class CorePluginBPlugin public setup(core: CoreSetup, deps: CorePluginBDeps) { window.corePluginB = `Plugin A said: ${deps.core_plugin_a.getGreeting()}`; window.hasAccessToInjectedMetadata = 'getInjectedVar' in core.injectedMetadata; + core.getStartServices().then(([coreStart, plugins]) => { + window.receivedStartServices = 'overlays' in coreStart; + }); core.application.register({ id: 'bar', diff --git a/test/plugin_functional/plugins/core_plugin_legacy/public/index.ts b/test/plugin_functional/plugins/core_plugin_legacy/public/index.ts index 6988ed82f34a7e..51b5d2aaf35876 100644 --- a/test/plugin_functional/plugins/core_plugin_legacy/public/index.ts +++ b/test/plugin_functional/plugins/core_plugin_legacy/public/index.ts @@ -22,8 +22,8 @@ import { npSetup } from 'ui/new_platform'; npSetup.core.application.register({ id: 'core_legacy_compat', title: 'Core Legacy Compat', - async mount(...args) { + async mount(context, params) { const { renderApp } = await import('./application'); - return renderApp(...args); + return renderApp(context, params); }, }); diff --git a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts index a971921ad3ed83..ff535835464870 100644 --- a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts +++ b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts @@ -36,28 +36,41 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider expect(corePluginB).to.equal(`Plugin A said: Hello from Plugin A!`); }); }); + describe('have injectedMetadata service provided', function describeIndexTests() { before(async () => { await PageObjects.common.navigateToApp('bar'); }); - it('should attach string to window.corePluginB', async () => { + it('should attach boolean to window.hasAccessToInjectedMetadata', async () => { const hasAccessToInjectedMetadata = await browser.execute( 'return window.hasAccessToInjectedMetadata' ); expect(hasAccessToInjectedMetadata).to.equal(true); }); }); + describe('have env data provided', function describeIndexTests() { before(async () => { await PageObjects.common.navigateToApp('bar'); }); - it('should attach pluginContext to window.corePluginB', async () => { + it('should attach pluginContext to window.env', async () => { const envData: any = await browser.execute('return window.env'); expect(envData.mode.dev).to.be(true); expect(envData.packageInfo.version).to.be.a('string'); }); }); + + describe('have access to start services via coreSetup.getStartServices', function describeIndexTests() { + before(async () => { + await PageObjects.common.navigateToApp('bar'); + }); + + it('should attach boolean to window.receivedStartServices', async () => { + const receivedStartServices = await browser.execute('return window.receivedStartServices'); + expect(receivedStartServices).to.equal(true); + }); + }); }); }