diff --git a/scripts/gulp/tasks/test.ts b/scripts/gulp/tasks/test.ts index bba7e5789a..e6c3572763 100644 --- a/scripts/gulp/tasks/test.ts +++ b/scripts/gulp/tasks/test.ts @@ -85,6 +85,7 @@ function karmaTest(watch: boolean, done: Function) { let karmaConfig = { configFile: join(SCRIPTS_ROOT, 'karma/karma.conf.js'), + singleRun: true, }; if (watch) { @@ -96,6 +97,9 @@ function karmaTest(watch: boolean, done: Function) { args: ['--grep', argv.testGrep] }; } + if (typeof argv.debug !== 'undefined') { + karmaConfig.singleRun = false; + } new karma.Server(karmaConfig, done).start(); } diff --git a/scripts/karma/system.config.js b/scripts/karma/system.config.js index 5801d5cd51..c156fddd1d 100644 --- a/scripts/karma/system.config.js +++ b/scripts/karma/system.config.js @@ -4,7 +4,7 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; // disable console debugs/errors/warns from printing out console.debug = () => {}; -console.error = () => {}; +// console.error = () => {}; console.warn = () => {}; __karma__.loaded = function () {}; diff --git a/src/animations/animation.ts b/src/animations/animation.ts index 244fb3656c..b04397cd21 100644 --- a/src/animations/animation.ts +++ b/src/animations/animation.ts @@ -103,6 +103,13 @@ export class Animation { return 0; } + /** + * Returns if the animation is a root one. + */ + isRoot(): boolean { + return !this.parent; + } + /** * Set the duration for this animation. */ diff --git a/src/components/app/test/app.spec.ts b/src/components/app/test/app.spec.ts index 143790cd26..8344df8071 100644 --- a/src/components/app/test/app.spec.ts +++ b/src/components/app/test/app.spec.ts @@ -167,8 +167,9 @@ describe('App', () => { expect(plt.exitApp).not.toHaveBeenCalled(); done(); }).catch((err: Error) => { + fail(err); done(err); - }); + }); }); it('should pop the second view in the root nav', () => { diff --git a/src/components/nav/test/basic/pages/first-page/first-page.html b/src/components/nav/test/basic/pages/first-page/first-page.html index 52ace40630..ff751b61e2 100644 --- a/src/components/nav/test/basic/pages/first-page/first-page.html +++ b/src/components/nav/test/basic/pages/first-page/first-page.html @@ -42,7 +42,7 @@ Toggle Can Leave - + diff --git a/src/components/nav/test/nav.spec.ts b/src/components/nav/test/nav.spec.ts index de2c9ce51e..109aa98e19 100644 --- a/src/components/nav/test/nav.spec.ts +++ b/src/components/nav/test/nav.spec.ts @@ -34,6 +34,7 @@ describe('Nav', () => { expect(nav.setPages).toHaveBeenCalledWith(knownViews, null, null); done(); }).catch((err: Error) => { + fail(err); done(err); }); }); @@ -56,6 +57,7 @@ describe('Nav', () => { expect(nav.setPages).toHaveBeenCalledWith(knownViews, null, null); done(); }).catch((err: Error) => { + fail(err); done(err); }); }); @@ -72,7 +74,8 @@ describe('Nav', () => { promise.then(() => { expect(nav.push).toHaveBeenCalled(); done(); - }).catch((err: Error) => { + }).catch((err: Error) => { + fail(err); done(err); }); }); diff --git a/src/components/tabs/tab.ts b/src/components/tabs/tab.ts index 9f81934581..a2e93136a0 100644 --- a/src/components/tabs/tab.ts +++ b/src/components/tabs/tab.ts @@ -292,7 +292,7 @@ export class Tab extends NavControllerBase { /** * @hidden */ - load(opts: NavOptions, done?: Function) { + load(opts: NavOptions, done?: () => void) { if (!this._loaded && this.root) { this.setElementClass('show-tab', true); this.push(this.root, this.rootParams, opts, done); @@ -305,7 +305,7 @@ export class Tab extends NavControllerBase { this._dom.read(() => { this.resize(); }); - done(true); + done(); } } diff --git a/src/components/tabs/test/tabs.spec.ts b/src/components/tabs/test/tabs.spec.ts index 426c402f50..a26adbce4a 100644 --- a/src/components/tabs/test/tabs.spec.ts +++ b/src/components/tabs/test/tabs.spec.ts @@ -162,7 +162,6 @@ describe('Tabs', () => { it('should get the tab', () => { var tabs = mockTabs(); var tab0 = mockTab(tabs); - tab0.setRoot({}); var tab1 = mockTab(tabs); expect(tabs.getIndex(tab0)).toEqual(0); diff --git a/src/navigation/nav-controller-base.ts b/src/navigation/nav-controller-base.ts index ee324bd912..25057eead9 100644 --- a/src/navigation/nav-controller-base.ts +++ b/src/navigation/nav-controller-base.ts @@ -3,8 +3,8 @@ import { ComponentRef, Input, ComponentFactoryResolver, ElementRef, EventEmitter import { AnimationOptions } from '../animations/animation'; import { App } from '../components/app/app'; import { Config } from '../config/config'; -import { convertToView, convertToViews, NavOptions, DIRECTION_BACK, DIRECTION_FORWARD, INIT_ZINDEX, - TransitionResolveFn, TransitionInstruction, STATE_NEW, STATE_INITIALIZED, STATE_ATTACHED, STATE_DESTROYED } from './nav-util'; +import { convertToViews, NavOptions, NavResult, DIRECTION_BACK, DIRECTION_FORWARD, INIT_ZINDEX, + TransitionInstruction, STATE_NEW, STATE_INITIALIZED, STATE_ATTACHED, STATE_DESTROYED } from './nav-util'; import { setZIndex } from './nav-util'; import { DeepLinker } from './deep-linker'; import { DomController } from '../platform/dom-controller'; @@ -80,40 +80,31 @@ export class NavControllerBase extends Ion implements NavController { this.id = 'n' + (++ctrlIds); } - push(page: any, params?: any, opts?: NavOptions, done?: Function): Promise { - return convertToView(this._linker, page, params).then(viewController => { - return this._queueTrns({ - insertStart: -1, - insertViews: [viewController], - opts: opts, - }, done); - }).catch((err: Error) => { - console.error('Failed to navigate: ', err.message); - throw err; - }); + push(page: any, params?: any, opts?: NavOptions, done?: () => void): Promise { + return this._queueTrns({ + insertStart: -1, + insertViews: [{ page: page, params: params }], + opts: opts, + }, done); } - insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, done?: Function): Promise { - return convertToView(this._linker, page, params).then(viewController => { - return this._queueTrns({ - insertStart: insertIndex, - insertViews: [viewController], - opts: opts, - }, done); - }); + insert(insertIndex: number, page: any, params?: any, opts?: NavOptions, done?: () => void): Promise { + return this._queueTrns({ + insertStart: insertIndex, + insertViews: [{ page: page, params: params }], + opts: opts, + }, done); } - insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: Function): Promise { - return convertToViews(this._linker, insertPages).then(viewControllers => { - return this._queueTrns({ - insertStart: insertIndex, - insertViews: viewControllers, - opts: opts, - }, done); - }); + insertPages(insertIndex: number, insertPages: any[], opts?: NavOptions, done?: () => void): Promise { + return this._queueTrns({ + insertStart: insertIndex, + insertViews: insertPages, + opts: opts, + }, done); } - pop(opts?: NavOptions, done?: Function): Promise { + pop(opts?: NavOptions, done?: () => void): Promise { return this._queueTrns({ removeStart: -1, removeCount: 1, @@ -121,7 +112,7 @@ export class NavControllerBase extends Ion implements NavController { }, done); } - popTo(indexOrViewCtrl: any, opts?: NavOptions, done?: Function): Promise { + popTo(indexOrViewCtrl: any, opts?: NavOptions, done?: () => void): Promise { let config: TransitionInstruction = { removeStart: -1, removeCount: -1, @@ -136,7 +127,7 @@ export class NavControllerBase extends Ion implements NavController { return this._queueTrns(config, done); } - popToRoot(opts?: NavOptions, done?: Function): Promise { + popToRoot(opts?: NavOptions, done?: () => void): Promise { return this._queueTrns({ removeStart: 1, removeCount: -1, @@ -152,7 +143,7 @@ export class NavControllerBase extends Ion implements NavController { return Promise.all(promises); } - remove(startIndex: number, removeCount: number = 1, opts?: NavOptions, done?: Function): Promise { + remove(startIndex: number, removeCount: number = 1, opts?: NavOptions, done?: () => void): Promise { return this._queueTrns({ removeStart: startIndex, removeCount: removeCount, @@ -160,7 +151,7 @@ export class NavControllerBase extends Ion implements NavController { }, done); } - removeView(viewController: ViewController, opts?: NavOptions, done?: Function): Promise { + removeView(viewController: ViewController, opts?: NavOptions, done?: () => void): Promise { return this._queueTrns({ removeView: viewController, removeStart: 0, @@ -169,19 +160,12 @@ export class NavControllerBase extends Ion implements NavController { }, done); } - setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: Function): Promise { - return convertToView(this._linker, pageOrViewCtrl, params).then((viewController) => { - return this._setPages([viewController], opts, done); - }); + setRoot(pageOrViewCtrl: any, params?: any, opts?: NavOptions, done?: () => void): Promise { + return this.setPages([{ page: pageOrViewCtrl, params: params }], opts, done); } - setPages(pages: any[], opts?: NavOptions, done?: Function): Promise { - return convertToViews(this._linker, pages).then(viewControllers => { - return this._setPages(viewControllers, opts, done); - }); - } - _setPages(viewControllers: ViewController[], opts?: NavOptions, done?: Function): Promise { + setPages(viewControllers: any[], opts?: NavOptions, done?: () => void): Promise { if (isBlank(opts)) { opts = {}; } @@ -208,81 +192,68 @@ export class NavControllerBase extends Ion implements NavController { // 7. _transitionStart(): called once the transition actually starts, it initializes the Animation underneath. // 8. _transitionFinish(): called once the transition finishes // 9. _cleanup(): syncs the navigation internal state with the DOM. For example it removes the pages from the DOM or hides/show them. - _queueTrns(ti: TransitionInstruction, done: Function): Promise { - let promise: Promise; - let resolve: Function = done; - let reject: Function = done; + _queueTrns(ti: TransitionInstruction, done: () => void): Promise { + const promise = new Promise((resolve, reject) => { + ti.resolve = resolve; + ti.reject = reject; + }); + ti.done = done; - if (done === undefined) { - // only create a promise if a done callback wasn't provided - // done can be a null, which avoids any functions - promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - } - - // ti.resolve() is called when the navigation transition is finished successfully - ti.resolve = (hasCompleted: boolean, isAsync: boolean, enteringName: string, leavingName: string, direction: string) => { - this._trnsId = null; - this._init = true; - resolve && resolve(hasCompleted, isAsync, enteringName, leavingName, direction); - - // let's see if there's another to kick off - this.setTransitioning(false); - this._swipeBackCheck(); - this._nextTrns(); - }; - - // ti.reject() is called when the navigation transition fails. ie. it is rejected at some point. - ti.reject = (rejectReason: any, transition: Transition) => { - this._trnsId = null; - this._queue.length = 0; - - // walk through the transition views so they are destroyed - while (transition) { - var enteringView = transition.enteringView; - if (enteringView && (enteringView._state === STATE_ATTACHED)) { - this._destroyView(enteringView); - } - if (transition.isRoot()) { - this._trnsCtrl.destroy(transition.trnsId); - break; - } - transition = transition.parent; - } - - reject && reject(false, false, rejectReason); - - // let's see if there's another to kick off - this.setTransitioning(false); - this._swipeBackCheck(); - this._nextTrns(); - }; - - if (ti.insertViews) { - // ensure we've got good views to insert - ti.insertViews = ti.insertViews.filter(v => v !== null); - if (ti.insertViews.length === 0) { - ti.reject('invalid views to insert'); - return promise; - } - - } else if (isPresent(ti.removeStart) && this._views.length === 0 && !this._isPortal) { - ti.reject('no views in the stack to be removed'); - return promise; + // Normalize empty + if (ti.insertViews && ti.insertViews.length === 0) { + ti.insertViews = undefined; } + // Enqueue transition instruction this._queue.push(ti); // if there isn't a transition already happening // then this will kick off this transition this._nextTrns(); - // promise is undefined if a done callbacks was provided return promise; } + _success(result: NavResult, ti: TransitionInstruction) { + this._init = true; + this._trnsId = null; + + // let's see if there's another to kick off + this.setTransitioning(false); + this._swipeBackCheck(); + this._nextTrns(); + + if (ti.done) { + ti.done( + result.hasCompleted, + result.requiresTransition, + result.enteringName, + result.leavingName, + result.direction + ); + } + ti.resolve(result.hasCompleted); + } + + _failed(rejectReason: any, ti: TransitionInstruction) { + this._trnsId = null; + this._queue.length = 0; + + // let's see if there's another to kick off + this.setTransitioning(false); + this._swipeBackCheck(); + this._nextTrns(); + + if (ti.done) { + ti.done(false, false, rejectReason); + } + if (ti.reject) { + ti.reject(rejectReason); + } else { + ti.resolve(false); + } + } + _nextTrns(): boolean { // this is the framework's bread 'n butta function // only one transition is allowed at any given time @@ -292,68 +263,53 @@ export class NavControllerBase extends Ion implements NavController { // there is no transition happening right now // get the next instruction - const ti = this._nextTI(); + const ti = this._queue.shift(); if (!ti) { return false; } - // ensure any of the inserted view are used - const insertViews = ti.insertViews; - if (insertViews) { - for (var i = 0; i < insertViews.length; i++) { - var nav = insertViews[i]._nav; - if (nav && nav !== this || insertViews[i]._state === STATE_DESTROYED) { - ti.reject('leavingView and enteringView are null. stack is already empty'); - return false; - } - } - } - - // get entering and leaving views - const leavingView = this.getActive(); - const enteringView = this._getEnteringView(ti, leavingView); - - if (!leavingView && !enteringView) { - ti.reject('leavingView and enteringView are null. stack is already empty'); - return false; - } - // set that this nav is actively transitioning - this.setTransitioning(true); + let enteringView: ViewController; + let leavingView: ViewController; - // Initialize enteringView - if (enteringView && enteringView._state === STATE_NEW) { - // render the entering view, and all child navs and views - // ******** DOM WRITE **************** - this._viewInit(enteringView); - } + this._startTI(ti) + .then(() => this._loadLazyLoading(ti)) + .then(() => { + leavingView = this.getActive(); + enteringView = this._getEnteringView(ti, leavingView); - // Only test canLeave/canEnter if there is transition - const requiresTransition = ti.requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView; - if (requiresTransition) { - // views have been initialized, now let's test - // to see if the transition is even allowed or not - return this._viewTest(enteringView, leavingView, ti); - } else { - return this._postViewInit(enteringView, leavingView, ti); - } + if (!leavingView && !enteringView) { + throw 'no views in the stack to be removed'; + } + + if (enteringView && enteringView._state === STATE_NEW) { + this._viewInit(enteringView); + } + + // Needs transition? + ti.requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView; + }) + .then(() => this._viewTest(enteringView, leavingView, ti)) + .then(() => this._postViewInit(enteringView, leavingView, ti)) + .then(() => this._transition(enteringView, leavingView, ti)) + .then((result) => this._success(result, ti)) + .catch((rejectReason) => this._failed(rejectReason, ti)); + + return true; } - _nextTI(): TransitionInstruction { - const ti = this._queue.shift(); - if (!ti) { - return null; - } + _startTI(ti: TransitionInstruction): Promise { const viewsLength = this._views.length; if (isPresent(ti.removeView)) { assert(isPresent(ti.removeStart), 'removeView needs removeStart'); assert(isPresent(ti.removeCount), 'removeView needs removeCount'); - var index = this._views.indexOf(ti.removeView); - if (index >= 0) { - ti.removeStart += index; + const index = this.indexOf(ti.removeView); + if (index < 0) { + return Promise.reject('removeView was not found'); } + ti.removeStart += index; } if (isPresent(ti.removeStart)) { if (ti.removeStart < 0) { @@ -373,7 +329,35 @@ export class NavControllerBase extends Ion implements NavController { } ti.enteringRequiresTransition = (ti.insertStart === viewsLength); } - return ti; + this.setTransitioning(true); + return Promise.resolve(); + } + + _loadLazyLoading(ti: TransitionInstruction): Promise { + const insertViews = ti.insertViews; + if (insertViews) { + assert(insertViews.length > 0, 'length can not be zero'); + return convertToViews(this._linker, insertViews).then((viewControllers) => { + assert(insertViews.length === viewControllers.length, 'lengths does not match'); + + // Check all the inserted view are correct + for (var i = 0; i < viewControllers.length; i++) { + var view = viewControllers[i]; + if (!view) { + throw 'invalid views to insert'; + } + var nav = view._nav; + if (nav && nav !== this) { + throw 'inserted view was already inserted'; + } + if (viewControllers[i]._state === STATE_DESTROYED) { + throw 'inserted view was already destroyed'; + } + } + ti.insertViews = viewControllers; + }); + } + return Promise.resolve(); } _getEnteringView(ti: TransitionInstruction, leavingView: ViewController): ViewController { @@ -435,10 +419,10 @@ export class NavControllerBase extends Ion implements NavController { console.warn(`You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`, this, this.getNativeElement()); - ti.reject('navigation stack needs at least one root page'); - return false; + throw 'navigation stack needs at least one root page'; } + // At this point the transition can not be rejected, any throw should be an error // there are views to insert if (insertViews) { // manually set the new view's id if an id was passed in the options @@ -479,27 +463,15 @@ export class NavControllerBase extends Ion implements NavController { } } - if (!ti.requiresTransition) { - // transition is not required, so we are already done! - // they're inserting/removing the views somewhere in the middle or - // beginning, so visually nothing needs to animate/transition - // resolve immediately because there's no animation that's happening - ti.resolve(true, false); - return true; - } - // set which animation it should use if it wasn't set yet - if (!opts.animation) { + if (ti.requiresTransition && !opts.animation) { if (isPresent(ti.removeStart)) { opts.animation = (leavingView || enteringView).getTransitionName(opts.direction); } else { opts.animation = (enteringView || leavingView).getTransitionName(opts.direction); } } - - // huzzah! let us transition these views - this._transitionInit(enteringView, leavingView, opts, ti.resolve); - return true; + ti.opts = opts; } /** @@ -509,6 +481,7 @@ export class NavControllerBase extends Ion implements NavController { assert(enteringView, 'enteringView must be non null'); assert(enteringView._state === STATE_NEW, 'enteringView state must be NEW'); + // render the entering view, and all child navs and views // entering view has not been initialized yet const componentProviders = ReflectiveInjector.resolve([ { provide: NavController, useValue: this }, @@ -551,52 +524,52 @@ export class NavControllerBase extends Ion implements NavController { this._zone.run(this._didLoad.bind(this, view)); } - _viewTest(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction): boolean { + _viewTest(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction): Promise { + // Only test canLeave/canEnter if there is transition + if (!ti.requiresTransition) { + return Promise.resolve(); + } + const promises: Promise[] = []; if (leavingView) { - var leavingTestResult = leavingView._lifecycleTest('Leave'); - - if (leavingTestResult === false) { - // synchronous reject - ti.reject((leavingTestResult !== false ? leavingTestResult : `ionViewCanLeave rejected`)); - return false; - } else if (leavingTestResult instanceof Promise) { - // async promise - promises.push(leavingTestResult); - } + promises.push(leavingView._lifecycleTest('Leave')); } - if (enteringView) { - var enteringTestResult = enteringView._lifecycleTest('Enter'); + promises.push(enteringView._lifecycleTest('Enter')); + } - if (enteringTestResult === false) { - // synchronous reject - ti.reject((enteringTestResult !== false ? enteringTestResult : `ionViewCanEnter rejected`)); - return false; - } else if (enteringTestResult instanceof Promise) { - // async promise - promises.push(enteringTestResult); + if (promises.length === 0) { + return Promise.resolve(); + } + + // darn, async promises, gotta wait for them to resolve + return Promise.all(promises).then((values: any[]) => { + if (values.some(result => result === false)) { + throw 'canEnter/Leave returned false'; } - } - - if (promises.length) { - // darn, async promises, gotta wait for them to resolve - Promise.all(promises).then((values: any[]) => { - if (values.some(result => result === false)) { - ti.reject(`ionViewCanEnter rejected`); - } else { - this._postViewInit(enteringView, leavingView, ti); - } - }).catch(ti.reject); - return true; - } else { - // synchronous and all tests passed! let's move on already - return this._postViewInit(enteringView, leavingView, ti); - } + }).catch((reason) => { + // Do not + ti.reject = null; + throw reason; + }); } - _transitionInit(enteringView: ViewController, leavingView: ViewController, opts: NavOptions, resolve: TransitionResolveFn) { + _transition(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction): Promise { + + if (!ti.requiresTransition) { + // transition is not required, so we are already done! + // they're inserting/removing the views somewhere in the middle or + // beginning, so visually nothing needs to animate/transition + // resolve immediately because there's no animation that's happening + return Promise.resolve({ + hasCompleted: true, + requiresTransition: false + }); + } + + const opts = ti.opts; + // figure out if this transition is the root one or a // child of a parent nav that has the root transition this._trnsId = this._trnsCtrl.getRootTrnsId(this); @@ -630,11 +603,8 @@ export class NavControllerBase extends Ion implements NavController { } // transition start has to be registered before attaching the view to the DOM! - transition.registerStart(() => { - this._transitionStart(transition, enteringView, leavingView, opts, resolve); - if (transition.parent) { - transition.parent.start(); - } + const promise = new Promise(resolve => transition.registerStart(resolve)).then(() => { + return this._transitionStart(transition, enteringView, leavingView, opts); }); if (enteringView && (enteringView._state === STATE_INITIALIZED)) { @@ -645,13 +615,15 @@ export class NavControllerBase extends Ion implements NavController { this._viewAttachToDOM(enteringView, enteringView._cmp, this._viewport); } + if (!transition.hasChildren) { // lowest level transition, so kick it off and let it bubble up to start all of them transition.start(); } + return promise; } - _transitionStart(transition: Transition, enteringView: ViewController, leavingView: ViewController, opts: NavOptions, resolve: TransitionResolveFn) { + _transitionStart(transition: Transition, enteringView: ViewController, leavingView: ViewController, opts: NavOptions): Promise { assert(this.isTransitioning(), 'isTransitioning() has to be true'); this._trnsId = null; @@ -689,15 +661,14 @@ export class NavControllerBase extends Ion implements NavController { // that will fire off the willEnter/Leave lifecycle events at the right time transition.beforeAddRead(this._viewsWillLifecycles.bind(this, enteringView, leavingView)); - // create a callback for when the animation is done - transition.onFinish(() => { - // transition animation has ended - this._zone.run(this._transitionFinish.bind(this, transition, opts, resolve)); - }); - // get the set duration of this transition const duration = transition.getDuration(); + // create a callback for when the animation is done + const promise = new Promise(resolve => { + transition.onFinish(resolve); + }); + if (transition.isRoot()) { // this is the top most, or only active transition, so disable the app // add XXms to the duration the app is disabled when the keyboard is open @@ -723,9 +694,14 @@ export class NavControllerBase extends Ion implements NavController { transition.play(); } } + + return promise.then(() => this._zone.run(() => { + return this._transitionFinish(transition, opts); + })); } - _transitionFinish(transition: Transition, opts: NavOptions, resolve: TransitionResolveFn) { + _transitionFinish(transition: Transition, opts: NavOptions): NavResult { + const hasCompleted = transition.hasCompleted; const enteringView = transition.enteringView; const leavingView = transition.leavingView; @@ -774,8 +750,13 @@ export class NavControllerBase extends Ion implements NavController { } } - // congrats, we did it! - resolve(hasCompleted, true, enteringName, leavingName, opts.direction); + return { + hasCompleted: hasCompleted, + requiresTransition: true, + enteringName: enteringName, + leavingName: leavingName, + direction: opts.direction + }; } _viewsWillLifecycles(enteringView: ViewController, leavingView: ViewController) { diff --git a/src/navigation/nav-util.ts b/src/navigation/nav-util.ts index 388c874f06..58fa528a48 100644 --- a/src/navigation/nav-util.ts +++ b/src/navigation/nav-util.ts @@ -8,7 +8,7 @@ import { NavControllerBase } from './nav-controller-base'; import { Transition } from '../transitions/transition'; -export function getComponent(linker: DeepLinker, nameOrPageOrView: any, params?: any) { +export function getComponent(linker: DeepLinker, nameOrPageOrView: any, params?: any): Promise { if (typeof nameOrPageOrView === 'function') { return Promise.resolve( new ViewController(nameOrPageOrView, params) @@ -24,7 +24,7 @@ export function getComponent(linker: DeepLinker, nameOrPageOrView: any, params?: return Promise.resolve(null); } -export function convertToView(linker: DeepLinker, nameOrPageOrView: any, params: any) { +export function convertToView(linker: DeepLinker, nameOrPageOrView: any, params: any): Promise { if (nameOrPageOrView) { if (isViewController(nameOrPageOrView)) { // is already a ViewController @@ -34,11 +34,10 @@ export function convertToView(linker: DeepLinker, nameOrPageOrView: any, params: return getComponent(linker, nameOrPageOrView, params); } - console.error(`invalid page component: ${nameOrPageOrView}`); return Promise.resolve(null); } -export function convertToViews(linker: DeepLinker, pages: any[]) { +export function convertToViews(linker: DeepLinker, pages: any[]): Promise { const views: Promise[] = []; if (isArray(pages)) { for (var i = 0; i < pages.length; i++) { @@ -147,6 +146,14 @@ export interface NavLink { defaultHistory?: any[]; } +export interface NavResult { + hasCompleted: boolean; + requiresTransition: boolean; + enteringName?: string; + leavingName?: string; + direction?: string; +} + export interface NavSegment { id: string; name: string; @@ -188,12 +195,13 @@ export interface TransitionRejectFn { export interface TransitionInstruction { opts: NavOptions; insertStart?: number; - insertViews?: ViewController[]; + insertViews?: any[]; removeView?: ViewController; removeStart?: number; removeCount?: number; - resolve?: TransitionResolveFn; - reject?: TransitionRejectFn; + resolve?: (hasCompleted: boolean) => void; + reject?: (rejectReason: string) => void; + done?: Function; leavingRequiresTransition?: boolean; enteringRequiresTransition?: boolean; requiresTransition?: boolean; diff --git a/src/navigation/test/deep-linker.spec.ts b/src/navigation/test/deep-linker.spec.ts index 17103bcf05..147074cfdd 100644 --- a/src/navigation/test/deep-linker.spec.ts +++ b/src/navigation/test/deep-linker.spec.ts @@ -284,6 +284,7 @@ describe('DeepLinker', () => { expect(result.length).toEqual(1); done(); }).catch((err: Error) => { + fail(err); done(err); }); }); diff --git a/src/navigation/test/nav-controller.spec.ts b/src/navigation/test/nav-controller.spec.ts index 8b33bcd136..641f6388e9 100644 --- a/src/navigation/test/nav-controller.spec.ts +++ b/src/navigation/test/nav-controller.spec.ts @@ -60,7 +60,7 @@ describe('NavController', () => { expect(nav.getByIndex(2).component).toEqual(MockView3); expect(nav.getByIndex(3).component).toEqual(MockView4); // Pop 1 - nav.pop({ animate: false }, pop1Done); + return nav.pop({ animate: false }, pop1Done); }).then(() => { expect(pop1Done).toHaveBeenCalledWith( hasCompleted, requiresTransition, 'MockView3', 'MockView4', DIRECTION_BACK @@ -88,9 +88,10 @@ describe('NavController', () => { expect(nav.getByIndex(0).component).toEqual(MockView1); done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); }); describe('push', () => { @@ -108,9 +109,10 @@ describe('NavController', () => { expect(nav.isTransitioning()).toEqual(false); done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should push a component as the second view at the end', (done: Function) => { mockViews(nav, [mockView(MockView1)]); @@ -128,9 +130,10 @@ describe('NavController', () => { done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should push a ViewController as the second view and fire lifecycles', (done: Function) => { let view1 = mockView(); @@ -169,20 +172,26 @@ describe('NavController', () => { done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); }); describe('insert', () => { - it('should not modify the view id', () => { + it('should not modify the view id', (done) => { let view = mockView(MockView4); view.id = 'custom_id'; - nav.insert(0, view); - + nav.insert(0, view).then(() => { + expect(view.id).toEqual('custom_id'); + done(); + }).catch(err => { + fail(err); + done(); + }); expect(view.id).toEqual('custom_id'); - }); + }, 10000); it('should insert at the begining with no async transition', (done: Function) => { @@ -213,9 +222,10 @@ describe('NavController', () => { done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should insert at the end when given -1', (done: Function) => { let opts: NavOptions = {}; @@ -231,9 +241,10 @@ describe('NavController', () => { expect(nav.last().component).toEqual(MockView2); done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should insert at the end when given a number greater than actual length', (done: Function) => { mockViews(nav, [mockView(MockView1)]); @@ -249,25 +260,28 @@ describe('NavController', () => { done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should not insert if null view', (done: Function) => { mockViews(nav, [mockView(MockView1)]); nav.insert(-1, null, null, null, trnsDone).then(() => { + fail('it should not succeed'); + done(); + }).catch((err: Error) => { let hasCompleted = false; let requiresTransition = false; let rejectReason = 'invalid views to insert'; + expect(err).toEqual(rejectReason); expect(trnsDone).toHaveBeenCalledWith(hasCompleted, requiresTransition, rejectReason); expect(nav.length()).toEqual(1); expect(nav.last().component).toEqual(MockView1); done(); - }).catch((err: Error) => { - done(err); }); - }); + }, 10000); it('should not insert any view in the stack if canLeave returns false', (done: Function) => { let view1 = mockView(MockView1); @@ -292,12 +306,11 @@ describe('NavController', () => { }).then(() => { expect(nav.length()).toEqual(3); done(); - }).catch((err: Error) => { - done(err); - }); - }); + }).catch(err => fail(err)); - it('should not remove any view from the stack if canLeave returns false', () => { + }, 10000); + + it('should not remove any view from the stack if canLeave returns false', (done) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); mockViews(nav, [view1, view2]); @@ -310,15 +323,17 @@ describe('NavController', () => { return (count === 3); }; - nav.pop(); - expect(nav.length()).toEqual(2); - - nav.pop(); - expect(nav.length()).toEqual(2); - - nav.pop(); - expect(nav.length()).toEqual(1); - }); + nav.pop().then(() => { + expect(nav.length()).toEqual(2); + return nav.pop(); + }).then(() => { + expect(nav.length()).toEqual(2); + return nav.pop(); + }).then(() => { + expect(nav.length()).toEqual(1); + done(); + }).catch(err => fail(err)); + }, 10000); }); @@ -356,104 +371,126 @@ describe('NavController', () => { done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); }); describe('pop', () => { - it('should not pop when no views in the stack', () => { - nav.pop(null, trnsDone); + it('should not pop when no views in the stack', (done) => { + nav.pop(null, trnsDone).then(() => { + fail('it should not succeed'); + done(); + }).catch((err) => { + let hasCompleted = false; + let requiresTransition = false; + let rejectReason = 'no views in the stack to be removed'; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, rejectReason + ); + expect(err).toEqual(rejectReason); + expect(nav.length()).toEqual(0); + expect(nav.isTransitioning()).toEqual(false); + done(); + }); + }, 10000); - let hasCompleted = false; - let requiresTransition = false; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, 'no views in the stack to be removed' - ); - expect(nav.length()).toEqual(0); - expect(nav.isTransitioning()).toEqual(false); - }); - - it('should remove the last view and fire lifecycles', () => { + it('should remove the last view and fire lifecycles', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); mockViews(nav, [view1, view2]); let instance1 = spyOnLifecycles(view1); let instance2 = spyOnLifecycles(view2); - nav.pop(null, trnsDone); + nav.pop(null, trnsDone).then(() => { - expect(instance1.ionViewDidLoad).toHaveBeenCalled(); - expect(instance1.ionViewCanEnter).toHaveBeenCalled(); - expect(instance1.ionViewWillEnter).toHaveBeenCalled(); - expect(instance1.ionViewDidEnter).toHaveBeenCalled(); - expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLoad).toHaveBeenCalled(); + expect(instance1.ionViewCanEnter).toHaveBeenCalled(); + expect(instance1.ionViewWillEnter).toHaveBeenCalled(); + expect(instance1.ionViewDidEnter).toHaveBeenCalled(); + expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); - expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewCanLeave).toHaveBeenCalled(); - expect(instance2.ionViewWillLeave).toHaveBeenCalled(); - expect(instance2.ionViewDidLeave).toHaveBeenCalled(); - expect(instance2.ionViewWillUnload).toHaveBeenCalled(); + expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewCanLeave).toHaveBeenCalled(); + expect(instance2.ionViewWillLeave).toHaveBeenCalled(); + expect(instance2.ionViewDidLeave).toHaveBeenCalled(); + expect(instance2.ionViewWillUnload).toHaveBeenCalled(); - let hasCompleted = true; - let requiresTransition = true; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, 'MockView1', 'MockView2', DIRECTION_BACK - ); - expect(nav.length()).toEqual(1); - expect(nav.getByIndex(0).component).toEqual(MockView1); - expect(nav.isTransitioning()).toEqual(false); - }); + let hasCompleted = true; + let requiresTransition = true; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, 'MockView1', 'MockView2', DIRECTION_BACK + ); + expect(nav.length()).toEqual(1); + expect(nav.getByIndex(0).component).toEqual(MockView1); + expect(nav.isTransitioning()).toEqual(false); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); }); describe('popTo', () => { - it('should pop to a view', () => { + it('should pop to a view', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); let view3 = mockView(MockView3); mockViews(nav, [view1, view2, view3]); - nav.popTo(view2, null, trnsDone); + nav.popTo(view2, null, trnsDone).then(() => { - let hasCompleted = true; - let requiresTransition = true; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, 'MockView2', 'MockView3', DIRECTION_BACK - ); - expect(nav.length()).toEqual(2); - expect(nav.getByIndex(0).component).toEqual(MockView1); - expect(nav.getByIndex(1).component).toEqual(MockView2); - }); + let hasCompleted = true; + let requiresTransition = true; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, 'MockView2', 'MockView3', DIRECTION_BACK + ); + expect(nav.length()).toEqual(2); + expect(nav.getByIndex(0).component).toEqual(MockView1); + expect(nav.getByIndex(1).component).toEqual(MockView2); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); - it('should pop to using an index number', () => { + it('should pop to using an index number', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); let view3 = mockView(MockView3); let view4 = mockView(MockView4); mockViews(nav, [view1, view2, view3, view4]); - nav.popTo(1, null, trnsDone); + nav.popTo(1, null, trnsDone).then(() => { - let hasCompleted = true; - let requiresTransition = true; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, 'MockView2', 'MockView4', DIRECTION_BACK - ); - expect(nav.length()).toEqual(2); - expect(nav.getByIndex(0).component).toEqual(MockView1); - expect(nav.getByIndex(1).component).toEqual(MockView2); - }); + let hasCompleted = true; + let requiresTransition = true; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, 'MockView2', 'MockView4', DIRECTION_BACK + ); + expect(nav.length()).toEqual(2); + expect(nav.getByIndex(0).component).toEqual(MockView1); + expect(nav.getByIndex(1).component).toEqual(MockView2); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); - it('should pop to first using an index number', () => { + it('should pop to first using an index number', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); let view3 = mockView(MockView3); @@ -465,58 +502,63 @@ describe('NavController', () => { let instance3 = spyOnLifecycles(view3); let instance4 = spyOnLifecycles(view4); - nav.popTo(0, null, trnsDone); + nav.popTo(0, null, trnsDone).then(() => { - expect(instance1.ionViewDidLoad).toHaveBeenCalled(); - expect(instance1.ionViewCanEnter).toHaveBeenCalled(); - expect(instance1.ionViewWillEnter).toHaveBeenCalled(); - expect(instance1.ionViewDidEnter).toHaveBeenCalled(); - expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLoad).toHaveBeenCalled(); + expect(instance1.ionViewCanEnter).toHaveBeenCalled(); + expect(instance1.ionViewWillEnter).toHaveBeenCalled(); + expect(instance1.ionViewDidEnter).toHaveBeenCalled(); + expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); - expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewWillLeave).toHaveBeenCalled(); - expect(instance2.ionViewDidLeave).toHaveBeenCalled(); - expect(instance2.ionViewWillUnload).toHaveBeenCalled(); + expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewWillLeave).toHaveBeenCalled(); + expect(instance2.ionViewDidLeave).toHaveBeenCalled(); + expect(instance2.ionViewWillUnload).toHaveBeenCalled(); - expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance3.ionViewWillLeave).toHaveBeenCalled(); - expect(instance3.ionViewDidLeave).toHaveBeenCalled(); - expect(instance3.ionViewWillUnload).toHaveBeenCalled(); + expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance3.ionViewWillLeave).toHaveBeenCalled(); + expect(instance3.ionViewDidLeave).toHaveBeenCalled(); + expect(instance3.ionViewWillUnload).toHaveBeenCalled(); - expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewCanLeave).toHaveBeenCalled(); - expect(instance4.ionViewWillLeave).toHaveBeenCalled(); - expect(instance4.ionViewDidLeave).toHaveBeenCalled(); - expect(instance4.ionViewWillUnload).toHaveBeenCalled(); + expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewCanLeave).toHaveBeenCalled(); + expect(instance4.ionViewWillLeave).toHaveBeenCalled(); + expect(instance4.ionViewDidLeave).toHaveBeenCalled(); + expect(instance4.ionViewWillUnload).toHaveBeenCalled(); - let hasCompleted = true; - let requiresTransition = true; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, 'MockView1', 'MockView4', DIRECTION_BACK - ); - expect(nav.length()).toEqual(1); - expect(nav.getByIndex(0).component).toEqual(MockView1); - }); + let hasCompleted = true; + let requiresTransition = true; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, 'MockView1', 'MockView4', DIRECTION_BACK + ); + expect(nav.length()).toEqual(1); + expect(nav.getByIndex(0).component).toEqual(MockView1); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); }); describe('popToRoot', () => { - it('should pop to the first view', () => { + it('should pop to the first view', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); let view3 = mockView(MockView3); @@ -528,58 +570,63 @@ describe('NavController', () => { let instance3 = spyOnLifecycles(view3); let instance4 = spyOnLifecycles(view4); - nav.popToRoot(null, trnsDone); + nav.popToRoot(null, trnsDone).then(() => { - expect(instance1.ionViewDidLoad).toHaveBeenCalled(); - expect(instance1.ionViewCanEnter).toHaveBeenCalled(); - expect(instance1.ionViewWillEnter).toHaveBeenCalled(); - expect(instance1.ionViewDidEnter).toHaveBeenCalled(); - expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLoad).toHaveBeenCalled(); + expect(instance1.ionViewCanEnter).toHaveBeenCalled(); + expect(instance1.ionViewWillEnter).toHaveBeenCalled(); + expect(instance1.ionViewDidEnter).toHaveBeenCalled(); + expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); - expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewWillLeave).toHaveBeenCalled(); - expect(instance2.ionViewDidLeave).toHaveBeenCalled(); - expect(instance2.ionViewWillUnload).toHaveBeenCalled(); + expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewWillLeave).toHaveBeenCalled(); + expect(instance2.ionViewDidLeave).toHaveBeenCalled(); + expect(instance2.ionViewWillUnload).toHaveBeenCalled(); - expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance3.ionViewWillLeave).toHaveBeenCalled(); - expect(instance3.ionViewDidLeave).toHaveBeenCalled(); - expect(instance3.ionViewWillUnload).toHaveBeenCalled(); + expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance3.ionViewWillLeave).toHaveBeenCalled(); + expect(instance3.ionViewDidLeave).toHaveBeenCalled(); + expect(instance3.ionViewWillUnload).toHaveBeenCalled(); - expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewCanLeave).toHaveBeenCalled(); - expect(instance4.ionViewWillLeave).toHaveBeenCalled(); - expect(instance4.ionViewDidLeave).toHaveBeenCalled(); - expect(instance4.ionViewWillUnload).toHaveBeenCalled(); + expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewCanLeave).toHaveBeenCalled(); + expect(instance4.ionViewWillLeave).toHaveBeenCalled(); + expect(instance4.ionViewDidLeave).toHaveBeenCalled(); + expect(instance4.ionViewWillUnload).toHaveBeenCalled(); - let hasCompleted = true; - let requiresTransition = true; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, 'MockView1', 'MockView4', DIRECTION_BACK - ); - expect(nav.length()).toEqual(1); - expect(nav.getByIndex(0).component).toEqual(MockView1); - }); + let hasCompleted = true; + let requiresTransition = true; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, 'MockView1', 'MockView4', DIRECTION_BACK + ); + expect(nav.length()).toEqual(1); + expect(nav.getByIndex(0).component).toEqual(MockView1); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); }); describe('remove', () => { - it('should remove the first three views in the beginning, no last view transition', () => { + it('should remove the first three views in the beginning, no last view transition', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); let view3 = mockView(MockView3); @@ -591,54 +638,59 @@ describe('NavController', () => { let instance3 = spyOnLifecycles(view3); let instance4 = spyOnLifecycles(view4); - nav.remove(0, 3, null, trnsDone); + nav.remove(0, 3, null, trnsDone).then(() => { - expect(instance1.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance1.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillLeave).toHaveBeenCalled(); - expect(instance1.ionViewDidLeave).toHaveBeenCalled(); - expect(instance1.ionViewWillUnload).toHaveBeenCalled(); + expect(instance1.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance1.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillLeave).toHaveBeenCalled(); + expect(instance1.ionViewDidLeave).toHaveBeenCalled(); + expect(instance1.ionViewWillUnload).toHaveBeenCalled(); - expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewWillLeave).toHaveBeenCalled(); - expect(instance2.ionViewDidLeave).toHaveBeenCalled(); - expect(instance2.ionViewWillUnload).toHaveBeenCalled(); + expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewWillLeave).toHaveBeenCalled(); + expect(instance2.ionViewDidLeave).toHaveBeenCalled(); + expect(instance2.ionViewWillUnload).toHaveBeenCalled(); - expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance3.ionViewWillLeave).toHaveBeenCalled(); - expect(instance3.ionViewDidLeave).toHaveBeenCalled(); - expect(instance3.ionViewWillUnload).toHaveBeenCalled(); + expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance3.ionViewWillLeave).toHaveBeenCalled(); + expect(instance3.ionViewDidLeave).toHaveBeenCalled(); + expect(instance3.ionViewWillUnload).toHaveBeenCalled(); - expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance4.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance4.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance4.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance4.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance4.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance4.ionViewWillUnload).not.toHaveBeenCalled(); - let hasCompleted = true; - let requiresTransition = false; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, undefined, undefined, undefined - ); - expect(nav.length()).toEqual(1); - expect(nav.getByIndex(0).component).toEqual(MockView4); - }); + let hasCompleted = true; + let requiresTransition = false; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, undefined, undefined, undefined + ); + expect(nav.length()).toEqual(1); + expect(nav.getByIndex(0).component).toEqual(MockView4); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); - it('should remove two views in the middle', () => { + it('should remove two views in the middle', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); let view3 = mockView(MockView3); @@ -652,65 +704,70 @@ describe('NavController', () => { let instance4 = spyOnLifecycles(view4); let instance5 = spyOnLifecycles(view5); - nav.remove(2, 2, null, trnsDone); + nav.remove(2, 2, null, trnsDone).then(() => { - expect(instance1.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance1.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance1.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); - expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance2.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance2.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewWillUnload).not.toHaveBeenCalled(); - expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance3.ionViewWillLeave).toHaveBeenCalled(); - expect(instance3.ionViewDidLeave).toHaveBeenCalled(); - expect(instance3.ionViewWillUnload).toHaveBeenCalled(); + expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance3.ionViewWillLeave).toHaveBeenCalled(); + expect(instance3.ionViewDidLeave).toHaveBeenCalled(); + expect(instance3.ionViewWillUnload).toHaveBeenCalled(); - expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance4.ionViewWillLeave).toHaveBeenCalled(); - expect(instance4.ionViewDidLeave).toHaveBeenCalled(); - expect(instance4.ionViewWillUnload).toHaveBeenCalled(); + expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance4.ionViewWillLeave).toHaveBeenCalled(); + expect(instance4.ionViewDidLeave).toHaveBeenCalled(); + expect(instance4.ionViewWillUnload).toHaveBeenCalled(); - expect(instance5.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance5.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance5.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance5.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance5.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance5.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance5.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance5.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance5.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance5.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance5.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance5.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance5.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance5.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance5.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance5.ionViewWillUnload).not.toHaveBeenCalled(); - let hasCompleted = true; - let requiresTransition = false; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, undefined, undefined, undefined - ); - expect(nav.length()).toEqual(3); - expect(nav.getByIndex(0).component).toEqual(MockView1); - expect(nav.getByIndex(1).component).toEqual(MockView2); - expect(nav.getByIndex(2).component).toEqual(MockView5); - }); + let hasCompleted = true; + let requiresTransition = false; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, undefined, undefined, undefined + ); + expect(nav.length()).toEqual(3); + expect(nav.getByIndex(0).component).toEqual(MockView1); + expect(nav.getByIndex(1).component).toEqual(MockView2); + expect(nav.getByIndex(2).component).toEqual(MockView5); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); - it('should remove the last two views at the end', () => { + it('should remove the last two views at the end', (done: Function) => { let view1 = mockView(MockView1); let view2 = mockView(MockView2); let view3 = mockView(MockView3); @@ -722,53 +779,58 @@ describe('NavController', () => { let instance3 = spyOnLifecycles(view3); let instance4 = spyOnLifecycles(view4); - nav.remove(2, 2, null, trnsDone); + nav.remove(2, 2, null, trnsDone).then(() => { - expect(instance1.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance1.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance1.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance1.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance1.ionViewWillUnload).not.toHaveBeenCalled(); - expect(instance2.ionViewDidLoad).toHaveBeenCalled(); - expect(instance2.ionViewCanEnter).toHaveBeenCalled(); - expect(instance2.ionViewWillEnter).toHaveBeenCalled(); - expect(instance2.ionViewDidEnter).toHaveBeenCalled(); - expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewWillLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewDidLeave).not.toHaveBeenCalled(); - expect(instance2.ionViewWillUnload).not.toHaveBeenCalled(); + expect(instance2.ionViewDidLoad).toHaveBeenCalled(); + expect(instance2.ionViewCanEnter).toHaveBeenCalled(); + expect(instance2.ionViewWillEnter).toHaveBeenCalled(); + expect(instance2.ionViewDidEnter).toHaveBeenCalled(); + expect(instance2.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewWillLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewDidLeave).not.toHaveBeenCalled(); + expect(instance2.ionViewWillUnload).not.toHaveBeenCalled(); - expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); - expect(instance3.ionViewWillLeave).toHaveBeenCalled(); - expect(instance3.ionViewDidLeave).toHaveBeenCalled(); - expect(instance3.ionViewWillUnload).toHaveBeenCalled(); + expect(instance3.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance3.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance3.ionViewCanLeave).not.toHaveBeenCalled(); + expect(instance3.ionViewWillLeave).toHaveBeenCalled(); + expect(instance3.ionViewDidLeave).toHaveBeenCalled(); + expect(instance3.ionViewWillUnload).toHaveBeenCalled(); - expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); - expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); - expect(instance4.ionViewCanLeave).toHaveBeenCalled(); - expect(instance4.ionViewWillLeave).toHaveBeenCalled(); - expect(instance4.ionViewDidLeave).toHaveBeenCalled(); - expect(instance4.ionViewWillUnload).toHaveBeenCalled(); + expect(instance4.ionViewDidLoad).not.toHaveBeenCalled(); + expect(instance4.ionViewCanEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewWillEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewDidEnter).not.toHaveBeenCalled(); + expect(instance4.ionViewCanLeave).toHaveBeenCalled(); + expect(instance4.ionViewWillLeave).toHaveBeenCalled(); + expect(instance4.ionViewDidLeave).toHaveBeenCalled(); + expect(instance4.ionViewWillUnload).toHaveBeenCalled(); - let hasCompleted = true; - let requiresTransition = true; - expect(trnsDone).toHaveBeenCalledWith( - hasCompleted, requiresTransition, 'MockView2', 'MockView4', DIRECTION_BACK - ); - expect(nav.length()).toEqual(2); - expect(nav.getByIndex(0).component).toEqual(MockView1); - expect(nav.getByIndex(1).component).toEqual(MockView2); - }); + let hasCompleted = true; + let requiresTransition = true; + expect(trnsDone).toHaveBeenCalledWith( + hasCompleted, requiresTransition, 'MockView2', 'MockView4', DIRECTION_BACK + ); + expect(nav.length()).toEqual(2); + expect(nav.getByIndex(0).component).toEqual(MockView1); + expect(nav.getByIndex(1).component).toEqual(MockView2); + done(); + }).catch((err: Error) => { + fail(err); + done(err); + }); + }, 10000); }); @@ -821,9 +883,10 @@ describe('NavController', () => { expect(nav.getByIndex(0).component).toEqual(MockView3); done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should set a ViewController as the root when its the middle view, with transition', (done: Function) => { let view1 = mockView(MockView1); @@ -873,9 +936,10 @@ describe('NavController', () => { done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should set a ViewController as the root when its the first view, with transition', (done: Function) => { let view1 = mockView(MockView1); @@ -925,9 +989,10 @@ describe('NavController', () => { done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); it('should set a page component as the root, with transition', (done: Function) => { let view1 = mockView(MockView1); @@ -953,9 +1018,10 @@ describe('NavController', () => { expect(nav.getByIndex(0).component).toEqual(MockView4); done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); }); describe('setPages', () => { @@ -982,9 +1048,10 @@ describe('NavController', () => { expect(nav.getByIndex(1).component).toEqual(MockView5); done(); }).catch((err: Error) => { + fail(err); done(err); }); - }); + }, 10000); }); diff --git a/src/navigation/test/overlay-proxy.spec.ts b/src/navigation/test/overlay-proxy.spec.ts index 16693e5c7c..79b899ffa4 100644 --- a/src/navigation/test/overlay-proxy.spec.ts +++ b/src/navigation/test/overlay-proxy.spec.ts @@ -16,6 +16,7 @@ describe('Overlay Proxy', () => { expect(instance.overlay.dismiss).toHaveBeenCalled(); done(); }).catch((err: Error) => { + fail(err); done(err); }); }); @@ -69,6 +70,7 @@ describe('Overlay Proxy', () => { expect(knownOverlay.onWillDismiss).toHaveBeenCalledWith(handler); done(); }).catch((err: Error) => { + fail(err); done(err); }); }); @@ -91,6 +93,7 @@ describe('Overlay Proxy', () => { expect(deepLinker.getComponentFromName).not.toHaveBeenCalled(); done(); }).catch((err: Error) => { + fail(err); done(err); }); }); @@ -112,6 +115,7 @@ describe('Overlay Proxy', () => { expect(deepLinker.getComponentFromName).toHaveBeenCalledWith(componentName); done(); }).catch((err: Error) => { + fail(err); done(err); }); }); diff --git a/src/navigation/view-controller.ts b/src/navigation/view-controller.ts index b5591f5557..4996028c93 100644 --- a/src/navigation/view-controller.ts +++ b/src/navigation/view-controller.ts @@ -541,26 +541,24 @@ export class ViewController { /** * @hidden */ - _lifecycleTest(lifecycle: string): boolean | Promise { + _lifecycleTest(lifecycle: string): Promise { const instance = this.instance; const methodName = 'ionViewCan' + lifecycle; if (instance && instance[methodName]) { try { var result = instance[methodName](); - if (result === false) { - return false; - } else if (result instanceof Promise) { + if (result instanceof Promise) { return result; } else { - return true; + // Any value but explitic false, should be true + return Promise.resolve(result !== false); } } catch (e) { - console.error(`${this.name} ${methodName} error: ${e.message}`); - return false; + return Promise.reject(`${this.name} ${methodName} error: ${e.message}`); } } - return true; + return Promise.resolve(true); } _lifecycle(lifecycle: string) { diff --git a/src/transitions/transition-controller.ts b/src/transitions/transition-controller.ts index cc7ba3468f..93004f2938 100644 --- a/src/transitions/transition-controller.ts +++ b/src/transitions/transition-controller.ts @@ -20,12 +20,12 @@ export class TransitionController { constructor(public plt: Platform, private _config: Config) {} getRootTrnsId(nav: NavControllerBase): number { - let parent = nav.parent; - while (parent) { - if (isPresent(parent._trnsId)) { - return parent._trnsId; + nav = nav.parent; + while (nav) { + if (isPresent(nav._trnsId)) { + return nav._trnsId; } - parent = parent.parent; + nav = nav.parent; } return null; } diff --git a/src/transitions/transition.ts b/src/transitions/transition.ts index 0847b970b8..fad72a7141 100644 --- a/src/transitions/transition.ts +++ b/src/transitions/transition.ts @@ -24,7 +24,12 @@ export class Transition extends Animation { parent: Transition; trnsId: number; - constructor(plt: Platform, public enteringView: ViewController, public leavingView: ViewController, opts: AnimationOptions) { + constructor( + plt: Platform, + public enteringView: ViewController, + public leavingView: ViewController, + opts: AnimationOptions + ) { super(plt, null, opts); } @@ -34,13 +39,12 @@ export class Transition extends Animation { this._trnsStart = trnsStart; } - isRoot(): boolean { - return !this.parent; - } - start() { this._trnsStart && this._trnsStart(); this._trnsStart = null; + + // bubble up start + this.parent && this.parent.start(); } destroy() { diff --git a/src/util/test/module-loader.spec.ts b/src/util/test/module-loader.spec.ts index b53c3465ff..32ecc0bdf6 100644 --- a/src/util/test/module-loader.spec.ts +++ b/src/util/test/module-loader.spec.ts @@ -19,7 +19,8 @@ describe('module-loader', () => { promise.then((response) => { expect(ngModuleLoader.load).toHaveBeenCalledWith(pathPrefix, exportSuffix); }).catch((err: Error) => { - done(err); + fail(err); + done(err); }); });