diff --git a/src/ui/public/chrome/directives/__tests__/sub_url_route_filter.js b/src/ui/public/chrome/directives/__tests__/sub_url_route_filter.js new file mode 100644 index 00000000000000..e4b27b5059950c --- /dev/null +++ b/src/ui/public/chrome/directives/__tests__/sub_url_route_filter.js @@ -0,0 +1,105 @@ +import ngMock from 'ng_mock'; +import expect from 'expect.js'; + +import { SubUrlRouteFilterProvider } from '../sub_url_route_filter'; + +describe('kbn-chrome subUrlRouteFilter()', () => { + describe('no ngRoute', () => { + beforeEach(ngMock.module('kibana/private')); + beforeEach(ngMock.inject(($injector) => { + expect($injector.has('$route')).to.be(false); + })); + + it('always returns true when there is no $route service', ngMock.inject((Private) => { + const subUrlRouteFilter = Private(SubUrlRouteFilterProvider); + expect(subUrlRouteFilter()).to.be(true); + })); + }); + + describe('with ngRoute', () => { + + beforeEach(ngMock.module('kibana/private', 'ngRoute', $routeProvider => { + $routeProvider.when('/foo', { + redirectTo: '/bar' + }); + + $routeProvider.when('/bar', { + template: '
foo => bar
' + }); + })); + + let test; + beforeEach(ngMock.inject((Private, $location, $rootScope, $route) => { + test = ({ path, assert }) => { + const subUrlRouteFilter = Private(SubUrlRouteFilterProvider); + $location.path(path); + + let result; + function runAssert() { + if (result) { + // only run once + return; + } + + try { + assert($route, subUrlRouteFilter); + result = {}; + } catch (error) { + result = { error }; + } + } + + $rootScope.$on('$routeUpdate', runAssert); + $rootScope.$on('$routeChangeSuccess', runAssert); + $rootScope.$apply(); + + // when no route matches there is no event so we run manually + if (!result) { + runAssert(); + } + + if (result.error) { + throw result.error; + } + }; + })); + + describe('no current route', () => { + it('returns false', () => { + test({ + path: '/baz', + assert($route, subUrlRouteFilter) { + expect($route.current).to.not.be.ok(); + expect(subUrlRouteFilter()).to.eql(false); + } + }); + }); + }); + + describe('redirectTo route', () => { + it('returns false', () => { + test({ + path: '/foo', + assert($route, subUrlRouteFilter) { + expect($route.current).to.be.ok(); + expect($route.current.redirectTo).to.be.ok(); + expect(subUrlRouteFilter()).to.eql(false); + } + }); + }); + }); + + describe('standard route', () => { + it('returns true', () => { + test({ + path: '/bar', + assert($route, subUrlRouteFilter) { + expect($route.current).to.be.ok(); + expect($route.current.template).to.be.ok(); + expect(subUrlRouteFilter()).to.eql(true); + } + }); + }); + }); + }); +}); diff --git a/src/ui/public/chrome/directives/kbn_chrome.js b/src/ui/public/chrome/directives/kbn_chrome.js index f2b438916a1e3c..95862f28bf7472 100644 --- a/src/ui/public/chrome/directives/kbn_chrome.js +++ b/src/ui/public/chrome/directives/kbn_chrome.js @@ -9,6 +9,7 @@ import { unhashUrl, } from 'ui/state_management/state_hashing'; import { notify } from 'ui/notify'; +import { SubUrlRouteFilterProvider } from './sub_url_route_filter'; export function kbnChromeProvider(chrome, internals) { @@ -41,16 +42,23 @@ export function kbnChromeProvider(chrome, internals) { internals.permanentlyHideChrome(); } - // listen for route changes, propogate to tabs - const onRouteChange = function () { + const subUrlRouteFilter = Private(SubUrlRouteFilterProvider); + + function updateSubUrls() { const urlWithHashes = window.location.href; const urlWithStates = unhashUrl(urlWithHashes, getUnhashableStates()); internals.trackPossibleSubUrl(urlWithStates); - }; + } + + function onRouteChange($event) { + if (subUrlRouteFilter($event)) { + updateSubUrls(); + } + } $rootScope.$on('$routeChangeSuccess', onRouteChange); $rootScope.$on('$routeUpdate', onRouteChange); - onRouteChange(); + updateSubUrls(); // initialize sub urls const allPendingHttpRequests = () => $http.pendingRequests; const removeSystemApiRequests = (pendingHttpRequests = []) => remove(pendingHttpRequests, isSystemApiRequest); diff --git a/src/ui/public/chrome/directives/sub_url_route_filter.js b/src/ui/public/chrome/directives/sub_url_route_filter.js new file mode 100644 index 00000000000000..ccfc68c834f670 --- /dev/null +++ b/src/ui/public/chrome/directives/sub_url_route_filter.js @@ -0,0 +1,18 @@ +/** + * Creates a function that will be called on each route change + * to determine if the event should be used to update the last + * subUrl of chrome links/tabs + * @injected + */ +export function SubUrlRouteFilterProvider($injector) { + if (!$injector.has('$route')) { + return function alwaysUpdate() { + return true; + }; + } + + const $route = $injector.get('$route'); + return function ignoreRedirectToRoutes() { + return Boolean($route.current && !$route.current.redirectTo); + }; +} diff --git a/src/ui/public/private/private.js b/src/ui/public/private/private.js index 9babdc4b3100db..389ebd2a885cad 100644 --- a/src/ui/public/private/private.js +++ b/src/ui/public/private/private.js @@ -89,7 +89,7 @@ function name(fn) { return fn.name || fn.toString().split('\n').shift(); } -uiModules.get('kibana') +uiModules.get('kibana/private') .provider('Private', function () { const provider = this;