Skip to content

Commit

Permalink
docs: can-load guard #1008
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Wind authored and satanTime committed Aug 25, 2024
1 parent fd98e37 commit cae47b8
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 2 deletions.
4 changes: 2 additions & 2 deletions docs/articles/guides/routing-guard.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ The example below is applicable for all types of guards:
[CodeSandbox](https://codesandbox.io/p/sandbox/github/help-me-mom/ng-mocks-sandbox/tree/tests/?file=/src/examples/TestRoutingGuard/can-match.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanMatch),
[StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestRoutingGuard/can-match.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanMatch)
- `canLoad` -
[CodeSandbox](https://codesandbox.io/p/sandbox/github/help-me-mom/ng-mocks-sandbox/tree/tests/?file=/src/examples/TestRoutingGuard/can-match.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanMatch),
[StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestRoutingGuard/can-match.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanMatch)
[CodeSandbox](https://codesandbox.io/p/sandbox/github/help-me-mom/ng-mocks-sandbox/tree/tests/?file=/src/examples/TestRoutingGuard/can-load.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanLoad),
[StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestRoutingGuard/can-load.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanLoad)
- class guards (legacy) -
[CodeSandbox](https://codesandbox.io/p/sandbox/github/help-me-mom/ng-mocks-sandbox/tree/tests/?file=/src/examples/TestRoutingGuard/test.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3Atest),
[StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestRoutingGuard/test.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3Atest)
Expand Down
172 changes: 172 additions & 0 deletions examples/TestRoutingGuard/can-load.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { Location } from '@angular/common';
import {
Component,
inject,
Injectable,
NgModule,
VERSION,
} from '@angular/core';
import {
CanLoadFn,
Router,
RouterModule,
RouterOutlet,
} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { from } from 'rxjs';
import { mapTo } from 'rxjs/operators';

import {
MockBuilder,
MockRender,
NG_MOCKS_GUARDS,
NG_MOCKS_ROOT_PROVIDERS,
ngMocks,
} from 'ng-mocks';

// A simple service simulating login check.
// It will be replaced with it's mock copy.
@Injectable()
class LoginService {
public isLoggedIn = false;
}

// The canLoad guard we want to test.
const canLoadGuard: CanLoadFn = (route, segments) => {
if (route && segments && inject(LoginService).isLoggedIn) {
return true;
}

return from(inject(Router).navigate(['/login'])).pipe(mapTo(false));
};

// Another canLoad guard like in a real world example,
// which should be removed from testing to avoid side effects on the route.
const sideEffectCanLoadGuard: CanLoadFn = () => false;

// A simple component pretending to be a login form.
// It will be replaced with a mock copy.
@Component({
selector: 'login',
template: 'login',
})
class LoginComponent {}

// A simple component pretending to be a protected dashboard.
// It will be replaced with a mock copy.
@Component({
selector: 'dashboard',
template: 'dashboard',
})
class DashboardComponent {}

@NgModule({
declarations: [DashboardComponent],
imports: [
RouterModule.forChild([
{
path: 'dashboard',
component: DashboardComponent,
},
{
path: '**',
redirectTo: 'dashboard',
},
]),
],
exports: [],
})
class DashboardModule {}

// Definition of the routing module.
@NgModule({
declarations: [LoginComponent, DashboardComponent],
exports: [RouterModule],
imports: [
RouterModule.forRoot([
{
component: LoginComponent,
path: 'login',
},
{
canLoad: [canLoadGuard, sideEffectCanLoadGuard],
path: '',
loadChildren: () => DashboardModule,
},
]),
],
providers: [LoginService],
})
class TargetModule {}

describe('TestRoutingGuard:canLoad', () => {
// Because we want to test a canLoad guard, it means that we want to
// test it's integration with RouterModule.
// Therefore, RouterModule and guard should be kept,
// and the rest of the module which defines the route can be mocked.
// To configure the RouterModule for the test,
// RouterModule, RouterTestingModule.withRoutes([]), NG_MOCKS_ROOT_PROVIDERS
// should be specified as the first parameter of MockBuilder (with empty routes).
// The module with routes and the guard should be specified
// as the second parameter of MockBuilder.
// Then NG_MOCKS_GUARDS should be excluded to remove all guards,
// and canLoadGuard should be kept to let you test it.
beforeEach(() => {
return MockBuilder(
[
RouterModule,
RouterTestingModule.withRoutes([]),
NG_MOCKS_ROOT_PROVIDERS,
],
[TargetModule],
)
.exclude(NG_MOCKS_GUARDS)
.keep(canLoadGuard);
});

it('redirects to login', async () => {
if (Number.parseInt(VERSION.major, 10) < 7) {
pending('Need Angular 7+');

return;
}

const fixture = MockRender(RouterOutlet, {});
const router = ngMocks.get(Router);
const location = ngMocks.get(Location);

// First we need to initialize navigation.
if (fixture.ngZone) {
fixture.ngZone.run(() => router.initialNavigation());
// is needed to wait until routing is finished.
await fixture.whenStable();
}

// Because by default we are not logged, the guard should
// redirect us /login page.
expect(location.path()).toEqual('/login');
expect(() => ngMocks.find(LoginComponent)).not.toThrow();
});

it('loads dashboard', async () => {
const fixture = MockRender(RouterOutlet, {});
const router = ngMocks.get(Router);
const location = ngMocks.get(Location);
const loginService = ngMocks.get(LoginService);

// Letting the guard know we have been logged in.
loginService.isLoggedIn = true;

// First we need to initialize navigation.
if (fixture.ngZone) {
fixture.ngZone.run(() => router.initialNavigation());
// is needed to wait until routing is finished.
await fixture.whenStable();
}

// Because now we are logged in, the guard should let us land on
// the dashboard.
expect(location.path()).toEqual('/dashboard');
expect(() => ngMocks.find(DashboardComponent)).not.toThrow();
});
});

0 comments on commit cae47b8

Please sign in to comment.