1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-12 22:44:11 +00:00

PM-15070 flag critical apps with unit tests

This commit is contained in:
voommen-livefront
2024-11-22 12:00:41 -06:00
parent 81884924f9
commit 522611cf3b
3 changed files with 113 additions and 4 deletions

View File

@@ -10,7 +10,7 @@
<bit-no-items [icon]="noItemsIcon" class="tw-text-main">
<ng-container slot="title">
<h2 class="tw-font-semibold mt-4">
{{ "noAppsInOrgTitle" | i18n: organization.name }}
{{ "noAppsInOrgTitle" | i18n: organization?.name }}
</h2>
</ng-container>
<ng-container slot="description">
@@ -83,9 +83,11 @@
<input
bitCheckbox
type="checkbox"
*ngIf="!r.isMarkedAsCritical"
[checked]="selectedUrls.has(r.name)"
(change)="onCheckboxChange(r.name, $event)"
/>
<i class="bwi bwi-star-f" *ngIf="r.isMarkedAsCritical"></i>
</td>
<td bitCell>
<span>{{ r.name }}</span>

View File

@@ -0,0 +1,99 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ActivatedRoute, convertToParamMap } from "@angular/router";
import { mock, MockProxy } from "jest-mock-extended";
import { of } from "rxjs";
// eslint-disable-next-line no-restricted-imports
import {
CriticalAppsApiService,
PasswordHealthReportApplicationsResponse,
} from "@bitwarden/bit-common/tools/reports/risk-insights";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { TableModule, ToastService } from "@bitwarden/components";
import { TableBodyDirective } from "@bitwarden/components/src/table/table.component";
import { LooseComponentsModule } from "../../shared";
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
import { AllApplicationsComponent } from "./all-applications.component";
describe("AllApplicationsComponent", () => {
const organizationServiceMock: OrganizationService = mock<OrganizationService>();
const configServiceMock: ConfigService = mock<ConfigService>();
const criticalAppsApiServiceMock: CriticalAppsApiService = mock<CriticalAppsApiService>();
const i18nService: MockProxy<I18nService> = mock<I18nService>();
let fixture: ComponentFixture<AllApplicationsComponent>;
let component: AllApplicationsComponent;
const activeRouteParams = convertToParamMap({ organizationId: "orgId" });
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AllApplicationsComponent, PipesModule, TableModule, LooseComponentsModule],
declarations: [TableBodyDirective],
providers: [
{ provide: CipherService, useValue: mock<CipherService>() },
{
provide: PasswordStrengthServiceAbstraction,
useValue: mock<PasswordStrengthServiceAbstraction>(),
},
{ provide: AuditService, useValue: mock<AuditService>() },
{ provide: I18nService, useValue: i18nService },
{ provide: ActivatedRoute, useValue: { paramMap: of(activeRouteParams), url: of([]) } },
{ provide: ToastService, useValue: mock<ToastService>() },
{ provide: OrganizationService, useValue: organizationServiceMock },
{ provide: ConfigService, useValue: configServiceMock },
{ provide: CriticalAppsApiService, useValue: criticalAppsApiServiceMock },
],
}).compileComponents();
});
beforeEach(async () => {
const data = new OrganizationData({} as any, {} as any);
const organization = new Organization(data);
organization.name = "orgName";
jest.spyOn(organizationServiceMock, "get$").mockReturnValue(of(organization));
const pwdHealthReportAppsResponse = [
{ id: "1", organizationId: "app1", uri: "" },
] as PasswordHealthReportApplicationsResponse[];
jest
.spyOn(criticalAppsApiServiceMock, "getCriticalApps")
.mockResolvedValue(Promise.resolve(pwdHealthReportAppsResponse));
jest.spyOn(configServiceMock, "getFeatureFlag").mockResolvedValue(true);
fixture = TestBed.createComponent(AllApplicationsComponent);
component = fixture.componentInstance;
await component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
});
it("should initialize component", () => {
expect(component).toBeTruthy();
});
it("should add to selectedUrls on checkbox change", async () => {
const event = { target: { checked: true } as HTMLInputElement } as unknown as Event;
component.onCheckboxChange("app1", event);
expect(component.getSelectedUrls().length).toBe(1);
});
it("should invoke CriticalAppsApiService", async () => {
jest.spyOn(criticalAppsApiServiceMock, "setCriticalApps").mockResolvedValue(Promise.resolve());
const event = { target: { checked: true } as HTMLInputElement } as unknown as Event;
component.onCheckboxChange("app1", event);
await component.markAppsAsCritical();
expect(criticalAppsApiServiceMock.setCriticalApps).toHaveBeenCalled();
expect(component.getSelectedUrls().length).toBe(0);
});
});

View File

@@ -58,6 +58,7 @@ export class AllApplicationsComponent implements OnInit {
this.activatedRoute.paramMap
.pipe(
takeUntilDestroyed(this.destroyRef),
map(async (params) => {
const organizationId = params.get("organizationId");
this.organization = await firstValueFrom(this.organizationService.get$(organizationId));
@@ -66,10 +67,15 @@ export class AllApplicationsComponent implements OnInit {
}),
switchMap(async (params) => {
const organizationId = (await params).get("organizationId");
await this.criticalAppsService.getCriticalApps(organizationId);
return await this.criticalAppsService.getCriticalApps(organizationId);
}),
)
.subscribe();
.subscribe((dbCriticalAppRecords) => {
applicationTableMockData.forEach((data) => {
data.isMarkedAsCritical = dbCriticalAppRecords.some((app) => app.uri === data.name);
});
this.dataSource.data = applicationTableMockData;
});
this.isCritialAppsFeatureEnabled = await this.configService.getFeatureFlag(
FeatureFlag.CriticalApps,
@@ -87,7 +93,7 @@ export class AllApplicationsComponent implements OnInit {
protected configService: ConfigService,
protected criticalAppsService: CriticalAppsApiService,
) {
this.dataSource.data = applicationTableMockData;
// this.dataSource.data = applicationTableMockData;
this.searchControl.valueChanges
.pipe(debounceTime(200), takeUntilDestroyed())
.subscribe((v) => (this.dataSource.filter = v));
@@ -132,4 +138,6 @@ export class AllApplicationsComponent implements OnInit {
this.selectedUrls.delete(urlName);
}
}
getSelectedUrls = () => Array.from(this.selectedUrls);
}