() },
{ provide: I18nService, useValue: { t: (key: string) => key } },
+ {
+ provide: EnvironmentService,
+ useValue: {
+ environment$: of({
+ getIconsUrl: () => "https://icons.example.com",
+ }),
+ },
+ },
+ {
+ provide: DomainSettingsService,
+ useValue: {
+ showFavicons$: of(true),
+ },
+ },
+ {
+ provide: CipherAuthorizationService,
+ useValue: {
+ canDeleteCipher$: jest.fn().mockReturnValue(of(true)),
+ },
+ },
],
}).compileComponents();
- const fixture = TestBed.createComponent(ArchiveComponent);
+ fixture = TestBed.createComponent(ArchiveComponent);
component = fixture.componentInstance;
});
@@ -137,4 +180,54 @@ describe("ArchiveComponent", () => {
expect(navigate).not.toHaveBeenCalled();
});
});
+
+ describe("clone menu option", () => {
+ const getBitMenuPanel = () => document.querySelector(".bit-menu-panel");
+
+ it("is shown when user has premium", async () => {
+ userHasPremium$.mockReturnValue(of(true));
+
+ const testFixture = TestBed.createComponent(ArchiveComponent);
+ testFixture.detectChanges();
+ await testFixture.whenStable();
+
+ const menuTrigger = testFixture.debugElement.query(By.css('button[aria-haspopup="menu"]'));
+ expect(menuTrigger).toBeTruthy();
+ (menuTrigger.nativeElement as HTMLButtonElement).click();
+ testFixture.detectChanges();
+
+ const menuPanel = getBitMenuPanel();
+ expect(menuPanel).toBeTruthy();
+
+ const menuButtons = menuPanel?.querySelectorAll("button[bitMenuItem]");
+ const cloneButtonFound = Array.from(menuButtons || []).some(
+ (btn) => btn.textContent?.trim() === "clone",
+ );
+
+ expect(cloneButtonFound).toBe(true);
+ });
+
+ it("is not shown when user does not have premium", async () => {
+ userHasPremium$.mockReturnValue(of(false));
+
+ const testFixture = TestBed.createComponent(ArchiveComponent);
+ testFixture.detectChanges();
+ await testFixture.whenStable();
+
+ const menuTrigger = testFixture.debugElement.query(By.css('button[aria-haspopup="menu"]'));
+ expect(menuTrigger).toBeTruthy();
+ (menuTrigger.nativeElement as HTMLButtonElement).click();
+ testFixture.detectChanges();
+
+ const menuPanel = getBitMenuPanel();
+ expect(menuPanel).toBeTruthy();
+
+ const menuButtons = menuPanel?.querySelectorAll("button[bitMenuItem]");
+ const cloneButtonFound = Array.from(menuButtons || []).some(
+ (btn) => btn.textContent?.trim() === "clone",
+ );
+
+ expect(cloneButtonFound).toBe(false);
+ });
+ });
});
diff --git a/apps/browser/src/vault/popup/settings/archive.component.ts b/apps/browser/src/vault/popup/settings/archive.component.ts
index 8a81d733039..a34609bd8f8 100644
--- a/apps/browser/src/vault/popup/settings/archive.component.ts
+++ b/apps/browser/src/vault/popup/settings/archive.component.ts
@@ -135,6 +135,10 @@ export class ArchiveComponent {
switchMap((userId) => this.cipherArchiveService.showSubscriptionEndedMessaging$(userId)),
);
+ protected userHasPremium$ = this.userId$.pipe(
+ switchMap((userId) => this.cipherArchiveService.userHasPremium$(userId)),
+ );
+
async navigateToPremium() {
await this.router.navigate(["/premium"]);
}
diff --git a/apps/cli/package.json b/apps/cli/package.json
index c80f79feff8..93658cce361 100644
--- a/apps/cli/package.json
+++ b/apps/cli/package.json
@@ -69,7 +69,7 @@
"browser-hrtime": "1.1.8",
"chalk": "4.1.2",
"commander": "14.0.0",
- "core-js": "3.47.0",
+ "core-js": "3.48.0",
"form-data": "4.0.4",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index 160ad4e867a..89b3b3ac5c6 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -38,6 +38,9 @@
"accessIntelligence": {
"message": "Access Intelligence"
},
+ "noApplicationsMatchTheseFilters": {
+ "message": "No applications match these filters"
+ },
"passwordRisk": {
"message": "Password Risk"
},
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.html
index 9af071a1268..2fa9fabf73d 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.html
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.html
@@ -43,5 +43,11 @@
[checkboxChange]="onCheckboxChange"
[showAppAtRiskMembers]="showAppAtRiskMembers"
>
+
+ @if (emptyTableExplanation()) {
+
+ {{ emptyTableExplanation() }}
+
+ }
}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.ts
index 07c531d6907..8cd0c2640f5 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.ts
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/applications.component.ts
@@ -104,6 +104,7 @@ export class ApplicationsComponent implements OnInit {
icon: " ",
},
]);
+ protected readonly emptyTableExplanation = signal("");
constructor(
protected i18nService: I18nService,
@@ -164,6 +165,12 @@ export class ApplicationsComponent implements OnInit {
this.dataSource.filter = (app) =>
filterFunction(app) &&
app.applicationName.toLowerCase().includes(searchText.toLowerCase());
+
+ if (this.dataSource?.filteredData?.length === 0) {
+ this.emptyTableExplanation.set(this.i18nService.t("noApplicationsMatchTheseFilters"));
+ } else {
+ this.emptyTableExplanation.set("");
+ }
});
}
diff --git a/package-lock.json b/package-lock.json
index 55873bdb40c..6352675d718 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -38,7 +38,7 @@
"bufferutil": "4.1.0",
"chalk": "4.1.2",
"commander": "14.0.0",
- "core-js": "3.47.0",
+ "core-js": "3.48.0",
"form-data": "4.0.4",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
@@ -205,7 +205,7 @@
"browser-hrtime": "1.1.8",
"chalk": "4.1.2",
"commander": "14.0.0",
- "core-js": "3.47.0",
+ "core-js": "3.48.0",
"form-data": "4.0.4",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
@@ -20879,9 +20879,9 @@
}
},
"node_modules/core-js": {
- "version": "3.47.0",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz",
- "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==",
+ "version": "3.48.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz",
+ "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
"hasInstallScript": true,
"license": "MIT",
"funding": {
diff --git a/package.json b/package.json
index 1a72c49d263..2cc60c08c9d 100644
--- a/package.json
+++ b/package.json
@@ -177,7 +177,7 @@
"bufferutil": "4.1.0",
"chalk": "4.1.2",
"commander": "14.0.0",
- "core-js": "3.47.0",
+ "core-js": "3.48.0",
"form-data": "4.0.4",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",