1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-18 16:33:47 +00:00
Files
browser/apps/web/src/app/auth/guards/deep-link.guard.spec.ts
Ike f1691a5ef1 PM-1391-Added previous-url to global-state (#5733)
* added previous-url to global-state

* updated storage of previousUrl for SSO/MFA flows

* revert file changes

* added post login routing

* Clear PreviousUrl from storage on new Login

* Components do not call StateService anymore

* removed needed query params

* refactored components to use RouterService

* fixed build error

* fixed mfa component

* updated logic for previous Url

* removed unneeded base implementation

* Added state call for Redirect Guard

* Fixed test cases

* Remove routing service calls

* renamed global field, changed routing to guard

* reverting constructor changes and git lint issue

* fixing constructor ordering

* fixing diffs to be clearer on actual cahnges.

* addressing accepting emergency access case

* refactor and add locked state logic

* refactor name of guard to be more clear

* Added comments and tests

* comments + support lock page deep linking + code ownership

* readability updates

* Combined guards and specs updated routing

* Update oss-routing.module.ts

* fixed stroybook build
2023-11-22 08:54:12 -08:00

191 lines
6.4 KiB
TypeScript

import { Component } from "@angular/core";
import { TestBed } from "@angular/core/testing";
import { Router, provideRouter } from "@angular/router";
import { RouterTestingHarness } from "@angular/router/testing";
import { MockProxy, mock } from "jest-mock-extended";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { RouterService } from "../../core/router.service";
import { deepLinkGuard } from "./deep-link.guard";
@Component({
template: "",
})
export class GuardedRouteTestComponent {}
@Component({
template: "",
})
export class LockTestComponent {}
@Component({
template: "",
})
export class RedirectTestComponent {}
/**
* We are assuming the guard is always being called. We are creating routes using the
* RouterTestingHarness.
*
* when persisting a URL to storage we don't care wether or not the user is locked or logged out.
* We only care about where the user is going, and has been.
*
* We are testing the activatedComponent because we are testing that the guard redirects when a user is
* unlocked.
*/
describe("Deep Link Guard", () => {
let authService: MockProxy<AuthService>;
let routerService: MockProxy<RouterService>;
let routerHarness: RouterTestingHarness;
beforeEach(async () => {
authService = mock<AuthService>();
routerService = mock<RouterService>();
TestBed.configureTestingModule({
providers: [
{ provide: AuthService, useValue: authService },
{ provide: RouterService, useValue: routerService },
provideRouter([
{
path: "guarded-route",
component: GuardedRouteTestComponent,
canActivate: [deepLinkGuard()],
},
{
path: "lock-route",
component: LockTestComponent,
canActivate: [deepLinkGuard()],
},
{
path: "redirect-route",
component: RedirectTestComponent,
},
]),
],
});
routerHarness = await RouterTestingHarness.create();
});
// Story: User's vault times out
it('should persist routerService.previousUrl when routerService.previousUrl does not contain "lock"', async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Locked);
routerService.getPreviousUrl.mockReturnValue("/previous-url");
// Act
await routerHarness.navigateByUrl("/lock-route");
// Assert
expect(routerService.persistLoginRedirectUrl).toHaveBeenCalledWith("/previous-url");
});
// Story: User's vault times out and previousUrl contains "lock"
it('should not persist routerService.previousUrl when routerService.previousUrl contains "lock"', async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Locked);
routerService.getPreviousUrl.mockReturnValue("/lock");
// Act
await routerHarness.navigateByUrl("/lock-route");
// Assert
expect(routerService.persistLoginRedirectUrl).not.toHaveBeenCalled();
});
// Story: User's vault times out and previousUrl is undefined
it("should not persist routerService.previousUrl when routerService.previousUrl is undefined", async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Locked);
routerService.getPreviousUrl.mockReturnValue(undefined);
// Act
await routerHarness.navigateByUrl("/lock-route");
// Assert
expect(routerService.persistLoginRedirectUrl).not.toHaveBeenCalled();
});
// Story: User tries to deep link to a guarded route and is logged out
it('should persist currentUrl when currentUrl does not contain "lock"', async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.LoggedOut);
// Act
await routerHarness.navigateByUrl("/guarded-route?item=123");
// Assert
expect(routerService.persistLoginRedirectUrl).toHaveBeenCalledWith("/guarded-route?item=123");
});
// Story: User tries to deep link to "lock"
it('should not persist currentUrl if the currentUrl contains "lock"', async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.LoggedOut);
// Act
await routerHarness.navigateByUrl("/lock-route");
// Assert
expect(routerService.persistLoginRedirectUrl).not.toHaveBeenCalled();
});
// Story: User tries to deep link to a guarded route from the lock page
it("should persist currentUrl over previousUrl", async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Locked);
routerService.getPreviousUrl.mockReturnValue("/previous-url");
// Act
await routerHarness.navigateByUrl("/guarded-route?item=123");
// Assert
expect(routerService.persistLoginRedirectUrl).toHaveBeenCalledWith("/guarded-route?item=123");
});
// Story: user tries to deep link and is unlocked
it("should not persist any URL if the user is unlocked", async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked);
// Act
await routerHarness.navigateByUrl("/guarded-route");
// Assert
expect(routerService.persistLoginRedirectUrl).not.toHaveBeenCalled();
});
// Story: User is redirected
it("should redirect user", async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked);
routerService.getAndClearLoginRedirectUrl.mockResolvedValue("/redirect-route");
// Act
const activatedComponent = await routerHarness.navigateByUrl("/guarded-route");
// Assert
expect(routerService.persistLoginRedirectUrl).not.toHaveBeenCalled();
expect(TestBed.inject(Router).url).toEqual("/redirect-route");
expect(activatedComponent).toBeInstanceOf(RedirectTestComponent);
});
// Story: User is not redirected
it("should not redirect user", async () => {
// Arrange
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked);
routerService.getAndClearLoginRedirectUrl.mockResolvedValue("");
// Act
const activatedComponent = await routerHarness.navigateByUrl("/guarded-route");
// Assert
expect(routerService.persistLoginRedirectUrl).not.toHaveBeenCalled();
expect(TestBed.inject(Router).url).toEqual("/guarded-route");
expect(activatedComponent).toBeInstanceOf(GuardedRouteTestComponent);
});
});