mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-23226] ToTp updating on Desktop (#15435)
* update `totpInfo$` observable when the cipher changes * mark cipher as required and remove ignore statements * adds totp countdown tests
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
import { BitTotpCountdownComponent } from "./totp-countdown.component";
|
||||
|
||||
describe("BitTotpCountdownComponent", () => {
|
||||
let component: BitTotpCountdownComponent;
|
||||
let fixture: ComponentFixture<BitTotpCountdownComponent>;
|
||||
let totpService: jest.Mocked<TotpService>;
|
||||
|
||||
const mockCipher1 = {
|
||||
id: "cipher-id",
|
||||
name: "Test Cipher",
|
||||
login: { totp: "totp-secret" },
|
||||
} as CipherView;
|
||||
|
||||
const mockCipher2 = {
|
||||
id: "cipher-id-2",
|
||||
name: "Test Cipher 2",
|
||||
login: { totp: "totp-secret-2" },
|
||||
} as CipherView;
|
||||
|
||||
const mockTotpResponse1 = {
|
||||
code: "123456",
|
||||
period: 30,
|
||||
};
|
||||
|
||||
const mockTotpResponse2 = {
|
||||
code: "987654",
|
||||
period: 10,
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
totpService = mock<TotpService>({
|
||||
getCode$: jest.fn().mockImplementation((totp) => {
|
||||
if (totp === mockCipher1.login.totp) {
|
||||
return of(mockTotpResponse1);
|
||||
}
|
||||
|
||||
return of(mockTotpResponse2);
|
||||
}),
|
||||
});
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
providers: [{ provide: TotpService, useValue: totpService }],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(BitTotpCountdownComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.cipher = mockCipher1;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("initializes totpInfo$ observable", (done) => {
|
||||
component.totpInfo$?.subscribe((info) => {
|
||||
expect(info.totpCode).toBe(mockTotpResponse1.code);
|
||||
expect(info.totpCodeFormatted).toBe("123 456");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("emits sendCopyCode when TOTP code is available", (done) => {
|
||||
const emitter = jest.spyOn(component.sendCopyCode, "emit");
|
||||
|
||||
component.totpInfo$?.subscribe((info) => {
|
||||
expect(emitter).toHaveBeenCalledWith({
|
||||
totpCode: info.totpCode,
|
||||
totpCodeFormatted: info.totpCodeFormatted,
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("updates totpInfo$ when cipher changes", (done) => {
|
||||
component.cipher = mockCipher2;
|
||||
component.ngOnChanges({
|
||||
cipher: {
|
||||
currentValue: mockCipher2,
|
||||
previousValue: mockCipher1,
|
||||
firstChange: false,
|
||||
isFirstChange: () => false,
|
||||
},
|
||||
});
|
||||
|
||||
component.totpInfo$?.subscribe((info) => {
|
||||
expect(info.totpCode).toBe(mockTotpResponse2.code);
|
||||
expect(info.totpCodeFormatted).toBe("987 654");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,13 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
} from "@angular/core";
|
||||
import { Observable, map, tap } from "rxjs";
|
||||
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
@@ -14,8 +20,8 @@ import { TypographyModule } from "@bitwarden/components";
|
||||
templateUrl: "totp-countdown.component.html",
|
||||
imports: [CommonModule, TypographyModule],
|
||||
})
|
||||
export class BitTotpCountdownComponent implements OnInit {
|
||||
@Input() cipher: CipherView;
|
||||
export class BitTotpCountdownComponent implements OnInit, OnChanges {
|
||||
@Input({ required: true }) cipher!: CipherView;
|
||||
@Output() sendCopyCode = new EventEmitter();
|
||||
|
||||
/**
|
||||
@@ -26,6 +32,16 @@ export class BitTotpCountdownComponent implements OnInit {
|
||||
constructor(protected totpService: TotpService) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.setTotpInfo();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes["cipher"]) {
|
||||
this.setTotpInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private setTotpInfo(): void {
|
||||
this.totpInfo$ = this.cipher?.login?.totp
|
||||
? this.totpService.getCode$(this.cipher.login.totp).pipe(
|
||||
map((response) => {
|
||||
|
||||
Reference in New Issue
Block a user