Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions docs/articles/guides/routing-guard.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ The example below is applicable for all types of guards:
- `canLoad` -
[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)
- standalone applications (Angular 14+) -
[CodeSandbox](https://codesandbox.io/p/sandbox/github/help-me-mom/ng-mocks-sandbox/tree/tests/?file=/src/examples/TestRoutingGuard/standalone.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3Astandalone),
[StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestRoutingGuard/standalone.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3Astandalone)
- 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 Expand Up @@ -118,6 +121,50 @@ expect(location.path()).toEqual('/login');

Profit, [an example of a test for a functional guard](#live-example).

## Functional Guards In Standalone Applications

If your app uses `bootstrapApplication()` with `provideRouter(...)`, then you can provide the standalone router setup directly in the test.
This keeps the example aligned with standalone routing without introducing an extra `NgModule` only for testing.

```ts
beforeEach(() => {
return MockBuilder(TargetComponent)
.keep(NG_MOCKS_ROOT_PROVIDERS)
.provide(provideLocationMocks())
.provide(
provideRouter([
{
component: LoginComponent,
path: 'login',
},
{
canActivate: [canActivateGuard],
component: DashboardComponent,
path: '**',
},
]),
)
.mock(LoginComponent)
.mock(DashboardComponent);
});
```

Now render a standalone component with a router outlet, initialize navigation, and assert the redirected route:

```ts
const fixture = MockRender(TargetComponent);
const router = ngMocks.get(Router);

if (fixture.ngZone) {
fixture.ngZone.run(() => router.initialNavigation());
await fixture.whenStable();
}

expect(router.url).toEqual('/login');
```

The full runnable example is available in [the standalone routing guard example](#live-example).

## Class Guards (legacy)

If your code has guards which a classes and angular services,
Expand Down Expand Up @@ -158,6 +205,8 @@ Profit.

- [Try it on CodeSandbox](https://codesandbox.io/p/sandbox/github/help-me-mom/ng-mocks-sandbox/tree/tests/?file=/src/examples/TestRoutingGuard/can-activate.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanActivate)
- [Try it on StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestRoutingGuard/can-activate.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3AcanActivate)
- [Try the standalone example on CodeSandbox](https://codesandbox.io/p/sandbox/github/help-me-mom/ng-mocks-sandbox/tree/tests/?file=/src/examples/TestRoutingGuard/standalone.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3Astandalone)
- [Try the standalone example on StackBlitz](https://stackblitz.com/github/help-me-mom/ng-mocks-sandbox/tree/tests?file=src/examples/TestRoutingGuard/standalone.spec.ts&initialpath=%3Fspec%3DTestRoutingGuard%3Astandalone)

```ts title="https://github.com/help-me-mom/ng-mocks/blob/main/examples/TestRoutingGuard/can-activate.spec.ts"
import { Location } from '@angular/common';
Expand Down
106 changes: 106 additions & 0 deletions examples/TestRoutingGuard/standalone.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
Component,
inject,
Injectable,
VERSION,
} from '@angular/core';
import {
CanActivateFn,
Router,
RouterModule,
provideRouter,
} from '@angular/router';
import { from } from 'rxjs';
import { mapTo } from 'rxjs/operators';

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

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

// A guard we want to test.
const canActivateGuard: CanActivateFn = (route, state) => {
if (route && state && inject(LoginService).isLoggedIn) {
return true;
}

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

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

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

@Component({
imports: [RouterModule],
standalone: true,
template: '<router-outlet></router-outlet>',
})
class TargetComponent {}

describe('TestRoutingGuard:standalone', () => {
if (Number.parseInt(VERSION.major, 10) < 14) {
it('needs a14', () => {
expect(true).toBeTruthy();
});

return;
}

beforeEach(() => {
return MockBuilder(TargetComponent)
.keep(NG_MOCKS_ROOT_PROVIDERS)
.provide(
provideRouter([
{
component: LoginComponent,
path: 'login',
},
{
canActivate: [canActivateGuard],
component: DashboardComponent,
path: '**',
},
]),
)
.mock(LoginComponent)
.mock(DashboardComponent);
});

it('redirects to login', async () => {
const fixture = MockRender(TargetComponent);
const router = ngMocks.get(Router);

if (fixture.ngZone) {
fixture.ngZone.run(() => router.initialNavigation());
await fixture.whenStable();
}

expect(router.url).toEqual('/login');
});
});
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,16 @@
"clean:nx": "rm -Rf e2e/nx/node_modules/ng-mocks && rm -Rf e2e/nx/apps/a-nx/src/test",
"s:test:e2e": "npm run s:test:a5 && npm run s:test:a6 && npm run s:test:a7 && npm run s:test:a8 && npm run s:test:a9 && npm run s:test:a10 && npm run s:test:a11 && npm run s:test:a12 && npm run s:test:a13 && npm run s:test:a14 && npm run s:test:a15 && npm run s:test:a16 && npm run s:test:a17 && npm run s:test:a18 && npm run s:test:a19 && npm run s:test:a20 && npm run s:test:a21 && npm run s:test:a22 && npm run s:test:nx",
"s:test:a5": "npm run s:test:a5es5 && npm run s:test:a5es2015",
"s:test:a5es5": "P=e2e/a5es5/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/test.spec.ts && rm $P/examples/TestRoutingResolver/test.spec.ts && rm $P/tests/issue-4282/test.spec.ts && rm $P/tests/issue-4282/global.spec.ts && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts && rm $P/tests/mock-service/observable.spec.ts",
"s:test:a5es2015": "P=e2e/a5es2015/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/test.spec.ts && rm $P/examples/TestRoutingResolver/test.spec.ts && rm $P/tests/issue-4282/test.spec.ts && rm $P/tests/issue-4282/global.spec.ts && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts && rm $P/tests/mock-service/observable.spec.ts",
"s:test:a6": "P=e2e/a6/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a7": "P=e2e/a7/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a8": "P=e2e/a8/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a9": "P=e2e/a9/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a10": "P=e2e/a10/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a11": "P=e2e/a11/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a12": "P=e2e/a12/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a13": "P=e2e/a13/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a5es5": "P=e2e/a5es5/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/test.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/test.spec.ts && rm $P/tests/issue-4282/test.spec.ts && rm $P/tests/issue-4282/global.spec.ts && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts && rm $P/tests/mock-service/observable.spec.ts",
"s:test:a5es2015": "P=e2e/a5es2015/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/test.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/test.spec.ts && rm $P/tests/issue-4282/test.spec.ts && rm $P/tests/issue-4282/global.spec.ts && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts && rm $P/tests/mock-service/observable.spec.ts",
"s:test:a6": "P=e2e/a6/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a7": "P=e2e/a7/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a8": "P=e2e/a8/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a9": "P=e2e/a9/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a10": "P=e2e/a10/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a11": "P=e2e/a11/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a12": "P=e2e/a12/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a13": "P=e2e/a13/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P && rm $P/examples/TestRoutingGuard/can-*.spec.ts && rm $P/examples/TestRoutingGuard/standalone.spec.ts && rm $P/examples/TestRoutingResolver/fn.spec.ts",
"s:test:a14": "P=e2e/a14/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P",
"s:test:a15": "P=e2e/a15/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P",
"s:test:a16": "P=e2e/a16/src/test && rm -Rf $P && mkdir -p $P && cp -R tests $P && cp -R examples $P",
Expand Down