Skip to content

Commit

Permalink
fix(vue): custom animation plays when replacing (#25863)
Browse files Browse the repository at this point in the history
resolves #25831
  • Loading branch information
liamdebeasi committed Sep 6, 2022
1 parent 629f1ed commit 2d3661a
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 8 deletions.
11 changes: 10 additions & 1 deletion packages/vue/src/components/IonRouterOutlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,18 @@ export const IonRouterOutlet = /*@__PURE__*/ defineComponent({
requestAnimationFrame(async () => {
enteringEl.classList.add('ion-page-invisible');

const hasRootDirection = direction === undefined || direction === 'root' || direction === 'none';
const result = await ionRouterOutlet.value.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined || direction === 'root' || direction === 'none' ? 0 : undefined,
/**
* replace operations result in a direction of none.
* These typically do not have need animations, so we set
* the duration to 0. However, if a developer explicitly
* passes an animationBuilder, we should assume that
* they want an animation to be played even
* though it is a replace operation.
*/
duration: hasRootDirection && animationBuilder === undefined ? 0 : undefined,
direction,
showGoBack,
progressAnimation,
Expand Down
4 changes: 3 additions & 1 deletion packages/vue/test/base/tests/unit/hooks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ describe('useIonRouter', () => {
await waitForRouter();

expect(router.currentRoute.value.path).toEqual('/page2');
expect(animFn).not.toHaveBeenCalled();

// Animation should still be called even though this is a replace operation
expect(animFn).toHaveBeenCalled();

expect(vm.ionRouter.canGoBack()).toEqual(false);
})
Expand Down
157 changes: 157 additions & 0 deletions packages/vue/test/base/tests/unit/router-outlet.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { enableAutoUnmount, mount } from '@vue/test-utils';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import {
IonicVue,
IonApp,
IonRouterOutlet,
IonPage,
useIonRouter,
createAnimation
} from '@ionic/vue';
import { onBeforeRouteLeave } from 'vue-router';
import { mockAnimation, waitForRouter } from './utils';

enableAutoUnmount(afterEach);

const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}

const BasePage = {
template: '<ion-page :data-pageid="name"></ion-page>',
components: { IonPage },
}

/**
* While these tests use useIonRouter,
* they are different from the tests in hook.spec.ts
* in that they are testing that the correct parameters
* are passed to IonRouterOutlet as opposed to hook.spec.ts
* which makes sure that the animation function is called when
* specifically using useIonRouter.
*/
describe('Routing', () => {
it('should have an animation duration of 0 if replacing without an explicit animation', async () => {
const Page1 = {
...BasePage,
setup() {
const ionRouter = useIonRouter();
const redirect = () => {
ionRouter.replace('/page2')
}

return { redirect }
}
};

const Page2 = {
...BasePage
};

const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Page1 },
{ path: '/page2', component: Page2 }
]
});

router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});

/**
* Mock the commit function on IonRouterOutlet
*/
const commitFn = jest.fn();
const routerOutlet = wrapper.findComponent(IonRouterOutlet);
routerOutlet.vm.$el.commit = commitFn;

// call redirect method on Page1
const cmp = wrapper.findComponent(Page1);
cmp.vm.redirect();
await waitForRouter();

expect(commitFn).toBeCalledWith(
/**
* We are not checking the first 2
* params in this test,
* so we can use expect.anything().
*/
expect.anything(),
expect.anything(),
expect.objectContaining({
direction: "none",
duration: 0,
animationBuilder: undefined
})
)
});

it('should have an animation duration of null if replacing with an explicit animation', async () => {
const animation = mockAnimation();
const Page1 = {
...BasePage,
setup() {
const ionRouter = useIonRouter();
const redirect = () => {
ionRouter.replace('/page2', animation)
}

return { redirect }
}
};

const Page2 = {
...BasePage
};

const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Page1 },
{ path: '/page2', component: Page2 }
]
});

router.push('/');
await router.isReady();
const wrapper = mount(App, {
global: {
plugins: [router, IonicVue]
}
});

/**
* Mock the commit function on IonRouterOutlet
*/
const commitFn = jest.fn();
const routerOutlet = wrapper.findComponent(IonRouterOutlet);
routerOutlet.vm.$el.commit = commitFn;

// call redirect method on Page1
const cmp = wrapper.findComponent(Page1);
cmp.vm.redirect();
await waitForRouter();

expect(commitFn).toBeCalledWith(
/**
* We are not checking the first 2
* params in this test,
* so we can use expect.anything().
*/
expect.anything(),
expect.anything(),
expect.objectContaining({
direction: "none",
duration: undefined,
animationBuilder: animation
})
)
});
});
8 changes: 2 additions & 6 deletions packages/vue/test/base/tests/unit/utils.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { flushPromises } from '@vue/test-utils';
import { createAnimation } from '@ionic/vue';

export const waitForRouter = async () => {
await flushPromises();
await new Promise((r) => setTimeout(r, 500));
}

export const mockAnimation = () => {
return jest.fn(() => {
return {
onFinish: () => {},
play: () => {}
}
})
return jest.fn(() => createAnimation());
}

0 comments on commit 2d3661a

Please sign in to comment.