Skip to content

Commit

Permalink
[PM-14421] Add autofill setting callout
Browse files Browse the repository at this point in the history
  • Loading branch information
shane-melton committed Jan 24, 2025
1 parent 82be88a commit f9e181f
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 2 deletions.
15 changes: 15 additions & 0 deletions apps/browser/src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2397,6 +2397,21 @@
}
}
},
"changeAtRiskPasswordsFaster": {
"message": "Change at-risk passwords faster"
},
"changeAtRiskPasswordsFasterDesc": {
"message": "Update your settings so you can quickly autofill your passwords and generate new ones"
},
"turnOnAutofill": {
"message": "Turn on autofill"
},
"turnedOnAutofill": {
"message": "Turned on autofill"
},
"dismiss": {
"message": "Dismiss"
},
"websiteItemLabel": {
"message": "Website $number$ (URI)",
"placeholders": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@
</ng-container>
</popup-header>

<bit-callout
*ngIf="!(autofillOnPageLoad$ | async) && !calloutDismissed()"
type="info"
[title]="'changeAtRiskPasswordsFaster' | i18n"
data-testid="autofill-callout"
>
<p bitTypography="body2">{{ "changeAtRiskPasswordsFasterDesc" | i18n }}</p>
<button
type="button"
bitButton
buttonType="primary"
(click)="activateAutofillOnPageLoad()"
data-testid="turn-on-autofill-button"
>
{{ "turnOnAutofill" | i18n }}
</button>
<button type="button" bitButton buttonType="secondary" (click)="calloutDismissed.set(true)">
{{ "dismiss" | i18n }}
</button>
</bit-callout>

<ng-container *ngIf="atRiskItems$ | async as items">
<p bitTypography="body2">{{ pageDescription$ | async }}</p>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, Input } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { mock } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom, of } from "rxjs";

Expand All @@ -8,10 +9,12 @@ import { IconComponent } from "@bitwarden/angular/vault/components/icon.componen
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { ToastService } from "@bitwarden/components";
import {
PasswordRepromptService,
SecurityTask,
Expand Down Expand Up @@ -59,6 +62,9 @@ describe("AtRiskPasswordsComponent", () => {
let mockTasks$: BehaviorSubject<SecurityTask[]>;
let mockCiphers$: BehaviorSubject<CipherView[]>;
let mockOrg$: BehaviorSubject<Organization>;
let mockAutofillOnPageLoad$: BehaviorSubject<boolean>;
const setAutofillOnPageLoad = jest.fn();
const mockToastService = mock<ToastService>();

beforeEach(async () => {
mockTasks$ = new BehaviorSubject<SecurityTask[]>([
Expand Down Expand Up @@ -86,6 +92,10 @@ describe("AtRiskPasswordsComponent", () => {
name: "Org 1",
} as Organization);

mockAutofillOnPageLoad$ = new BehaviorSubject<boolean>(false);
setAutofillOnPageLoad.mockClear();
mockToastService.showToast.mockClear();

await TestBed.configureTestingModule({
imports: [AtRiskPasswordsComponent],
providers: [
Expand All @@ -111,6 +121,14 @@ describe("AtRiskPasswordsComponent", () => {
{ provide: AccountService, useValue: { activeAccount$: of({ id: "user" }) } },
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
{ provide: PasswordRepromptService, useValue: mock<PasswordRepromptService>() },
{
provide: AutofillSettingsServiceAbstraction,
useValue: {
autofillOnPageLoad$: mockAutofillOnPageLoad$,
setAutofillOnPageLoad,
},
},
{ provide: ToastService, useValue: mockToastService },
],
})
.overrideModule(JslibModule, {
Expand Down Expand Up @@ -175,4 +193,34 @@ describe("AtRiskPasswordsComponent", () => {
expect(description).toBe("atRiskPasswordsDescMultiOrg");
});
});

describe("autofill callout", () => {
it("should show the callout if autofill is disabled", async () => {
mockAutofillOnPageLoad$.next(false);
fixture.detectChanges();
const callout = fixture.debugElement.query(By.css('[data-testid="autofill-callout"]'));

expect(callout).toBeTruthy();
});

it("should hide the callout if autofill is enabled", async () => {
mockAutofillOnPageLoad$.next(true);
fixture.detectChanges();
const callout = fixture.debugElement.query(By.css('[data-testid="autofill-callout"]'));

expect(callout).toBeFalsy();
});

describe("turn on autofill button", () => {
it("should call the service to turn on autofill and show a toast", () => {
const button = fixture.debugElement.query(
By.css('[data-testid="turn-on-autofill-button"]'),
);
button.nativeElement.click();

expect(setAutofillOnPageLoad).toHaveBeenCalledWith(true);
expect(mockToastService.showToast).toHaveBeenCalled();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommonModule } from "@angular/common";
import { Component, inject } from "@angular/core";
import { Component, inject, signal } from "@angular/core";
import { Router } from "@angular/router";
import { combineLatest, map, of, shareReplay, startWith, switchMap } from "rxjs";

Expand All @@ -9,11 +9,19 @@ import {
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { BadgeComponent, ItemModule, TypographyModule } from "@bitwarden/components";
import {
BadgeComponent,
ButtonModule,
CalloutModule,
ItemModule,
ToastService,
TypographyModule,
} from "@bitwarden/components";
import {
filterOutNullish,
OrgIconDirective,
Expand All @@ -39,6 +47,8 @@ import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page
BadgeComponent,
TypographyModule,
OrgIconDirective,
CalloutModule,
ButtonModule,
],
templateUrl: "./at-risk-passwords.component.html",
})
Expand All @@ -51,6 +61,8 @@ export class AtRiskPasswordsComponent {
private platformUtilsService = inject(PlatformUtilsService);
private passwordRepromptService = inject(PasswordRepromptService);
private router = inject(Router);
private autofillSettingsService = inject(AutofillSettingsServiceAbstraction);
private toastService = inject(ToastService);

private activeUserData$ = this.accountService.activeAccount$.pipe(
filterOutNullish(),
Expand All @@ -77,6 +89,9 @@ export class AtRiskPasswordsComponent {
startWith(true),
);

protected autofillOnPageLoad$ = this.autofillSettingsService.autofillOnPageLoad$;
protected calloutDismissed = signal(false);

protected atRiskItems$ = this.activeUserData$.pipe(
map(({ tasks, ciphers }) =>
tasks
Expand Down Expand Up @@ -120,4 +135,13 @@ export class AtRiskPasswordsComponent {
this.platformUtilsService.launchUri(cipher.login.uri);
}
}

async activateAutofillOnPageLoad() {
await this.autofillSettingsService.setAutofillOnPageLoad(true);
this.toastService.showToast({
variant: "success",
message: this.i18nService.t("turnedOnAutofill"),
title: "",
});
}
}

0 comments on commit f9e181f

Please sign in to comment.