mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-11 10:10:25 +01:00
add back events for browser refresh extension (#11085)
- something went sideways in a merge
This commit is contained in:
parent
51a2ec393c
commit
26f3dcfc66
@ -3,6 +3,8 @@ import { ActivatedRoute, Router } from "@angular/router";
|
|||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
@ -44,12 +46,14 @@ describe("AddEditV2Component", () => {
|
|||||||
const disable = jest.fn();
|
const disable = jest.fn();
|
||||||
const navigate = jest.fn();
|
const navigate = jest.fn();
|
||||||
const back = jest.fn().mockResolvedValue(null);
|
const back = jest.fn().mockResolvedValue(null);
|
||||||
|
const collect = jest.fn().mockResolvedValue(null);
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
buildConfig.mockClear();
|
buildConfig.mockClear();
|
||||||
disable.mockClear();
|
disable.mockClear();
|
||||||
navigate.mockClear();
|
navigate.mockClear();
|
||||||
back.mockClear();
|
back.mockClear();
|
||||||
|
collect.mockClear();
|
||||||
|
|
||||||
addEditCipherInfo$ = new BehaviorSubject(null);
|
addEditCipherInfo$ = new BehaviorSubject(null);
|
||||||
cipherServiceMock = mock<CipherService>();
|
cipherServiceMock = mock<CipherService>();
|
||||||
@ -66,6 +70,7 @@ describe("AddEditV2Component", () => {
|
|||||||
{ provide: ActivatedRoute, useValue: { queryParams: queryParams$ } },
|
{ provide: ActivatedRoute, useValue: { queryParams: queryParams$ } },
|
||||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||||
{ provide: CipherService, useValue: cipherServiceMock },
|
{ provide: CipherService, useValue: cipherServiceMock },
|
||||||
|
{ provide: EventCollectionService, useValue: { collect } },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
.overrideProvider(CipherFormConfigService, {
|
.overrideProvider(CipherFormConfigService, {
|
||||||
@ -122,6 +127,57 @@ describe("AddEditV2Component", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("analytics", () => {
|
||||||
|
it("does not log viewed event when mode is add", fakeAsync(() => {
|
||||||
|
queryParams$.next({});
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(collect).not.toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("does not log viewed event whe mode is clone", fakeAsync(() => {
|
||||||
|
queryParams$.next({ cipherId: "222-333-444-5555", clone: "true" });
|
||||||
|
buildConfigResponse.originalCipher = {} as Cipher;
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(collect).not.toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("logs viewed event when mode is edit", fakeAsync(() => {
|
||||||
|
buildConfigResponse.originalCipher = {
|
||||||
|
edit: true,
|
||||||
|
id: "222-333-444-5555",
|
||||||
|
organizationId: "444-555-666",
|
||||||
|
} as Cipher;
|
||||||
|
queryParams$.next({ cipherId: "222-333-444-5555" });
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(collect).toHaveBeenCalledWith(
|
||||||
|
EventType.Cipher_ClientViewed,
|
||||||
|
"222-333-444-5555",
|
||||||
|
false,
|
||||||
|
"444-555-666",
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("logs viewed event whe mode is partial-edit", fakeAsync(() => {
|
||||||
|
buildConfigResponse.originalCipher = { edit: false } as Cipher;
|
||||||
|
queryParams$.next({ cipherId: "222-333-444-5555", orgId: "444-555-666" });
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(collect).toHaveBeenCalledWith(
|
||||||
|
EventType.Cipher_ClientViewed,
|
||||||
|
"222-333-444-5555",
|
||||||
|
false,
|
||||||
|
"444-555-666",
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
describe("addEditCipherInfo initialization", () => {
|
describe("addEditCipherInfo initialization", () => {
|
||||||
it("populates config.initialValues with `addEditCipherInfo` values", fakeAsync(() => {
|
it("populates config.initialValues with `addEditCipherInfo` values", fakeAsync(() => {
|
||||||
const addEditCipherInfo = {
|
const addEditCipherInfo = {
|
||||||
|
@ -6,6 +6,8 @@ import { ActivatedRoute, Params, Router } from "@angular/router";
|
|||||||
import { firstValueFrom, map, switchMap } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
@ -160,6 +162,7 @@ export class AddEditV2Component implements OnInit {
|
|||||||
private popupRouterCacheService: PopupRouterCacheService,
|
private popupRouterCacheService: PopupRouterCacheService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
) {
|
) {
|
||||||
this.subscribeToParams();
|
this.subscribeToParams();
|
||||||
}
|
}
|
||||||
@ -275,6 +278,15 @@ export class AddEditV2Component implements OnInit {
|
|||||||
await this.cipherService.setAddEditCipherInfo(null);
|
await this.cipherService.setAddEditCipherInfo(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (["edit", "partial-edit"].includes(config.mode) && config.originalCipher?.id) {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
EventType.Cipher_ClientViewed,
|
||||||
|
config.originalCipher.id,
|
||||||
|
false,
|
||||||
|
config.originalCipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,9 @@ import { ActivatedRoute, Router } from "@angular/router";
|
|||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
|
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@ -29,10 +31,12 @@ describe("ViewV2Component", () => {
|
|||||||
let fixture: ComponentFixture<ViewV2Component>;
|
let fixture: ComponentFixture<ViewV2Component>;
|
||||||
const params$ = new Subject();
|
const params$ = new Subject();
|
||||||
const mockNavigate = jest.fn();
|
const mockNavigate = jest.fn();
|
||||||
|
const collect = jest.fn().mockResolvedValue(null);
|
||||||
|
|
||||||
const mockCipher = {
|
const mockCipher = {
|
||||||
id: "122-333-444",
|
id: "122-333-444",
|
||||||
type: CipherType.Login,
|
type: CipherType.Login,
|
||||||
|
orgId: "222-444-555",
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockVaultPopupAutofillService = {
|
const mockVaultPopupAutofillService = {
|
||||||
@ -48,6 +52,7 @@ describe("ViewV2Component", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
mockNavigate.mockClear();
|
mockNavigate.mockClear();
|
||||||
|
collect.mockClear();
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ViewV2Component],
|
imports: [ViewV2Component],
|
||||||
@ -59,6 +64,7 @@ describe("ViewV2Component", () => {
|
|||||||
{ provide: ConfigService, useValue: mock<ConfigService>() },
|
{ provide: ConfigService, useValue: mock<ConfigService>() },
|
||||||
{ provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() },
|
{ provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() },
|
||||||
{ provide: ActivatedRoute, useValue: { queryParams: params$ } },
|
{ provide: ActivatedRoute, useValue: { queryParams: params$ } },
|
||||||
|
{ provide: EventCollectionService, useValue: { collect } },
|
||||||
{
|
{
|
||||||
provide: I18nService,
|
provide: I18nService,
|
||||||
useValue: {
|
useValue: {
|
||||||
@ -122,5 +128,18 @@ describe("ViewV2Component", () => {
|
|||||||
|
|
||||||
expect(component.headerText).toEqual("viewItemHeader note");
|
expect(component.headerText).toEqual("viewItemHeader note");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it("sends viewed event", fakeAsync(() => {
|
||||||
|
params$.next({ cipherId: "122-333-444" });
|
||||||
|
|
||||||
|
flush(); // Resolve all promises
|
||||||
|
|
||||||
|
expect(collect).toHaveBeenCalledWith(
|
||||||
|
EventType.Cipher_ClientViewed,
|
||||||
|
mockCipher.id,
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,9 +6,11 @@ import { ActivatedRoute, Router } from "@angular/router";
|
|||||||
import { firstValueFrom, map, Observable, switchMap } from "rxjs";
|
import { firstValueFrom, map, Observable, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AUTOFILL_ID, SHOW_AUTOFILL_BUTTON } from "@bitwarden/common/autofill/constants";
|
import { AUTOFILL_ID, SHOW_AUTOFILL_BUTTON } from "@bitwarden/common/autofill/constants";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
@ -73,6 +75,7 @@ export class ViewV2Component {
|
|||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private vaultPopupAutofillService: VaultPopupAutofillService,
|
private vaultPopupAutofillService: VaultPopupAutofillService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
) {
|
) {
|
||||||
this.subscribeToParams();
|
this.subscribeToParams();
|
||||||
}
|
}
|
||||||
@ -90,6 +93,13 @@ export class ViewV2Component {
|
|||||||
if (this.loadAction === AUTOFILL_ID || this.loadAction === SHOW_AUTOFILL_BUTTON) {
|
if (this.loadAction === AUTOFILL_ID || this.loadAction === SHOW_AUTOFILL_BUTTON) {
|
||||||
await this.vaultPopupAutofillService.doAutofill(this.cipher);
|
await this.vaultPopupAutofillService.doAutofill(this.cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
EventType.Cipher_ClientViewed,
|
||||||
|
cipher.id,
|
||||||
|
false,
|
||||||
|
cipher.organizationId,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
takeUntilDestroyed(),
|
takeUntilDestroyed(),
|
||||||
)
|
)
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
bitSuffix
|
bitSuffix
|
||||||
bitPasswordInputToggle
|
bitPasswordInputToggle
|
||||||
data-testid="visibility-for-card-number"
|
data-testid="visibility-for-card-number"
|
||||||
|
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardNumberVisible)"
|
||||||
></button>
|
></button>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
@ -60,6 +61,7 @@
|
|||||||
bitSuffix
|
bitSuffix
|
||||||
bitPasswordInputToggle
|
bitPasswordInputToggle
|
||||||
data-testid="visibility-for-card-code"
|
data-testid="visibility-for-card-code"
|
||||||
|
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardCodeVisible)"
|
||||||
></button>
|
></button>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
</bit-card>
|
</bit-card>
|
||||||
|
@ -4,6 +4,7 @@ import { ReactiveFormsModule } from "@angular/forms";
|
|||||||
import { By } from "@angular/platform-browser";
|
import { By } from "@angular/platform-browser";
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@ -27,6 +28,7 @@ describe("CardDetailsSectionComponent", () => {
|
|||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CardDetailsSectionComponent, CommonModule, ReactiveFormsModule],
|
imports: [CardDetailsSectionComponent, CommonModule, ReactiveFormsModule],
|
||||||
providers: [
|
providers: [
|
||||||
|
{ provide: EventCollectionService, useValue: mock<EventCollectionService>() },
|
||||||
{ provide: CipherFormContainer, useValue: cipherFormProvider },
|
{ provide: CipherFormContainer, useValue: cipherFormProvider },
|
||||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||||
],
|
],
|
||||||
|
@ -4,6 +4,8 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
|||||||
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
|
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@ -91,10 +93,13 @@ export class CardDetailsSectionComponent implements OnInit {
|
|||||||
{ name: "12 - " + this.i18nService.t("december"), value: "12" },
|
{ name: "12 - " + this.i18nService.t("december"), value: "12" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
EventType = EventType;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cipherFormContainer: CipherFormContainer,
|
private cipherFormContainer: CipherFormContainer,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
) {
|
) {
|
||||||
this.cipherFormContainer.registerChildForm("cardDetails", this.cardDetailsForm);
|
this.cipherFormContainer.registerChildForm("cardDetails", this.cardDetailsForm);
|
||||||
|
|
||||||
@ -149,6 +154,21 @@ export class CardDetailsSectionComponent implements OnInit {
|
|||||||
return this.i18nService.t("cardDetails");
|
return this.i18nService.t("cardDetails");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logCardEvent(hiddenFieldVisible: boolean, event: EventType) {
|
||||||
|
const { mode, originalCipher } = this.cipherFormContainer.config;
|
||||||
|
|
||||||
|
const isEdit = ["edit", "partial-edit"].includes(mode);
|
||||||
|
|
||||||
|
if (hiddenFieldVisible && isEdit) {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
event,
|
||||||
|
originalCipher.id,
|
||||||
|
false,
|
||||||
|
originalCipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Set form initial form values from the current cipher */
|
/** Set form initial form values from the current cipher */
|
||||||
private setInitialValues() {
|
private setInitialValues() {
|
||||||
const { cardholderName, number, brand, expMonth, expYear, code } = this.originalCipherView.card;
|
const { cardholderName, number, brand, expMonth, expYear, code } = this.originalCipherView.card;
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
bitPasswordInputToggle
|
bitPasswordInputToggle
|
||||||
data-testid="visibility-for-custom-hidden-field"
|
data-testid="visibility-for-custom-hidden-field"
|
||||||
[disabled]="!canViewPasswords(i)"
|
[disabled]="!canViewPasswords(i)"
|
||||||
|
(toggledChange)="logHiddenEvent($event)"
|
||||||
></button>
|
></button>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
|
@ -4,7 +4,9 @@ import { CdkDragDrop } from "@angular/cdk/drag-drop";
|
|||||||
import { DebugElement } from "@angular/core";
|
import { DebugElement } from "@angular/core";
|
||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { By } from "@angular/platform-browser";
|
import { By } from "@angular/platform-browser";
|
||||||
|
import { mock } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import {
|
import {
|
||||||
CardLinkedId,
|
CardLinkedId,
|
||||||
@ -50,6 +52,7 @@ describe("CustomFieldsComponent", () => {
|
|||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CustomFieldsComponent],
|
imports: [CustomFieldsComponent],
|
||||||
providers: [
|
providers: [
|
||||||
|
{ provide: EventCollectionService, useValue: mock<EventCollectionService>() },
|
||||||
{
|
{
|
||||||
provide: I18nService,
|
provide: I18nService,
|
||||||
useValue: { t: (...keys: string[]) => keys.filter(Boolean).join(" ") },
|
useValue: { t: (...keys: string[]) => keys.filter(Boolean).join(" ") },
|
||||||
|
@ -19,6 +19,8 @@ import { FormArray, FormBuilder, FormsModule, ReactiveFormsModule } from "@angul
|
|||||||
import { Subject, zip } from "rxjs";
|
import { Subject, zip } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherType, FieldType, LinkedIdType } from "@bitwarden/common/vault/enums";
|
import { CipherType, FieldType, LinkedIdType } from "@bitwarden/common/vault/enums";
|
||||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||||
@ -118,6 +120,7 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit {
|
|||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private liveAnnouncer: LiveAnnouncer,
|
private liveAnnouncer: LiveAnnouncer,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
) {
|
) {
|
||||||
this.destroyed$ = inject(DestroyRef);
|
this.destroyed$ = inject(DestroyRef);
|
||||||
this.cipherFormContainer.registerChildForm("customFields", this.customFieldsForm);
|
this.cipherFormContainer.registerChildForm("customFields", this.customFieldsForm);
|
||||||
@ -301,6 +304,21 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logHiddenEvent(hiddenFieldVisible: boolean) {
|
||||||
|
const { mode, originalCipher } = this.cipherFormContainer.config;
|
||||||
|
|
||||||
|
const isEdit = ["edit", "partial-edit"].includes(mode);
|
||||||
|
|
||||||
|
if (hiddenFieldVisible && isEdit) {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
EventType.Cipher_ClientToggledHiddenFieldVisible,
|
||||||
|
originalCipher.id,
|
||||||
|
false,
|
||||||
|
originalCipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the linked field options for the current cipher type
|
* Returns the linked field options for the current cipher type
|
||||||
*
|
*
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
*ngIf="viewHiddenFields"
|
*ngIf="viewHiddenFields"
|
||||||
data-testid="toggle-password-visibility"
|
data-testid="toggle-password-visibility"
|
||||||
bitPasswordInputToggle
|
bitPasswordInputToggle
|
||||||
|
(toggledChange)="logVisibleEvent($event, EventType.Cipher_ClientToggledPasswordVisible)"
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -113,6 +114,7 @@
|
|||||||
*ngIf="viewHiddenFields"
|
*ngIf="viewHiddenFields"
|
||||||
data-testid="toggle-totp-visibility"
|
data-testid="toggle-totp-visibility"
|
||||||
bitPasswordInputToggle
|
bitPasswordInputToggle
|
||||||
|
(toggledChange)="logVisibleEvent($event, EventType.Cipher_ClientToggledTOTPSeedVisible)"
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
|
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
|
||||||
|
import { By } from "@angular/platform-browser";
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
|
import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
|
||||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||||
import { ToastService } from "@bitwarden/components";
|
import { ToastService } from "@bitwarden/components";
|
||||||
|
import { BitPasswordInputToggleDirective } from "@bitwarden/components/src/form-field/password-input-toggle.directive";
|
||||||
|
|
||||||
import { CipherFormGenerationService } from "../../abstractions/cipher-form-generation.service";
|
import { CipherFormGenerationService } from "../../abstractions/cipher-form-generation.service";
|
||||||
import { TotpCaptureService } from "../../abstractions/totp-capture.service";
|
import { TotpCaptureService } from "../../abstractions/totp-capture.service";
|
||||||
@ -34,6 +39,7 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
let toastService: MockProxy<ToastService>;
|
let toastService: MockProxy<ToastService>;
|
||||||
let totpCaptureService: MockProxy<TotpCaptureService>;
|
let totpCaptureService: MockProxy<TotpCaptureService>;
|
||||||
let i18nService: MockProxy<I18nService>;
|
let i18nService: MockProxy<I18nService>;
|
||||||
|
const collect = jest.fn().mockResolvedValue(null);
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
cipherFormContainer = mock<CipherFormContainer>();
|
cipherFormContainer = mock<CipherFormContainer>();
|
||||||
@ -43,6 +49,7 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
toastService = mock<ToastService>();
|
toastService = mock<ToastService>();
|
||||||
totpCaptureService = mock<TotpCaptureService>();
|
totpCaptureService = mock<TotpCaptureService>();
|
||||||
i18nService = mock<I18nService>();
|
i18nService = mock<I18nService>();
|
||||||
|
collect.mockClear();
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [LoginDetailsSectionComponent],
|
imports: [LoginDetailsSectionComponent],
|
||||||
@ -53,6 +60,7 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
{ provide: ToastService, useValue: toastService },
|
{ provide: ToastService, useValue: toastService },
|
||||||
{ provide: TotpCaptureService, useValue: totpCaptureService },
|
{ provide: TotpCaptureService, useValue: totpCaptureService },
|
||||||
{ provide: I18nService, useValue: i18nService },
|
{ provide: I18nService, useValue: i18nService },
|
||||||
|
{ provide: EventCollectionService, useValue: { collect } },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
.overrideComponent(LoginDetailsSectionComponent, {
|
.overrideComponent(LoginDetailsSectionComponent, {
|
||||||
@ -255,6 +263,32 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
expect(getTogglePasswordVisibilityBtn()).toBeNull();
|
expect(getTogglePasswordVisibilityBtn()).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("logs password viewed event when toggledChange is true", async () => {
|
||||||
|
cipherFormContainer.config.mode = "edit";
|
||||||
|
cipherFormContainer.config.originalCipher = {
|
||||||
|
id: "111-222-333",
|
||||||
|
organizationId: "333-444-555",
|
||||||
|
} as Cipher;
|
||||||
|
jest.spyOn(component, "viewHiddenFields", "get").mockReturnValue(true);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const passwordToggle = fixture.debugElement.query(
|
||||||
|
By.directive(BitPasswordInputToggleDirective),
|
||||||
|
);
|
||||||
|
await passwordToggle.triggerEventHandler("toggledChange", true);
|
||||||
|
|
||||||
|
expect(collect).toHaveBeenCalledWith(
|
||||||
|
EventType.Cipher_ClientToggledPasswordVisible,
|
||||||
|
"111-222-333",
|
||||||
|
false,
|
||||||
|
"333-444-555",
|
||||||
|
);
|
||||||
|
|
||||||
|
await passwordToggle.triggerEventHandler("toggledChange", false);
|
||||||
|
|
||||||
|
expect(collect).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
describe("password generation", () => {
|
describe("password generation", () => {
|
||||||
it("should show generate password button when editable", () => {
|
it("should show generate password button when editable", () => {
|
||||||
expect(getGeneratePasswordBtn()).not.toBeNull();
|
expect(getGeneratePasswordBtn()).not.toBeNull();
|
||||||
|
@ -6,6 +6,8 @@ import { map } from "rxjs";
|
|||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
|
import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view";
|
||||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||||
@ -48,6 +50,7 @@ import { AutofillOptionsComponent } from "../autofill-options/autofill-options.c
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class LoginDetailsSectionComponent implements OnInit {
|
export class LoginDetailsSectionComponent implements OnInit {
|
||||||
|
EventType = EventType;
|
||||||
loginDetailsForm = this.formBuilder.group({
|
loginDetailsForm = this.formBuilder.group({
|
||||||
username: [""],
|
username: [""],
|
||||||
password: [""],
|
password: [""],
|
||||||
@ -106,6 +109,7 @@ export class LoginDetailsSectionComponent implements OnInit {
|
|||||||
private generationService: CipherFormGenerationService,
|
private generationService: CipherFormGenerationService,
|
||||||
private auditService: AuditService,
|
private auditService: AuditService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
@Optional() private totpCaptureService?: TotpCaptureService,
|
@Optional() private totpCaptureService?: TotpCaptureService,
|
||||||
) {
|
) {
|
||||||
this.cipherFormContainer.registerChildForm("loginDetails", this.loginDetailsForm);
|
this.cipherFormContainer.registerChildForm("loginDetails", this.loginDetailsForm);
|
||||||
@ -163,6 +167,24 @@ export class LoginDetailsSectionComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Logs the givin event when in edit mode */
|
||||||
|
logVisibleEvent = async (passwordVisible: boolean, event: EventType) => {
|
||||||
|
const { mode, originalCipher } = this.cipherFormContainer.config;
|
||||||
|
|
||||||
|
const isEdit = ["edit", "partial-edit"].includes(mode);
|
||||||
|
|
||||||
|
if (!passwordVisible || !isEdit || !originalCipher) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
event,
|
||||||
|
originalCipher.id,
|
||||||
|
false,
|
||||||
|
originalCipher.organizationId,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
captureTotp = async () => {
|
captureTotp = async () => {
|
||||||
if (!this.canCaptureTotp) {
|
if (!this.canCaptureTotp) {
|
||||||
return;
|
return;
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
bitIconButton
|
bitIconButton
|
||||||
bitPasswordInputToggle
|
bitPasswordInputToggle
|
||||||
data-testid="toggle-number"
|
data-testid="toggle-number"
|
||||||
|
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardNumberVisible)"
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
bitIconButton="bwi-clone"
|
bitIconButton="bwi-clone"
|
||||||
@ -69,6 +70,7 @@
|
|||||||
bitIconButton
|
bitIconButton
|
||||||
bitPasswordInputToggle
|
bitPasswordInputToggle
|
||||||
data-testid="toggle-code"
|
data-testid="toggle-code"
|
||||||
|
(toggledChange)="logCardEvent($event, EventType.Cipher_ClientToggledCardCodeVisible)"
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
bitIconButton="bwi-clone"
|
bitIconButton="bwi-clone"
|
||||||
@ -79,6 +81,7 @@
|
|||||||
[valueLabel]="'securityCode' | i18n"
|
[valueLabel]="'securityCode' | i18n"
|
||||||
[appA11yTitle]="'copyValue' | i18n"
|
[appA11yTitle]="'copyValue' | i18n"
|
||||||
data-testid="copy-code"
|
data-testid="copy-code"
|
||||||
|
(click)="logCardEvent(true, EventType.Cipher_ClientCopiedCardCode)"
|
||||||
></button>
|
></button>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
</read-only-cipher-card>
|
</read-only-cipher-card>
|
||||||
|
@ -2,8 +2,10 @@ import { CommonModule } from "@angular/common";
|
|||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import {
|
import {
|
||||||
CardComponent,
|
CardComponent,
|
||||||
SectionComponent,
|
SectionComponent,
|
||||||
@ -32,9 +34,17 @@ import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CardDetailsComponent {
|
export class CardDetailsComponent {
|
||||||
@Input() card: CardView;
|
@Input() cipher: CipherView;
|
||||||
|
EventType = EventType;
|
||||||
|
|
||||||
constructor(private i18nService: I18nService) {}
|
constructor(
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
get card() {
|
||||||
|
return this.cipher.card;
|
||||||
|
}
|
||||||
|
|
||||||
get setSectionTitle() {
|
get setSectionTitle() {
|
||||||
if (this.card.brand && this.card.brand !== "Other") {
|
if (this.card.brand && this.card.brand !== "Other") {
|
||||||
@ -42,4 +52,15 @@ export class CardDetailsComponent {
|
|||||||
}
|
}
|
||||||
return this.i18nService.t("cardDetails");
|
return this.i18nService.t("cardDetails");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logCardEvent(conditional: boolean, event: EventType) {
|
||||||
|
if (conditional) {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
event,
|
||||||
|
this.cipher.id,
|
||||||
|
false,
|
||||||
|
this.cipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
</app-autofill-options-view>
|
</app-autofill-options-view>
|
||||||
|
|
||||||
<!-- CARD DETAILS -->
|
<!-- CARD DETAILS -->
|
||||||
<app-card-details-view *ngIf="hasCard" [card]="cipher.card"></app-card-details-view>
|
<app-card-details-view *ngIf="hasCard" [cipher]="cipher"></app-card-details-view>
|
||||||
|
|
||||||
<!-- IDENTITY SECTIONS -->
|
<!-- IDENTITY SECTIONS -->
|
||||||
<app-view-identity-sections *ngIf="cipher.identity" [cipher]="cipher">
|
<app-view-identity-sections *ngIf="cipher.identity" [cipher]="cipher">
|
||||||
@ -42,8 +42,7 @@
|
|||||||
|
|
||||||
<!-- CUSTOM FIELDS -->
|
<!-- CUSTOM FIELDS -->
|
||||||
<ng-container *ngIf="cipher.fields">
|
<ng-container *ngIf="cipher.fields">
|
||||||
<app-custom-fields-v2 [fields]="cipher.fields" [cipherType]="cipher.type">
|
<app-custom-fields-v2 [cipher]="cipher"> </app-custom-fields-v2>
|
||||||
</app-custom-fields-v2>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- ATTACHMENTS SECTION -->
|
<!-- ATTACHMENTS SECTION -->
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<bit-card>
|
<bit-card>
|
||||||
<div
|
<div
|
||||||
class="tw-border-secondary-300 [&_bit-form-field:last-of-type]:tw-mb-0"
|
class="tw-border-secondary-300 [&_bit-form-field:last-of-type]:tw-mb-0"
|
||||||
*ngFor="let field of fields; let last = last"
|
*ngFor="let field of cipher.fields; let last = last"
|
||||||
[ngClass]="{ 'tw-mb-4': !last }"
|
[ngClass]="{ 'tw-mb-4': !last }"
|
||||||
>
|
>
|
||||||
<bit-form-field *ngIf="field.type === fieldType.Text" [disableReadOnlyBorder]="last">
|
<bit-form-field *ngIf="field.type === fieldType.Text" [disableReadOnlyBorder]="last">
|
||||||
@ -24,7 +24,13 @@
|
|||||||
<bit-form-field *ngIf="field.type === fieldType.Hidden" [disableReadOnlyBorder]="last">
|
<bit-form-field *ngIf="field.type === fieldType.Hidden" [disableReadOnlyBorder]="last">
|
||||||
<bit-label>{{ field.name }}</bit-label>
|
<bit-label>{{ field.name }}</bit-label>
|
||||||
<input readonly bitInput type="password" [value]="field.value" aria-readonly="true" />
|
<input readonly bitInput type="password" [value]="field.value" aria-readonly="true" />
|
||||||
<button bitSuffix type="button" bitIconButton bitPasswordInputToggle></button>
|
<button
|
||||||
|
bitSuffix
|
||||||
|
type="button"
|
||||||
|
bitIconButton
|
||||||
|
bitPasswordInputToggle
|
||||||
|
(toggledChange)="logHiddenEvent($event)"
|
||||||
|
></button>
|
||||||
<button
|
<button
|
||||||
bitIconButton="bwi-clone"
|
bitIconButton="bwi-clone"
|
||||||
bitSuffix
|
bitSuffix
|
||||||
@ -33,6 +39,7 @@
|
|||||||
showToast
|
showToast
|
||||||
[valueLabel]="field.name"
|
[valueLabel]="field.name"
|
||||||
[appA11yTitle]="'copyValue' | i18n"
|
[appA11yTitle]="'copyValue' | i18n"
|
||||||
|
(click)="logCopyEvent()"
|
||||||
></button>
|
></button>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
<bit-form-control *ngIf="field.type === fieldType.Boolean">
|
<bit-form-control *ngIf="field.type === fieldType.Boolean">
|
||||||
|
@ -2,10 +2,12 @@ import { CommonModule } from "@angular/common";
|
|||||||
import { Component, Input, OnInit } from "@angular/core";
|
import { Component, Input, OnInit } from "@angular/core";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherType, FieldType, LinkedIdType } from "@bitwarden/common/vault/enums";
|
import { CipherType, FieldType, LinkedIdType } from "@bitwarden/common/vault/enums";
|
||||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||||
import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
|
import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
|
||||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||||
import {
|
import {
|
||||||
@ -37,12 +39,14 @@ import {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CustomFieldV2Component implements OnInit {
|
export class CustomFieldV2Component implements OnInit {
|
||||||
@Input() fields: FieldView[];
|
@Input() cipher: CipherView;
|
||||||
@Input() cipherType: CipherType;
|
|
||||||
fieldType = FieldType;
|
fieldType = FieldType;
|
||||||
fieldOptions: any;
|
fieldOptions: any;
|
||||||
|
|
||||||
constructor(private i18nService: I18nService) {}
|
constructor(
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.fieldOptions = this.getLinkedFieldsOptionsForCipher();
|
this.fieldOptions = this.getLinkedFieldsOptionsForCipher();
|
||||||
@ -53,8 +57,28 @@ export class CustomFieldV2Component implements OnInit {
|
|||||||
return this.i18nService.t(linkedType.i18nKey);
|
return this.i18nService.t(linkedType.i18nKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logHiddenEvent(hiddenFieldVisible: boolean) {
|
||||||
|
if (hiddenFieldVisible) {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
EventType.Cipher_ClientToggledHiddenFieldVisible,
|
||||||
|
this.cipher.id,
|
||||||
|
false,
|
||||||
|
this.cipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async logCopyEvent() {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
EventType.Cipher_ClientCopiedHiddenField,
|
||||||
|
this.cipher.id,
|
||||||
|
false,
|
||||||
|
this.cipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private getLinkedFieldsOptionsForCipher() {
|
private getLinkedFieldsOptionsForCipher() {
|
||||||
switch (this.cipherType) {
|
switch (this.cipher.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
return LoginView.prototype.linkedFieldOptions;
|
return LoginView.prototype.linkedFieldOptions;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
showToast
|
showToast
|
||||||
[appA11yTitle]="'copyValue' | i18n"
|
[appA11yTitle]="'copyValue' | i18n"
|
||||||
data-testid="copy-password"
|
data-testid="copy-password"
|
||||||
|
(click)="logCopyEvent()"
|
||||||
></button>
|
></button>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
<div
|
<div
|
||||||
|
@ -4,7 +4,9 @@ import { Router } from "@angular/router";
|
|||||||
import { Observable, shareReplay } from "rxjs";
|
import { Observable, shareReplay } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
|
||||||
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import {
|
import {
|
||||||
@ -61,6 +63,7 @@ export class LoginCredentialsViewComponent {
|
|||||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
|
private eventCollectionService: EventCollectionService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get fido2CredentialCreationDateValue(): string {
|
get fido2CredentialCreationDateValue(): string {
|
||||||
@ -76,8 +79,17 @@ export class LoginCredentialsViewComponent {
|
|||||||
await this.router.navigate(["/premium"]);
|
await this.router.navigate(["/premium"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pwToggleValue(evt: boolean) {
|
async pwToggleValue(passwordVisible: boolean) {
|
||||||
this.passwordRevealed = evt;
|
this.passwordRevealed = passwordVisible;
|
||||||
|
|
||||||
|
if (passwordVisible) {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
EventType.Cipher_ClientToggledPasswordVisible,
|
||||||
|
this.cipher.id,
|
||||||
|
false,
|
||||||
|
this.cipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
togglePasswordCount() {
|
togglePasswordCount() {
|
||||||
@ -87,4 +99,13 @@ export class LoginCredentialsViewComponent {
|
|||||||
setTotpCopyCode(e: TotpCodeValues) {
|
setTotpCopyCode(e: TotpCodeValues) {
|
||||||
this.totpCodeCopyObj = e;
|
this.totpCodeCopyObj = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logCopyEvent() {
|
||||||
|
await this.eventCollectionService.collect(
|
||||||
|
EventType.Cipher_ClientCopiedPassword,
|
||||||
|
this.cipher.id,
|
||||||
|
false,
|
||||||
|
this.cipher.organizationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +158,8 @@ describe("CopyCipherFieldService", () => {
|
|||||||
expect(eventCollectionService.collect).toHaveBeenCalledWith(
|
expect(eventCollectionService.collect).toHaveBeenCalledWith(
|
||||||
EventType.Cipher_ClientCopiedPassword,
|
EventType.Cipher_ClientCopiedPassword,
|
||||||
cipher.id,
|
cipher.id,
|
||||||
|
false,
|
||||||
|
cipher.organizationId,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -125,7 +125,12 @@ export class CopyCipherFieldService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (action.event !== undefined) {
|
if (action.event !== undefined) {
|
||||||
await this.eventCollectionService.collect(action.event, cipher.id);
|
await this.eventCollectionService.collect(
|
||||||
|
action.event,
|
||||||
|
cipher.id,
|
||||||
|
false,
|
||||||
|
cipher.organizationId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user