import { mount } from '@vue/test-utils'; import { createRouter, createWebHistory } from '@ionic/vue-router'; import { IonicVue, IonApp, IonRouterOutlet, IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel } from '@ionic/vue'; import { onBeforeRouteLeave } from 'vue-router'; import { waitForRouter } from './utils'; const App = { components: { IonApp, IonRouterOutlet }, template: '', } const BasePage = { template: '', components: { IonPage }, } describe('Routing', () => { beforeAll(() => { (HTMLElement.prototype as HTMLIonRouterOutletElement).commit = jest.fn(); }); it('should pass no props', async () => { const Page1 = { ...BasePage, props: { title: { type: String, default: 'Default Title' } } }; const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/', component: Page1 } ] }); router.push('/'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); const cmp = wrapper.findComponent(Page1); expect(cmp.props()).toEqual({ title: 'Default Title' }); }); it('should pass route props as an object', async () => { const Page1 = { ...BasePage, props: { title: { type: String, default: 'Default Title' } } }; const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/', component: Page1, props: { title: 'Page 1 Title' } } ] }); router.push('/'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); const cmp = wrapper.findComponent(Page1); expect(cmp.props()).toEqual({ title: 'Page 1 Title' }); }); it('should pass route props as a function', async () => { const Page1 = { ...BasePage, props: { title: { type: String, default: 'Default Title' } } }; const propsFn = jest.fn((route) => { return { title: `${route.params.id} Title` } }); const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/myPath/:id', component: Page1, props: propsFn }, { path: '/otherPage', component: Page1 } ] }); router.push('/myPath/123'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); const cmp = wrapper.findComponent(Page1); expect(cmp.props()).toEqual({ title: '123 Title' }); router.push('/otherPage'); await waitForRouter(); expect(propsFn.mock.calls.length).toBe(1); router.back(); await waitForRouter(); expect(propsFn.mock.calls.length).toBe(1); expect(cmp.props()).toEqual({ title: '123 Title' }); }); it('should pass route params as props', async () => { const Page1 = { ...BasePage, props: { title: { type: String, default: 'Default Title' } } }; const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/:title', component: Page1, props: true } ] }); router.push('/myPath'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); const cmp = wrapper.findComponent(Page1); expect(cmp.props()).toEqual({ title: 'myPath' }); }); it('should call vue router hooks properly', async () => { const leaveHook = jest.fn(); const Page1 = { ...BasePage, setup() { onBeforeRouteLeave(leaveHook); } }; 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] } }); // Navigate to 2nd page router.push('/page2'); await waitForRouter(); expect(leaveHook).toBeCalled(); }); // Verifies fix for https://github.com/ionic-team/ionic-framework/issues/22492 it('should show correct view when replacing', async () => { const Tabs = { components: { IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel }, template: ` Tab 1 Tab 2 `, } const Tab1 = { components: { IonPage }, template: `Tab 1` } const Tab2 = { components: { IonPage }, template: `Tab 2` } const Parent = { ...BasePage, template: `Parent Page` } const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/', redirect: '/tabs/tab1' }, { path: '/parent', component: Parent }, { path: '/tabs/', component: Tabs, children: [ { path: '/', redirect: 'tab1' }, { path: 'tab1', component: Tab1 }, { path: 'tab2', component: Tab2 } ]} ] }); router.push('/'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); // Go to Tab 2 const tabButtons = wrapper.findAllComponents(IonTabButton); tabButtons[1].trigger('click'); await waitForRouter(); expect(wrapper.findComponent(Tab2).exists()).toBe(true); expect(wrapper.findComponent(Parent).exists()).toBe(false); router.replace('/parent') await waitForRouter(); expect(wrapper.findComponent(Parent).exists()).toBe(true); expect(wrapper.findComponent(Tabs).exists()).toBe(false); router.replace('/tabs/tab1'); await waitForRouter(); expect(wrapper.findComponent(Parent).exists()).toBe(false); expect(wrapper.findComponent(Tab1).exists()).toBe(true); expect(wrapper.findComponent(Tab2).exists()).toBe(false); }); // Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23043 it('should show the latest props passed to a route', async () => { const Page1 = { ...BasePage, props: { title: { type: String, default: 'Default Title' } } }; const Home = { ...BasePage } const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/', component: Home }, { path: '/:title', component: Page1, props: true } ] }); router.push('/'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); router.push('/abc'); await waitForRouter(); const cmp = wrapper.findComponent(Page1); expect(cmp.props()).toEqual({ title: 'abc' }); router.back(); await waitForRouter(); router.push('/xyz'); await waitForRouter(); const cmpAgain = wrapper.findComponent(Page1); expect(cmpAgain.props()).toEqual({ title: 'xyz' }); }); // Verifies fix for https://github.com/ionic-team/ionic-framework/issues/23043 it('should call the props function again when params change', async () => { const Page1 = { ...BasePage, props: { title: { type: String, default: 'Default Title' } } }; const Home = { ...BasePage } const propsFn = jest.fn((route) => { return { title: `${route.params.id} Title` } }); const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/myPath/:id', component: Page1, props: propsFn }, { path: '/', component: Home } ] }); router.push('/'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); router.push('/myPath/123'); await waitForRouter(); const cmp = wrapper.findComponent(Page1); expect(propsFn.mock.calls.length).toBe(1); expect(cmp.props()).toEqual({ title: '123 Title' }); router.back(); await waitForRouter(); router.push('/myPath/abc'); await waitForRouter(); expect(propsFn.mock.calls.length).toBe(2); const cmpAgain = wrapper.findComponent(Page1); expect(cmpAgain.props()).toEqual({ title: 'abc Title' }); }); // Verifies fix for https://github.com/ionic-team/ionic-framework/pull/23189 it('should update props on a parameterized url', async () => { const Page = { props: { id: { type: String, default: 'Default ID' } }, components: { IonPage }, template: `{{ $props.id }}` } const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [ { path: '/page/:id', component: Page, props: true }, { path: '/', redirect: '/page/1' } ] }); router.push('/'); await router.isReady(); const wrapper = mount(App, { global: { plugins: [router, IonicVue] } }); const page = wrapper.findComponent(Page); expect(page.props()).toEqual({ id: '1' }); router.push('/page/2'); await waitForRouter(); expect(page.props()).toEqual({ id: '2' }); }); });