mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
(Vault) [PM-27543] Create PremiumSetupIntentRedirectGuard (#17143)
* add state definition * create abstraction, no-op, and web service * update service name to reflect file name * create redirect guard and add to web route * update service import * [PM-27543] Cleanup premiumInterestRedirectGuard * [PM-27543] Add tests for premium-interest-redirect guard * [PM-27543] Undo change to billing docs * [PM-27543] Add error handling to guard * [PM-27543] Improve tests * [PM-27543] Add callToAction query parameter --------- Co-authored-by: Shane <smelton@bitwarden.com>
This commit is contained in:
@@ -50,6 +50,7 @@ import {
|
||||
import { canAccessEmergencyAccess } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
||||
import { LockComponent } from "@bitwarden/key-management-ui";
|
||||
import { premiumInterestRedirectGuard } from "@bitwarden/web-vault/app/vault/guards/premium-interest-redirect/premium-interest-redirect.guard";
|
||||
|
||||
import { flagEnabled, Flags } from "../utils/flags";
|
||||
|
||||
@@ -630,7 +631,7 @@ const routes: Routes = [
|
||||
children: [
|
||||
{
|
||||
path: "vault",
|
||||
canActivate: [setupExtensionRedirectGuard],
|
||||
canActivate: [premiumInterestRedirectGuard, setupExtensionRedirectGuard],
|
||||
loadChildren: () => VaultModule,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { TestBed } from "@angular/core/testing";
|
||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from "@angular/router";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { PremiumInterestStateService } from "@bitwarden/angular/billing/services/premium-interest/premium-interest-state.service.abstraction";
|
||||
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
|
||||
import { premiumInterestRedirectGuard } from "./premium-interest-redirect.guard";
|
||||
|
||||
describe("premiumInterestRedirectGuard", () => {
|
||||
const _state = Object.freeze({}) as RouterStateSnapshot;
|
||||
const emptyRoute = Object.freeze({ queryParams: {} }) as ActivatedRouteSnapshot;
|
||||
|
||||
const account = {
|
||||
id: "account-id",
|
||||
} as Account;
|
||||
|
||||
const activeAccount$ = new BehaviorSubject<Account | null>(account);
|
||||
const createUrlTree = jest.fn();
|
||||
const getPremiumInterest = jest.fn().mockResolvedValue(false);
|
||||
const logError = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
getPremiumInterest.mockClear();
|
||||
createUrlTree.mockClear();
|
||||
logError.mockClear();
|
||||
activeAccount$.next(account);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: Router, useValue: { createUrlTree } },
|
||||
{ provide: AccountService, useValue: { activeAccount$ } },
|
||||
{
|
||||
provide: PremiumInterestStateService,
|
||||
useValue: { getPremiumInterest },
|
||||
},
|
||||
{ provide: LogService, useValue: { error: logError } },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
function runPremiumInterestGuard(route?: ActivatedRouteSnapshot) {
|
||||
// Run the guard within injection context so `inject` works as you'd expect
|
||||
// Pass state object to make TypeScript happy
|
||||
return TestBed.runInInjectionContext(async () =>
|
||||
premiumInterestRedirectGuard(route ?? emptyRoute, _state),
|
||||
);
|
||||
}
|
||||
|
||||
it("returns `true` when the user does not intend to setup premium", async () => {
|
||||
getPremiumInterest.mockResolvedValueOnce(false);
|
||||
|
||||
expect(await runPremiumInterestGuard()).toBe(true);
|
||||
});
|
||||
|
||||
it("redirects to premium subscription page when user intends to setup premium", async () => {
|
||||
const urlTree = { toString: () => "/settings/subscription/premium" };
|
||||
createUrlTree.mockReturnValueOnce(urlTree);
|
||||
getPremiumInterest.mockResolvedValueOnce(true);
|
||||
|
||||
const result = await runPremiumInterestGuard();
|
||||
|
||||
expect(createUrlTree).toHaveBeenCalledWith(["/settings/subscription/premium"], {
|
||||
queryParams: { callToAction: "upgradeToPremium" },
|
||||
});
|
||||
expect(result).toBe(urlTree);
|
||||
});
|
||||
|
||||
it("redirects to login when active account is missing", async () => {
|
||||
const urlTree = { toString: () => "/login" };
|
||||
createUrlTree.mockReturnValueOnce(urlTree);
|
||||
activeAccount$.next(null);
|
||||
|
||||
const result = await runPremiumInterestGuard();
|
||||
|
||||
expect(createUrlTree).toHaveBeenCalledWith(["/login"]);
|
||||
expect(result).toBe(urlTree);
|
||||
});
|
||||
|
||||
it("returns `true` and logs error when getPremiumInterest throws an error", async () => {
|
||||
const error = new Error("Premium interest check failed");
|
||||
getPremiumInterest.mockRejectedValueOnce(error);
|
||||
|
||||
expect(await runPremiumInterestGuard()).toBe(true);
|
||||
expect(logError).toHaveBeenCalledWith("Error in premiumInterestRedirectGuard", error);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { inject } from "@angular/core";
|
||||
import { CanActivateFn, Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { PremiumInterestStateService } from "@bitwarden/angular/billing/services/premium-interest/premium-interest-state.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
|
||||
export const premiumInterestRedirectGuard: CanActivateFn = async () => {
|
||||
const router = inject(Router);
|
||||
const accountService = inject(AccountService);
|
||||
const premiumInterestStateService = inject(PremiumInterestStateService);
|
||||
const logService = inject(LogService);
|
||||
|
||||
try {
|
||||
const currentAcct = await firstValueFrom(accountService.activeAccount$);
|
||||
|
||||
if (!currentAcct) {
|
||||
return router.createUrlTree(["/login"]);
|
||||
}
|
||||
|
||||
const intendsToSetupPremium = await premiumInterestStateService.getPremiumInterest(
|
||||
currentAcct.id,
|
||||
);
|
||||
|
||||
if (intendsToSetupPremium) {
|
||||
return router.createUrlTree(["/settings/subscription/premium"], {
|
||||
queryParams: { callToAction: "upgradeToPremium" },
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
logService.error("Error in premiumInterestRedirectGuard", error);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user