From a6b41838b57a49f4c8554230bbaf78354b71e86d Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Thu, 30 Nov 2017 15:00:11 +0100 Subject: [PATCH] Give access to angular $injector via chrome (#15267) * Implement getActiveInjector * Fix typo in comments * Rename method to sound more frightening * Move test utils * Add more documentation * Fix typo in comment * Fix name of method in comments --- .../public/stub_get_active_injector.js | 44 +++++++++++++++++++ src/ui/public/chrome/chrome.js | 33 ++++++++++++-- 2 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 src/test_utils/public/stub_get_active_injector.js diff --git a/src/test_utils/public/stub_get_active_injector.js b/src/test_utils/public/stub_get_active_injector.js new file mode 100644 index 000000000000..170c1ed8f020 --- /dev/null +++ b/src/test_utils/public/stub_get_active_injector.js @@ -0,0 +1,44 @@ +/** + * This test file contains stubs for chrome.dangerouslyGetActiveInjector, that you will + * need to load if any part of the code you are testing relies on that method. + * You will need to call setupInjectorStub and teardownInjectorStub in specific + * places inside your test file to setup and teardown the stub. + * If you can call both of them at the same place you can also use the shortcut + * setupAndTeardownInjectorStub instead. + */ + +import ngMock from 'ng_mock'; + +import chrome from 'ui/chrome'; +import sinon from 'sinon'; + +/** + * This method setups the stub for chrome.dangerouslyGetActiveInjector. You must call it in + * a place where beforeEach is allowed to be called (read: inside your describe) + * method. You must call this AFTER you've called `ngMock.module` to setup the modules, + * but BEFORE you first execute code, that uses chrome.getActiveInjector. + */ +export function setupInjectorStub() { + beforeEach(ngMock.inject(($injector) => { + sinon.stub(chrome, 'dangerouslyGetActiveInjector').returns(Promise.resolve($injector)); + })); +} + +/** + * This methods tears down the stub for chrome.dangerouslyGetActiveInjector. You must call it + * in a place where afterEach is allowed to be called. + */ +export function teardownInjectorStub() { + afterEach(() => { + chrome.getActiveInjector.restore(); + }); +} + +/** + * This method combines setupInjectorStub and teardownInjectorStub in one method. + * It can be used if you can call the other two methods directly behind each other. + */ +export function setupAndTeardownInjectorStub() { + setupInjectorStub(); + teardownInjectorStub(); +} diff --git a/src/ui/public/chrome/chrome.js b/src/ui/public/chrome/chrome.js index d5101afab62e..7daffb7f5b7b 100644 --- a/src/ui/public/chrome/chrome.js +++ b/src/ui/public/chrome/chrome.js @@ -46,7 +46,34 @@ templateApi(chrome, internals); themeApi(chrome, internals); translationsApi(chrome, internals); -chrome.bootstrap = function () { - chrome.setupAngular(); - angular.bootstrap(document.body, ['kibana']); +const waitForBootstrap = new Promise(resolve => { + chrome.bootstrap = function () { + chrome.setupAngular(); + angular.bootstrap(document.body, ['kibana']); + resolve(); + }; +}); + +/** + * ---- ATTENTION: Read documentation carefully before using this! ---- + * + * Returns a promise, that resolves with an instance of the currently used Angular + * $injector service for usage outside of Angular. + * You can use this injector to get access to any other injectable component (service, + * constant, etc.) by using its get method. + * + * If you ever use Angular outside of an Angular context via this method, you should + * be really sure you know what you are doing! + * + * When using this method inside your code, you will need to stub it while running + * tests. Look into 'src/test_utils/public/stub_get_active_injector' for more information. + */ +chrome.dangerouslyGetActiveInjector = () => { + return waitForBootstrap.then(() => { + const $injector = angular.element(document.body).injector(); + if (!$injector) { + return Promise.reject('document.body had no angular context after bootstrapping'); + } + return $injector; + }); };