diff --git a/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts b/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts index 338632fef04f..fa5a38cdac60 100644 --- a/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts +++ b/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts @@ -2,12 +2,14 @@ import { ComponentRef } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { mock, MockProxy } from "jest-mock-extended"; -import { of } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { ClientType } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -21,6 +23,7 @@ describe("ItemDetailsV2Component", () => { let fixture: ComponentFixture; let componentRef: ComponentRef; let mockPlatformUtilsService: MockProxy; + let desktopMilestone3Flag$: BehaviorSubject; const cipher = { id: "cipher1", @@ -52,18 +55,28 @@ describe("ItemDetailsV2Component", () => { beforeEach(async () => { mockPlatformUtilsService = mock(); + mockPlatformUtilsService.getClientType.mockReturnValue(ClientType.Web); + desktopMilestone3Flag$ = new BehaviorSubject(false); await TestBed.configureTestingModule({ imports: [ItemDetailsV2Component], providers: [ { provide: I18nService, useValue: { t: (key: string) => key } }, - { provide: PlatformUtilsService, useValue: { getClientType: () => ClientType.Web } }, + { provide: PlatformUtilsService, useValue: mockPlatformUtilsService }, + { + provide: ConfigService, + useValue: { + getFeatureFlag$: (flag: FeatureFlag) => + flag === FeatureFlag.DesktopUiMigrationMilestone3 + ? desktopMilestone3Flag$.asObservable() + : of(false), + }, + }, { provide: EnvironmentService, useValue: { environment$: of({ getIconsUrl: () => "https://icons.example.com" }) }, }, { provide: DomainSettingsService, useValue: { showFavicons$: of(true) } }, - { provide: PlatformUtilsService, useValue: mockPlatformUtilsService }, ], }).compileComponents(); }); @@ -102,30 +115,37 @@ describe("ItemDetailsV2Component", () => { expect(owner).toBeNull(); }); - it("should show archive badge when cipher is archived and client is Desktop", () => { - jest.spyOn(mockPlatformUtilsService, "getClientType").mockReturnValue(ClientType.Desktop); + describe("showArchiveBadge", () => { + it("is true when cipher is archived on Desktop and DesktopUiMigrationMilestone3 is off", () => { + mockPlatformUtilsService.getClientType.mockReturnValue(ClientType.Desktop); + desktopMilestone3Flag$.next(false); + componentRef.setInput("cipher", { ...cipher, isArchived: true }); - const archivedCipher = { ...cipher, isArchived: true }; - componentRef.setInput("cipher", archivedCipher); + expect((component as any).showArchiveBadge()).toBe(true); + }); - expect((component as any).showArchiveBadge()).toBe(true); - }); - - it("should not show archive badge when cipher is not archived", () => { - jest.spyOn(mockPlatformUtilsService, "getClientType").mockReturnValue(ClientType.Desktop); + it("is false when DesktopUiMigrationMilestone3 is on (dialog renders its own badge)", () => { + mockPlatformUtilsService.getClientType.mockReturnValue(ClientType.Desktop); + desktopMilestone3Flag$.next(true); + componentRef.setInput("cipher", { ...cipher, isArchived: true }); - const unarchivedCipher = { ...cipher, isArchived: false }; - componentRef.setInput("cipher", unarchivedCipher); + expect((component as any).showArchiveBadge()).toBe(false); + }); - expect((component as any).showArchiveBadge()).toBe(false); - }); + it("is false when cipher is not archived", () => { + mockPlatformUtilsService.getClientType.mockReturnValue(ClientType.Desktop); + desktopMilestone3Flag$.next(false); + componentRef.setInput("cipher", { ...cipher, isArchived: false }); - it("should not show archive badge when client is not Desktop", () => { - jest.spyOn(mockPlatformUtilsService, "getClientType").mockReturnValue(ClientType.Web); + expect((component as any).showArchiveBadge()).toBe(false); + }); - const archivedCipher = { ...cipher, isArchived: true }; - componentRef.setInput("cipher", archivedCipher); + it("is false when client is not Desktop", () => { + mockPlatformUtilsService.getClientType.mockReturnValue(ClientType.Web); + desktopMilestone3Flag$.next(false); + componentRef.setInput("cipher", { ...cipher, isArchived: true }); - expect((component as any).showArchiveBadge()).toBe(false); + expect((component as any).showArchiveBadge()).toBe(false); + }); }); }); diff --git a/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts b/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts index 73e7c2706be3..1278cee68904 100644 --- a/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts +++ b/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts @@ -1,10 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { Component, computed, input, signal } from "@angular/core"; +import { Component, computed, inject, input, signal } from "@angular/core"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. import { toSignal } from "@angular/core/rxjs-interop"; -import { fromEvent, map, startWith } from "rxjs"; +import { fromEvent, map, of, startWith } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ClientType } from "@bitwarden/client-type"; @@ -13,6 +13,8 @@ import { CollectionTypes, } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -91,16 +93,23 @@ export class ItemDetailsV2Component { } }); + private readonly platformUtilsService = inject(PlatformUtilsService); + + private readonly configService = inject(ConfigService); + + private readonly desktopMilestone3Enabled = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.DesktopUiMigrationMilestone3) ?? of(false), + ); + protected readonly showArchiveBadge = computed(() => { return ( - this.cipher().isArchived && this.platformUtilsService.getClientType() === ClientType.Desktop + this.cipher().isArchived && + this.platformUtilsService.getClientType() === ClientType.Desktop && + !this.desktopMilestone3Enabled() ); }); - constructor( - private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, - ) {} + constructor(private i18nService: I18nService) {} toggleShowMore() { this.showAllDetails.update((value) => !value); diff --git a/libs/vault/src/vault-item-dialog/vault-item-dialog.component.ts b/libs/vault/src/vault-item-dialog/vault-item-dialog.component.ts index 719af61c9a2c..9191dd0eb902 100644 --- a/libs/vault/src/vault-item-dialog/vault-item-dialog.component.ts +++ b/libs/vault/src/vault-item-dialog/vault-item-dialog.component.ts @@ -33,6 +33,7 @@ import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; import { AsyncActionsModule, + BadgeModule, BitIconButtonComponent, ButtonModule, CenterPositionStrategy, @@ -130,6 +131,7 @@ export type VaultItemDialogResult = UnionOfValues; CommonModule, CipherFormModule, AsyncActionsModule, + BadgeModule, ItemModule, PremiumBadgeComponent, I18nPipe,