mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-08 09:43:42 +01:00
[PM-4963] Migrate breach report components (#10045)
* WIP - migrate exposed passwords report components * lint fix * migrate components in reports * migrate breach and unsecured websites reports * undo change routing * revert changes to reports * revert changes * migrate breach report component * update form * revert back to text input * revert change to logic * layout fixes * add spec * fix typo * undo changes to exposed passowords report * fix test --------- Co-authored-by: jordan-bite <jordan@bite-interactive.com>
This commit is contained in:
parent
e22568f05a
commit
b2d4d1bec2
@ -2,26 +2,17 @@
|
|||||||
|
|
||||||
<bit-container>
|
<bit-container>
|
||||||
<p>{{ "breachDesc" | i18n }}</p>
|
<p>{{ "breachDesc" | i18n }}</p>
|
||||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
<form [bitSubmit]="submit" [formGroup]="formGroup">
|
||||||
<div class="row">
|
<bit-form-field class="tw-w-1/2" disableMargin>
|
||||||
<div class="form-group col-6">
|
<bit-label>{{ "username" | i18n }}</bit-label>
|
||||||
<label for="username">{{ "username" | i18n }}</label>
|
<input id="username" type="text" formControlName="username" bitInput />
|
||||||
<input
|
</bit-form-field>
|
||||||
id="username"
|
<small class="form-text text-muted tw-mb-4">{{ "breachCheckUsernameEmail" | i18n }}</small>
|
||||||
type="text"
|
<button type="submit" buttonType="primary" bitButton [loading]="loading">
|
||||||
name="Username"
|
|
||||||
class="form-control"
|
|
||||||
[(ngModel)]="username"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<small class="form-text text-muted">{{ "breachCheckUsernameEmail" | i18n }}</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
|
||||||
{{ "checkBreaches" | i18n }}
|
{{ "checkBreaches" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="mt-4" *ngIf="!form.loading && checkedUsername">
|
<div class="mt-4" *ngIf="!loading && checkedUsername">
|
||||||
<p *ngIf="error">{{ "reportError" | i18n }}...</p>
|
<p *ngIf="error">{{ "reportError" | i18n }}...</p>
|
||||||
<ng-container *ngIf="!error">
|
<ng-container *ngIf="!error">
|
||||||
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!breachedAccounts.length">
|
<app-callout type="success" title="{{ 'goodNews' | i18n }}" *ngIf="!breachedAccounts.length">
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
import { ReactiveFormsModule } from "@angular/forms";
|
||||||
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
|
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||||
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
|
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { BreachAccountResponse } from "@bitwarden/common/models/response/breach-account.response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
|
import { BreachReportComponent } from "./breach-report.component";
|
||||||
|
|
||||||
|
const breachedAccounts = [
|
||||||
|
new BreachAccountResponse({
|
||||||
|
addedDate: "2021-01-01",
|
||||||
|
breachDate: "2021-01-01",
|
||||||
|
dataClasses: ["test"],
|
||||||
|
description: "test",
|
||||||
|
domain: "test.com",
|
||||||
|
isActive: true,
|
||||||
|
isVerified: true,
|
||||||
|
logoPath: "test",
|
||||||
|
modifiedDate: "2021-01-01",
|
||||||
|
name: "test",
|
||||||
|
pwnCount: 1,
|
||||||
|
title: "test",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("BreachReportComponent", () => {
|
||||||
|
let component: BreachReportComponent;
|
||||||
|
let fixture: ComponentFixture<BreachReportComponent>;
|
||||||
|
let auditService: MockProxy<AuditService>;
|
||||||
|
let accountService: MockProxy<AccountService>;
|
||||||
|
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
||||||
|
id: "testId" as UserId,
|
||||||
|
email: "test@example.com",
|
||||||
|
emailVerified: true,
|
||||||
|
name: "Test User",
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
auditService = mock<AuditService>();
|
||||||
|
accountService = mock<AccountService>();
|
||||||
|
accountService.activeAccount$ = activeAccountSubject;
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [BreachReportComponent, I18nPipe],
|
||||||
|
imports: [ReactiveFormsModule],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: AuditService,
|
||||||
|
useValue: auditService,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AccountService,
|
||||||
|
useValue: accountService,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: I18nService,
|
||||||
|
useValue: mock<I18nService>(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BreachReportComponent);
|
||||||
|
component = fixture.componentInstance as BreachReportComponent;
|
||||||
|
fixture.detectChanges();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should initialize component", () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should initialize form with account email", async () => {
|
||||||
|
expect(component.formGroup.get("username").value).toEqual("test@example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should mark form as touched and show validation error if form is invalid on submit", async () => {
|
||||||
|
component.formGroup.get("username").setValue("");
|
||||||
|
await component.submit();
|
||||||
|
|
||||||
|
expect(component.formGroup.touched).toBe(true);
|
||||||
|
expect(component.formGroup.invalid).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call auditService.breachedAccounts with lowercase username", async () => {
|
||||||
|
auditService.breachedAccounts.mockResolvedValue(breachedAccounts);
|
||||||
|
component.formGroup.get("username").setValue("validUsername");
|
||||||
|
|
||||||
|
await component.submit();
|
||||||
|
|
||||||
|
expect(auditService.breachedAccounts).toHaveBeenCalledWith("validusername");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set breachedAccounts and checkedUsername after successful submit", async () => {
|
||||||
|
auditService.breachedAccounts.mockResolvedValue(breachedAccounts);
|
||||||
|
|
||||||
|
await component.submit();
|
||||||
|
|
||||||
|
expect(component.breachedAccounts).toEqual(breachedAccounts);
|
||||||
|
expect(component.checkedUsername).toEqual("test@example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set error to true if auditService.breachedAccounts throws an error", async () => {
|
||||||
|
auditService.breachedAccounts.mockRejectedValue(new Error("test error"));
|
||||||
|
component.formGroup.get("username").setValue("validUsername");
|
||||||
|
|
||||||
|
await component.submit();
|
||||||
|
|
||||||
|
expect(component.error).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set loading to false after submit", async () => {
|
||||||
|
auditService.breachedAccounts.mockResolvedValue([]);
|
||||||
|
component.formGroup.get("username").setValue("validUsername");
|
||||||
|
|
||||||
|
await component.submit();
|
||||||
|
|
||||||
|
expect(component.loading).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,5 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
@ -10,32 +11,46 @@ import { BreachAccountResponse } from "@bitwarden/common/models/response/breach-
|
|||||||
templateUrl: "breach-report.component.html",
|
templateUrl: "breach-report.component.html",
|
||||||
})
|
})
|
||||||
export class BreachReportComponent implements OnInit {
|
export class BreachReportComponent implements OnInit {
|
||||||
|
loading = false;
|
||||||
error = false;
|
error = false;
|
||||||
username: string;
|
|
||||||
checkedUsername: string;
|
checkedUsername: string;
|
||||||
breachedAccounts: BreachAccountResponse[] = [];
|
breachedAccounts: BreachAccountResponse[] = [];
|
||||||
formPromise: Promise<BreachAccountResponse[]>;
|
formGroup = this.formBuilder.group({
|
||||||
|
username: ["", { validators: [Validators.required], updateOn: "change" }],
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private auditService: AuditService,
|
private auditService: AuditService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.username = await firstValueFrom(
|
this.formGroup
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
.get("username")
|
||||||
);
|
.setValue(
|
||||||
|
await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
submit = async () => {
|
||||||
|
this.formGroup.markAsTouched();
|
||||||
|
|
||||||
|
if (this.formGroup.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.error = false;
|
this.error = false;
|
||||||
this.username = this.username.toLowerCase();
|
this.loading = true;
|
||||||
|
const username = this.formGroup.value.username.toLowerCase();
|
||||||
try {
|
try {
|
||||||
this.formPromise = this.auditService.breachedAccounts(this.username);
|
this.breachedAccounts = await this.auditService.breachedAccounts(username);
|
||||||
this.breachedAccounts = await this.formPromise;
|
|
||||||
} catch {
|
} catch {
|
||||||
this.error = true;
|
this.error = true;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
}
|
}
|
||||||
this.checkedUsername = this.username;
|
|
||||||
}
|
this.checkedUsername = username;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user