1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00

[PM-23197] update cipherService to return decCiphers (#15433)

* update cipherService to return decCiphers, update input to use signal, refactor observable, update spec
This commit is contained in:
Jason Ng
2025-07-03 17:35:50 -04:00
committed by GitHub
parent b4d87007ba
commit c7fc9b88fc
4 changed files with 57 additions and 59 deletions

View File

@@ -383,7 +383,7 @@ export class CipherService implements CipherServiceAbstraction {
const decCiphers = await this.getDecryptedCiphers(userId); const decCiphers = await this.getDecryptedCiphers(userId);
if (decCiphers != null && decCiphers.length !== 0) { if (decCiphers != null && decCiphers.length !== 0) {
await this.reindexCiphers(userId); await this.reindexCiphers(userId);
return await this.getDecryptedCiphers(userId); return decCiphers;
} }
const decrypted = await this.decryptCiphers(await this.getAll(userId), userId); const decrypted = await this.decryptCiphers(await this.getAll(userId), userId);

View File

@@ -1,4 +1,4 @@
<ng-container *ngIf="showNewItemSpotlight"> <ng-container *ngIf="showNewItemSpotlight$ | async">
<bit-spotlight <bit-spotlight
[title]="nudgeTitle" [title]="nudgeTitle"
[subtitle]="nudgeBody" [subtitle]="nudgeBody"

View File

@@ -1,27 +1,30 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { ComponentRef } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ComponentFixture, TestBed } from "@angular/core/testing";
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { of } from "rxjs"; import { of } from "rxjs";
import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
import { AccountService, Account } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/sdk-internal"; import { CipherType } from "@bitwarden/sdk-internal";
import { FakeAccountService, mockAccountServiceWith } from "../../../../../common/spec";
import { NewItemNudgeComponent } from "./new-item-nudge.component"; import { NewItemNudgeComponent } from "./new-item-nudge.component";
describe("NewItemNudgeComponent", () => { describe("NewItemNudgeComponent", () => {
let component: NewItemNudgeComponent; let component: NewItemNudgeComponent;
let componentRef: ComponentRef<NewItemNudgeComponent>;
let fixture: ComponentFixture<NewItemNudgeComponent>; let fixture: ComponentFixture<NewItemNudgeComponent>;
let i18nService: MockProxy<I18nService>; let i18nService: MockProxy<I18nService>;
let accountService: MockProxy<AccountService>;
let nudgesService: MockProxy<NudgesService>; let nudgesService: MockProxy<NudgesService>;
const accountService: FakeAccountService = mockAccountServiceWith("test-user-id" as UserId);
beforeEach(async () => { beforeEach(async () => {
i18nService = mock<I18nService>({ t: (key: string) => key }); i18nService = mock<I18nService>({ t: (key: string) => key });
accountService = mock<AccountService>();
nudgesService = mock<NudgesService>(); nudgesService = mock<NudgesService>();
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
@@ -37,7 +40,8 @@ describe("NewItemNudgeComponent", () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(NewItemNudgeComponent); fixture = TestBed.createComponent(NewItemNudgeComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.configType = null; // Set to null for initial state componentRef = fixture.componentRef;
componentRef.setInput("configType", null); // Set a default type for testing
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -46,13 +50,11 @@ describe("NewItemNudgeComponent", () => {
}); });
it("should set nudge title and body for CipherType.Login type", async () => { it("should set nudge title and body for CipherType.Login type", async () => {
component.configType = CipherType.Login; componentRef.setInput("configType", CipherType.Login);
accountService.activeAccount$ = of({ id: "test-user-id" as UserId } as Account); fixture.detectChanges();
jest.spyOn(component, "checkHasSpotlightDismissed").mockResolvedValue(true); component.showNewItemSpotlight$.subscribe((value) => {
expect(value).toEqual(true);
await component.ngOnInit(); });
expect(component.showNewItemSpotlight).toBe(true);
expect(component.nudgeTitle).toBe("newLoginNudgeTitle"); expect(component.nudgeTitle).toBe("newLoginNudgeTitle");
expect(component.nudgeBody).toBe( expect(component.nudgeBody).toBe(
"newLoginNudgeBodyOne <strong>newLoginNudgeBodyBold</strong> newLoginNudgeBodyTwo", "newLoginNudgeBodyOne <strong>newLoginNudgeBodyBold</strong> newLoginNudgeBodyTwo",
@@ -61,39 +63,38 @@ describe("NewItemNudgeComponent", () => {
}); });
it("should set nudge title and body for CipherType.Card type", async () => { it("should set nudge title and body for CipherType.Card type", async () => {
component.configType = CipherType.Card; componentRef.setInput("configType", CipherType.Card);
accountService.activeAccount$ = of({ id: "test-user-id" as UserId } as Account); fixture.detectChanges();
jest.spyOn(component, "checkHasSpotlightDismissed").mockResolvedValue(true); component.showNewItemSpotlight$.subscribe((value) => {
expect(value).toEqual(true);
await component.ngOnInit(); });
expect(component.showNewItemSpotlight).toBe(true);
expect(component.nudgeTitle).toBe("newCardNudgeTitle"); expect(component.nudgeTitle).toBe("newCardNudgeTitle");
expect(component.nudgeBody).toBe("newCardNudgeBody"); expect(component.nudgeBody).toBe("newCardNudgeBody");
expect(component.dismissalNudgeType).toBe(NudgeType.NewCardItemStatus); expect(component.dismissalNudgeType).toBe(NudgeType.NewCardItemStatus);
}); });
it("should not show anything if spotlight has been dismissed", async () => { it("should not show anything if spotlight has been dismissed", async () => {
component.configType = CipherType.Identity; componentRef.setInput("configType", CipherType.Identity);
accountService.activeAccount$ = of({ id: "test-user-id" as UserId } as Account); fixture.detectChanges();
jest.spyOn(component, "checkHasSpotlightDismissed").mockResolvedValue(false); component.showNewItemSpotlight$.subscribe((value) => {
expect(value).toEqual(false);
await component.ngOnInit(); });
expect(component.showNewItemSpotlight).toBe(false);
expect(component.dismissalNudgeType).toBe(NudgeType.NewIdentityItemStatus); expect(component.dismissalNudgeType).toBe(NudgeType.NewIdentityItemStatus);
}); });
it("should set showNewItemSpotlight to false when user dismisses spotlight", async () => { it("should set showNewItemSpotlight to false when user dismisses spotlight", async () => {
component.showNewItemSpotlight = true; component.showNewItemSpotlight$ = of(true);
component.dismissalNudgeType = NudgeType.NewLoginItemStatus; component.dismissalNudgeType = NudgeType.NewLoginItemStatus;
component.activeUserId = "test-user-id" as UserId; const activeUserId = "test-user-id" as UserId;
component.activeUserId$ = of(activeUserId);
const dismissSpy = jest.spyOn(nudgesService, "dismissNudge").mockResolvedValue(); const dismissSpy = jest.spyOn(nudgesService, "dismissNudge").mockResolvedValue();
await component.dismissNewItemSpotlight(); await component.dismissNewItemSpotlight();
expect(component.showNewItemSpotlight).toBe(false); component.showNewItemSpotlight$.subscribe((value) => {
expect(dismissSpy).toHaveBeenCalledWith(NudgeType.NewLoginItemStatus, component.activeUserId); expect(value).toEqual(false);
});
expect(dismissSpy).toHaveBeenCalledWith(NudgeType.NewLoginItemStatus, activeUserId);
}); });
}); });

View File

@@ -1,6 +1,7 @@
import { NgIf } from "@angular/common"; import { AsyncPipe, NgIf } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core"; import { Component, input } from "@angular/core";
import { firstValueFrom } from "rxjs"; import { toObservable } from "@angular/core/rxjs-interop";
import { combineLatest, firstValueFrom, map, of, switchMap } from "rxjs";
import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component"; import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component";
@@ -13,12 +14,17 @@ import { CipherType } from "@bitwarden/sdk-internal";
@Component({ @Component({
selector: "vault-new-item-nudge", selector: "vault-new-item-nudge",
templateUrl: "./new-item-nudge.component.html", templateUrl: "./new-item-nudge.component.html",
imports: [NgIf, SpotlightComponent], imports: [NgIf, SpotlightComponent, AsyncPipe],
}) })
export class NewItemNudgeComponent implements OnInit { export class NewItemNudgeComponent {
@Input({ required: true }) configType: CipherType | null = null; configType = input.required<CipherType | null>();
activeUserId: UserId | null = null; activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
showNewItemSpotlight: boolean = false; showNewItemSpotlight$ = combineLatest([
this.activeUserId$,
toObservable(this.configType).pipe(map((cipherType) => this.mapToNudgeType(cipherType))),
]).pipe(
switchMap(([userId, nudgeType]) => this.nudgesService.showNudgeSpotlight$(nudgeType, userId)),
);
nudgeTitle: string = ""; nudgeTitle: string = "";
nudgeBody: string = ""; nudgeBody: string = "";
dismissalNudgeType: NudgeType | null = null; dismissalNudgeType: NudgeType | null = null;
@@ -29,10 +35,8 @@ export class NewItemNudgeComponent implements OnInit {
private nudgesService: NudgesService, private nudgesService: NudgesService,
) {} ) {}
async ngOnInit() { mapToNudgeType(cipherType: CipherType | null): NudgeType {
this.activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); switch (cipherType) {
switch (this.configType) {
case CipherType.Login: { case CipherType.Login: {
const nudgeBodyOne = this.i18nService.t("newLoginNudgeBodyOne"); const nudgeBodyOne = this.i18nService.t("newLoginNudgeBodyOne");
const nudgeBodyBold = this.i18nService.t("newLoginNudgeBodyBold"); const nudgeBodyBold = this.i18nService.t("newLoginNudgeBodyBold");
@@ -40,25 +44,25 @@ export class NewItemNudgeComponent implements OnInit {
this.dismissalNudgeType = NudgeType.NewLoginItemStatus; this.dismissalNudgeType = NudgeType.NewLoginItemStatus;
this.nudgeTitle = this.i18nService.t("newLoginNudgeTitle"); this.nudgeTitle = this.i18nService.t("newLoginNudgeTitle");
this.nudgeBody = `${nudgeBodyOne} <strong>${nudgeBodyBold}</strong> ${nudgeBodyTwo}`; this.nudgeBody = `${nudgeBodyOne} <strong>${nudgeBodyBold}</strong> ${nudgeBodyTwo}`;
break; return NudgeType.NewLoginItemStatus;
} }
case CipherType.Card: case CipherType.Card:
this.dismissalNudgeType = NudgeType.NewCardItemStatus; this.dismissalNudgeType = NudgeType.NewCardItemStatus;
this.nudgeTitle = this.i18nService.t("newCardNudgeTitle"); this.nudgeTitle = this.i18nService.t("newCardNudgeTitle");
this.nudgeBody = this.i18nService.t("newCardNudgeBody"); this.nudgeBody = this.i18nService.t("newCardNudgeBody");
break; return NudgeType.NewCardItemStatus;
case CipherType.Identity: case CipherType.Identity:
this.dismissalNudgeType = NudgeType.NewIdentityItemStatus; this.dismissalNudgeType = NudgeType.NewIdentityItemStatus;
this.nudgeTitle = this.i18nService.t("newIdentityNudgeTitle"); this.nudgeTitle = this.i18nService.t("newIdentityNudgeTitle");
this.nudgeBody = this.i18nService.t("newIdentityNudgeBody"); this.nudgeBody = this.i18nService.t("newIdentityNudgeBody");
break; return NudgeType.NewIdentityItemStatus;
case CipherType.SecureNote: case CipherType.SecureNote:
this.dismissalNudgeType = NudgeType.NewNoteItemStatus; this.dismissalNudgeType = NudgeType.NewNoteItemStatus;
this.nudgeTitle = this.i18nService.t("newNoteNudgeTitle"); this.nudgeTitle = this.i18nService.t("newNoteNudgeTitle");
this.nudgeBody = this.i18nService.t("newNoteNudgeBody"); this.nudgeBody = this.i18nService.t("newNoteNudgeBody");
break; return NudgeType.NewNoteItemStatus;
case CipherType.SshKey: { case CipherType.SshKey: {
const sshPartOne = this.i18nService.t("newSshNudgeBodyOne"); const sshPartOne = this.i18nService.t("newSshNudgeBodyOne");
@@ -67,25 +71,18 @@ export class NewItemNudgeComponent implements OnInit {
this.dismissalNudgeType = NudgeType.NewSshItemStatus; this.dismissalNudgeType = NudgeType.NewSshItemStatus;
this.nudgeTitle = this.i18nService.t("newSshNudgeTitle"); this.nudgeTitle = this.i18nService.t("newSshNudgeTitle");
this.nudgeBody = `${sshPartOne} <a href="https://bitwarden.com/help/ssh-agent" class="tw-text-primary-600 tw-font-bold" target="_blank">${sshPartTwo}</a>`; this.nudgeBody = `${sshPartOne} <a href="https://bitwarden.com/help/ssh-agent" class="tw-text-primary-600 tw-font-bold" target="_blank">${sshPartTwo}</a>`;
break; return NudgeType.NewSshItemStatus;
} }
default: default:
throw new Error("Unsupported cipher type"); throw new Error("Unsupported cipher type");
} }
this.showNewItemSpotlight = await this.checkHasSpotlightDismissed(
this.dismissalNudgeType as NudgeType,
this.activeUserId,
);
} }
async dismissNewItemSpotlight() { async dismissNewItemSpotlight() {
if (this.dismissalNudgeType && this.activeUserId) { const activeUserId = await firstValueFrom(this.activeUserId$);
await this.nudgesService.dismissNudge(this.dismissalNudgeType, this.activeUserId as UserId); if (this.dismissalNudgeType && activeUserId) {
this.showNewItemSpotlight = false; await this.nudgesService.dismissNudge(this.dismissalNudgeType, activeUserId as UserId);
this.showNewItemSpotlight$ = of(false);
} }
} }
async checkHasSpotlightDismissed(nudgeType: NudgeType, userId: UserId): Promise<boolean> {
return await firstValueFrom(this.nudgesService.showNudgeSpotlight$(nudgeType, userId));
}
} }