mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
[PM-9733] Custom Vault Timeout (#10515)
* handle timeout changes that are predefined string values - Passing a string to `Math.max` will cause a NaN to be set. * type form instance so TypeScript is more aware of the form values
This commit is contained in:
@@ -0,0 +1,81 @@
|
|||||||
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
|
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||||
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
|
||||||
|
|
||||||
|
import { VaultTimeoutInputComponent } from "./vault-timeout-input.component";
|
||||||
|
|
||||||
|
describe("VaultTimeoutInputComponent", () => {
|
||||||
|
let component: VaultTimeoutInputComponent;
|
||||||
|
let fixture: ComponentFixture<VaultTimeoutInputComponent>;
|
||||||
|
const get$ = jest.fn().mockReturnValue(new BehaviorSubject({}));
|
||||||
|
const availableVaultTimeoutActions$ = jest.fn().mockReturnValue(new BehaviorSubject([]));
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [VaultTimeoutInputComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: PolicyService, useValue: { get$ } },
|
||||||
|
{ provide: VaultTimeoutSettingsService, useValue: { availableVaultTimeoutActions$ } },
|
||||||
|
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(VaultTimeoutInputComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.vaultTimeoutOptions = [
|
||||||
|
{ name: "oneMinute", value: 1 },
|
||||||
|
{ name: "fiveMinutes", value: 5 },
|
||||||
|
{ name: "fifteenMinutes", value: 15 },
|
||||||
|
{ name: "thirtyMinutes", value: 30 },
|
||||||
|
{ name: "oneHour", value: 60 },
|
||||||
|
{ name: "fourHours", value: 240 },
|
||||||
|
{ name: "onRefresh", value: VaultTimeoutStringType.OnRestart },
|
||||||
|
];
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("form", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await component.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invokes the onChange associated with `ControlValueAccessor`", () => {
|
||||||
|
const onChange = jest.fn();
|
||||||
|
component.registerOnChange(onChange);
|
||||||
|
|
||||||
|
component.form.controls.vaultTimeout.setValue(VaultTimeoutStringType.OnRestart);
|
||||||
|
|
||||||
|
expect(onChange).toHaveBeenCalledWith(VaultTimeoutStringType.OnRestart);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates custom value to match preset option", () => {
|
||||||
|
// 1 hour
|
||||||
|
component.form.controls.vaultTimeout.setValue(60);
|
||||||
|
|
||||||
|
expect(component.form.value.custom).toEqual({ hours: 1, minutes: 0 });
|
||||||
|
|
||||||
|
// 17 minutes
|
||||||
|
component.form.controls.vaultTimeout.setValue(17);
|
||||||
|
|
||||||
|
expect(component.form.value.custom).toEqual({ hours: 0, minutes: 17 });
|
||||||
|
|
||||||
|
// 2.25 hours
|
||||||
|
component.form.controls.vaultTimeout.setValue(135);
|
||||||
|
|
||||||
|
expect(component.form.value.custom).toEqual({ hours: 2, minutes: 15 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets custom timeout to 0 when a preset string option is selected", () => {
|
||||||
|
// Set custom value to random values
|
||||||
|
component.form.controls.custom.setValue({ hours: 1, minutes: 1 });
|
||||||
|
|
||||||
|
component.form.controls.vaultTimeout.setValue(VaultTimeoutStringType.OnLocked);
|
||||||
|
|
||||||
|
expect(component.form.value.custom).toEqual({ hours: 0, minutes: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,6 +4,8 @@ import {
|
|||||||
AbstractControl,
|
AbstractControl,
|
||||||
ControlValueAccessor,
|
ControlValueAccessor,
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
|
FormControl,
|
||||||
|
FormGroup,
|
||||||
NG_VALIDATORS,
|
NG_VALIDATORS,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
@@ -22,13 +24,15 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { VaultTimeout, VaultTimeoutOption } from "@bitwarden/common/types/vault-timeout.type";
|
import { VaultTimeout, VaultTimeoutOption } from "@bitwarden/common/types/vault-timeout.type";
|
||||||
import { FormFieldModule, SelectModule } from "@bitwarden/components";
|
import { FormFieldModule, SelectModule } from "@bitwarden/components";
|
||||||
|
|
||||||
interface VaultTimeoutFormValue {
|
type VaultTimeoutForm = FormGroup<{
|
||||||
vaultTimeout: VaultTimeout | null;
|
vaultTimeout: FormControl<VaultTimeout | null>;
|
||||||
custom: {
|
custom: FormGroup<{
|
||||||
hours: number | null;
|
hours: FormControl<number | null>;
|
||||||
minutes: number | null;
|
minutes: FormControl<number | null>;
|
||||||
};
|
}>;
|
||||||
}
|
}>;
|
||||||
|
|
||||||
|
type VaultTimeoutFormValue = VaultTimeoutForm["value"];
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "auth-vault-timeout-input",
|
selector: "auth-vault-timeout-input",
|
||||||
@@ -64,7 +68,7 @@ export class VaultTimeoutInputComponent
|
|||||||
static CUSTOM_VALUE = -100;
|
static CUSTOM_VALUE = -100;
|
||||||
static MIN_CUSTOM_MINUTES = 0;
|
static MIN_CUSTOM_MINUTES = 0;
|
||||||
|
|
||||||
form = this.formBuilder.group({
|
form: VaultTimeoutForm = this.formBuilder.group({
|
||||||
vaultTimeout: [null],
|
vaultTimeout: [null],
|
||||||
custom: this.formBuilder.group({
|
custom: this.formBuilder.group({
|
||||||
hours: [null],
|
hours: [null],
|
||||||
@@ -120,7 +124,7 @@ export class VaultTimeoutInputComponent
|
|||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
.subscribe((value) => {
|
.subscribe((value) => {
|
||||||
const current = Math.max(value, 0);
|
const current = typeof value === "string" ? 0 : Math.max(value, 0);
|
||||||
|
|
||||||
// This cannot emit an event b/c it would cause form.valueChanges to fire again
|
// This cannot emit an event b/c it would cause form.valueChanges to fire again
|
||||||
// and we are already handling that above so just silently update
|
// and we are already handling that above so just silently update
|
||||||
|
|||||||
Reference in New Issue
Block a user