mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-09 16:16:41 +08:00
feat(router): add navigation hooks (#21709)
This commit is contained in:
@ -70,9 +70,17 @@ export class Router implements ComponentInterface {
|
||||
}
|
||||
|
||||
@Listen('popstate', { target: 'window' })
|
||||
protected onPopState() {
|
||||
protected async onPopState() {
|
||||
const direction = this.historyDirection();
|
||||
const path = this.getPath();
|
||||
let path = this.getPath();
|
||||
|
||||
const canProceed = await this.runGuards(path);
|
||||
if (canProceed !== true) {
|
||||
if (typeof canProceed === 'object') {
|
||||
path = parsePath(canProceed.redirect);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
console.debug('[ion-router] URL changed -> update nav', path, direction);
|
||||
return this.writeNavStateRoot(path, direction);
|
||||
}
|
||||
@ -85,6 +93,21 @@ export class Router implements ComponentInterface {
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@Method()
|
||||
async canTransition() {
|
||||
const canProceed = await this.runGuards();
|
||||
if (canProceed !== true) {
|
||||
if (typeof canProceed === 'object') {
|
||||
return canProceed.redirect;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the specified URL.
|
||||
*
|
||||
@ -92,14 +115,25 @@ export class Router implements ComponentInterface {
|
||||
* @param direction The direction of the animation. Defaults to `"forward"`.
|
||||
*/
|
||||
@Method()
|
||||
push(url: string, direction: RouterDirection = 'forward', animation?: AnimationBuilder) {
|
||||
async push(url: string, direction: RouterDirection = 'forward', animation?: AnimationBuilder) {
|
||||
if (url.startsWith('.')) {
|
||||
url = (new URL(url, window.location.href)).pathname;
|
||||
}
|
||||
console.debug('[ion-router] URL pushed -> updating nav', url, direction);
|
||||
|
||||
const path = parsePath(url);
|
||||
const queryString = url.split('?')[1];
|
||||
let path = parsePath(url);
|
||||
let queryString = url.split('?')[1];
|
||||
|
||||
const canProceed = await this.runGuards(path);
|
||||
if (canProceed !== true) {
|
||||
if (typeof canProceed === 'object') {
|
||||
path = parsePath(canProceed.redirect);
|
||||
queryString = canProceed.redirect.split('?')[1];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.setPath(path, direction, queryString);
|
||||
return this.writeNavStateRoot(path, direction, animation);
|
||||
}
|
||||
@ -191,6 +225,7 @@ export class Router implements ComponentInterface {
|
||||
// lookup redirect rule
|
||||
const redirects = readRedirects(this.el);
|
||||
const redirect = routeRedirect(path, redirects);
|
||||
|
||||
let redirectFrom: string[] | null = null;
|
||||
if (redirect) {
|
||||
this.setPath(redirect.to!, direction);
|
||||
@ -237,6 +272,25 @@ export class Router implements ComponentInterface {
|
||||
}
|
||||
return resolve;
|
||||
}
|
||||
private async runGuards(to: string[] | null = this.getPath(), from: string[] | null = parsePath(this.previousPath)) {
|
||||
if (!to || !from) { return true; }
|
||||
|
||||
const routes = readRoutes(this.el);
|
||||
|
||||
const toChain = routerPathToChain(to, routes);
|
||||
const fromChain = routerPathToChain(from, routes);
|
||||
|
||||
const beforeEnterHook = toChain && toChain[toChain.length - 1].beforeEnter;
|
||||
const beforeLeaveHook = fromChain && fromChain[fromChain.length - 1].beforeLeave;
|
||||
|
||||
const canLeave = beforeLeaveHook ? await beforeLeaveHook() : true;
|
||||
if (canLeave === false || typeof canLeave === 'object') { return canLeave; }
|
||||
|
||||
const canEnter = beforeEnterHook ? await beforeEnterHook() : true;
|
||||
if (canEnter === false || typeof canEnter === 'object') { return canEnter; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async writeNavState(
|
||||
node: HTMLElement | undefined, chain: RouteChain, direction: RouterDirection,
|
||||
|
||||
128
core/src/components/router/test/guards/href.e2e.ts
Normal file
128
core/src/components/router/test/guards/href.e2e.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('router: guards - href - allow/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 1: beforeEnter: allow, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'allow');
|
||||
|
||||
const href = await page.$('#href');
|
||||
await href.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
test('router: guards - href - block/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 2: beforeEnter: block, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'block');
|
||||
|
||||
const href = await page.$('#href');
|
||||
await href.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
test('router: guards - href - redirect/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 3: beforeEnter: redirect, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'redirect');
|
||||
|
||||
const href = await page.$('#href');
|
||||
await href.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/test');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
test('router: guards - href - allow/block', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 4: beforeEnter: allow, beforeLeave: block
|
||||
await setBeforeLeaveHook(page, 'block');
|
||||
|
||||
const href = await page.$('#href');
|
||||
await href.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
});
|
||||
|
||||
// TODO this is an actual bug in the code.
|
||||
test('router: guards - href - allow/redirect', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 5: beforeEnter: allow, beforeLeave: redirect
|
||||
await setBeforeLeaveHook(page, 'redirect');
|
||||
|
||||
const href = await page.$('#href');
|
||||
await href.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/test');
|
||||
});
|
||||
|
||||
const checkUrl = async (page, url: string) => {
|
||||
const getUrl = await page.url();
|
||||
expect(getUrl).toContain(url);
|
||||
}
|
||||
|
||||
const setBeforeEnterHook = async (page, type: string) => {
|
||||
const button = await page.$(`ion-radio-group#beforeEnter ion-radio[value=${type}]`);
|
||||
await button.click();
|
||||
}
|
||||
|
||||
const setBeforeLeaveHook = async (page, type: string) => {
|
||||
const button = await page.$(`ion-radio-group#beforeLeave ion-radio[value=${type}]`);
|
||||
await button.click();
|
||||
}
|
||||
194
core/src/components/router/test/guards/index.html
Normal file
194
core/src/components/router/test/guards/index.html
Normal file
@ -0,0 +1,194 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Navigation Guards</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||
<style>
|
||||
.toolbar {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 100;
|
||||
width: 200px;
|
||||
background: white;
|
||||
box-shadow: 0px 1px 10px rgba(0,0,0,0.2);
|
||||
}
|
||||
</style>
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script> <script>
|
||||
|
||||
class HomePage extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Home Page</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-list>
|
||||
<ion-button id="router-push">router.push</ion-button><br>
|
||||
<ion-router-link href="/child">
|
||||
<ion-button id="router-link">ion-router-link</ion-button><br>
|
||||
</ion-router-link>
|
||||
<ion-button href="/child" id="href">href</ion-button>
|
||||
</ion-list>
|
||||
</ion-content>`;
|
||||
|
||||
const childButton = this.querySelector('#router-push');
|
||||
childButton.addEventListener('click', () => {
|
||||
const r = document.querySelector('ion-router');
|
||||
r.push('/child');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ChildPage extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons>
|
||||
<ion-back-button default-href="/test"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Child Page</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
</ion-content>`;
|
||||
}
|
||||
}
|
||||
|
||||
class TestPage extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons>
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Test Page</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
</ion-content>`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('home-page', HomePage);
|
||||
customElements.define('child-page', ChildPage);
|
||||
customElements.define('test-page', TestPage);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="toolbar">
|
||||
<ion-radio-group id="beforeEnter" value="allow">
|
||||
<ion-list-header>
|
||||
<ion-label>
|
||||
beforeEnter Behavior
|
||||
</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Allow Navigation</ion-label>
|
||||
<ion-radio value="allow"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Block Navigation</ion-label>
|
||||
<ion-radio value="block"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Redirect</ion-label>
|
||||
<ion-radio value="redirect"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
|
||||
<br><br>
|
||||
|
||||
<ion-radio-group id="beforeLeave" value="allow">
|
||||
<ion-list-header>
|
||||
<ion-label>
|
||||
beforeLeave Behavior
|
||||
</ion-label>
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Allow Navigation</ion-label>
|
||||
<ion-radio value="allow"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Block Navigation</ion-label>
|
||||
<ion-radio value="block"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Redirect</ion-label>
|
||||
<ion-radio value="redirect"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</div>
|
||||
|
||||
<ion-app>
|
||||
|
||||
<ion-router>
|
||||
<ion-route-redirect from="/" to="/home"></ion-route-redirect>
|
||||
<ion-route url="/home" component="home-page"></ion-route>
|
||||
<ion-route url="/test" component="test-page"></ion-route>
|
||||
<ion-route url="/child" component="child-page"></ion-route>
|
||||
</ion-router>
|
||||
|
||||
<ion-nav></ion-nav>
|
||||
|
||||
<script>
|
||||
const beforeEnterGroup = document.querySelector('ion-radio-group#beforeEnter');
|
||||
const beforeLeaveGroup = document.querySelector('ion-radio-group#beforeLeave');
|
||||
beforeEnterGroup.addEventListener('ionChange', (ev) => {
|
||||
switch (ev.detail.value) {
|
||||
case "redirect":
|
||||
page.beforeEnter = redirect;
|
||||
break;
|
||||
case "block":
|
||||
page.beforeEnter = block;
|
||||
break;
|
||||
default:
|
||||
page.beforeEnter = allow;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
beforeLeaveGroup.addEventListener('ionChange', (ev) => {
|
||||
switch (ev.detail.value) {
|
||||
case "redirect":
|
||||
page.beforeLeave = redirect;
|
||||
break;
|
||||
case "block":
|
||||
page.beforeLeave = block;
|
||||
break;
|
||||
default:
|
||||
page.beforeLeave = allow;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const redirect = (to = '/test') => { return { redirect: to }};
|
||||
const block = () => false;
|
||||
const allow = () => true;
|
||||
|
||||
const page = document.querySelector('ion-route[component="child-page"]');
|
||||
page.beforeEnter = allow;
|
||||
page.beforeLeave = allow;
|
||||
</script>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
128
core/src/components/router/test/guards/router-link.e2e.ts
Normal file
128
core/src/components/router/test/guards/router-link.e2e.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('router: guards - router-link - allow/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 1: beforeEnter: allow, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'allow');
|
||||
|
||||
const routerLink = await page.$('#router-link');
|
||||
await routerLink.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
test('router: guards - router-link - block/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 2: beforeEnter: block, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'block');
|
||||
|
||||
const routerLink = await page.$('#router-link');
|
||||
await routerLink.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
test('router: guards - router-link - redirect/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 3: beforeEnter: redirect, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'redirect');
|
||||
|
||||
const routerLink = await page.$('#router-link');
|
||||
await routerLink.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/test');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
test('router: guards - router-link - allow/block', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 4: beforeEnter: allow, beforeLeave: block
|
||||
await setBeforeLeaveHook(page, 'block');
|
||||
|
||||
const routerLink = await page.$('#router-link');
|
||||
await routerLink.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
});
|
||||
|
||||
// TODO this is an actual bug in the code.
|
||||
test('router: guards - router-link - allow/redirect', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 5: beforeEnter: allow, beforeLeave: redirect
|
||||
await setBeforeLeaveHook(page, 'redirect');
|
||||
|
||||
const routerLink = await page.$('#router-link');
|
||||
await routerLink.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/test');
|
||||
});
|
||||
|
||||
const checkUrl = async (page, url: string) => {
|
||||
const getUrl = await page.url();
|
||||
expect(getUrl).toContain(url);
|
||||
}
|
||||
|
||||
const setBeforeEnterHook = async (page, type: string) => {
|
||||
const button = await page.$(`ion-radio-group#beforeEnter ion-radio[value=${type}]`);
|
||||
await button.click();
|
||||
}
|
||||
|
||||
const setBeforeLeaveHook = async (page, type: string) => {
|
||||
const button = await page.$(`ion-radio-group#beforeLeave ion-radio[value=${type}]`);
|
||||
await button.click();
|
||||
}
|
||||
128
core/src/components/router/test/guards/router-push.e2e.ts
Normal file
128
core/src/components/router/test/guards/router-push.e2e.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('router: guards - router.push - allow/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 1: beforeEnter: allow, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'allow');
|
||||
|
||||
const routerPush = await page.$('#router-push');
|
||||
await routerPush.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
test('router: guards - router.push - block/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 2: beforeEnter: block, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'block');
|
||||
|
||||
const routerPush = await page.$('#router-push');
|
||||
await routerPush.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
test('router: guards - router.push - redirect/allow', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 3: beforeEnter: redirect, beforeLeave: allow
|
||||
await setBeforeEnterHook(page, 'redirect');
|
||||
|
||||
const routerPush = await page.$('#router-push');
|
||||
await routerPush.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/test');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/home');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
test('router: guards - router.push - allow/block', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 4: beforeEnter: allow, beforeLeave: block
|
||||
await setBeforeLeaveHook(page, 'block');
|
||||
|
||||
const routerPush = await page.$('#router-push');
|
||||
await routerPush.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
});
|
||||
|
||||
// TODO this is an actual bug in the code.
|
||||
test('router: guards - router.push - allow/redirect', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router/test/guards?ionic:_testing=true'
|
||||
});
|
||||
|
||||
// Test 5: beforeEnter: allow, beforeLeave: redirect
|
||||
await setBeforeLeaveHook(page, 'redirect');
|
||||
|
||||
const routerPush = await page.$('#router-push');
|
||||
await routerPush.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/child');
|
||||
|
||||
const backButton = await page.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
await checkUrl(page, '#/test');
|
||||
});
|
||||
|
||||
const checkUrl = async (page, url: string) => {
|
||||
const getUrl = await page.url();
|
||||
expect(getUrl).toContain(url);
|
||||
}
|
||||
|
||||
const setBeforeEnterHook = async (page, type: string) => {
|
||||
const button = await page.$(`ion-radio-group#beforeEnter ion-radio[value=${type}]`);
|
||||
await button.click();
|
||||
}
|
||||
|
||||
const setBeforeLeaveHook = async (page, type: string) => {
|
||||
const button = await page.$(`ion-radio-group#beforeLeave ion-radio[value=${type}]`);
|
||||
await button.click();
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { AnimationBuilder, ComponentProps } from '../../../interface';
|
||||
import { NavigationHookCallback } from '../../route/route-interface';
|
||||
|
||||
export interface HTMLStencilElement extends HTMLElement {
|
||||
componentOnReady(): Promise<this>;
|
||||
@ -36,6 +37,8 @@ export interface RouteEntry {
|
||||
id: string;
|
||||
path: string[];
|
||||
params: {[key: string]: any} | undefined;
|
||||
beforeLeave?: NavigationHookCallback;
|
||||
beforeEnter?: NavigationHookCallback;
|
||||
}
|
||||
|
||||
export interface RouteNode extends RouteEntry {
|
||||
|
||||
@ -29,6 +29,8 @@ export const readRouteNodes = (root: Element, node = root): RouteTree => {
|
||||
path: parsePath(readProp(el, 'url')),
|
||||
id: component.toLowerCase(),
|
||||
params: el.componentProps,
|
||||
beforeLeave: el.beforeLeave,
|
||||
beforeEnter: el.beforeEnter,
|
||||
children: readRouteNodes(root, el)
|
||||
};
|
||||
});
|
||||
@ -57,7 +59,9 @@ const flattenNode = (chain: RouteChain, routes: RouteChain[], node: RouteNode) =
|
||||
s.push({
|
||||
id: node.id,
|
||||
path: node.path,
|
||||
params: node.params
|
||||
params: node.params,
|
||||
beforeLeave: node.beforeLeave,
|
||||
beforeEnter: node.beforeEnter
|
||||
});
|
||||
|
||||
if (node.children.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user