1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 02:33:46 +00:00
Files
browser/libs/vault/src/components/download-attachment/download-attachment.component.spec.ts
Bryan Cunningham 5b815c4ae4 [CL-879] use tooltip on icon button (#16576)
* Add tooltip to icon button to display label

* remove legacy cdr variable

* create overlay on focus or hover

* attach describdedby ids

* fix type errors

* remove aria-describedby when not necessary

* fix failing tests

* implement Claude feedback

* fixing broken specs

* remove host attr binding

* Simplify directive aria logic

* Move id to statis number

* do not render empty tooltip

* pass id to tooltip component

* remove pointer-events none to allow tooltip on normal buttons

* exclude some tooltip stories

* change describedby input name

* add story with tooltip on regular button

* enhanced tooltip docs

* set model directly

* change model to input
2025-10-29 09:49:16 -04:00

182 lines
6.3 KiB
TypeScript

import { ComponentFixture, TestBed } from "@angular/core/testing";
import { By } from "@angular/platform-browser";
import { mock } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DECRYPT_ERROR } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { StateProvider } from "@bitwarden/common/platform/state";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { ToastService } from "@bitwarden/components";
import { PasswordRepromptService } from "../../services/password-reprompt.service";
import { DownloadAttachmentComponent } from "./download-attachment.component";
class MockRequest {
constructor(public url: string) {}
}
describe("DownloadAttachmentComponent", () => {
let component: DownloadAttachmentComponent;
let fixture: ComponentFixture<DownloadAttachmentComponent>;
const activeUserId$ = new BehaviorSubject("888-333-222-222");
const showToast = jest.fn();
const getAttachmentData = jest
.fn()
.mockResolvedValue({ url: "https://www.downloadattachement.com" });
const download = jest.fn();
const attachment = {
id: "222-3333-4444",
url: "https://www.attachment.com",
fileName: "attachment-filename",
size: "1234",
} as AttachmentView;
const cipherView = {
id: "5555-444-3333",
type: CipherType.Login,
name: "Test Login",
login: {
username: "username",
password: "password",
},
} as CipherView;
const ciphers$ = new BehaviorSubject({
"5555-444-3333": {
id: "5555-444-3333",
attachments: [
{
id: "222-3333-4444",
fileName: "encrypted-filename",
key: "encrypted-key",
},
],
},
});
const getFeatureFlag = jest.fn().mockResolvedValue(false);
beforeEach(async () => {
showToast.mockClear();
getAttachmentData.mockClear();
download.mockClear();
await TestBed.configureTestingModule({
imports: [DownloadAttachmentComponent],
providers: [
{ provide: EncryptService, useValue: mock<EncryptService>() },
{ provide: I18nService, useValue: { t: (key: string) => key } },
{ provide: StateProvider, useValue: { activeUserId$ } },
{ provide: ToastService, useValue: { showToast } },
{ provide: ApiService, useValue: { getAttachmentData } },
{ provide: FileDownloadService, useValue: { download } },
{ provide: PasswordRepromptService, useValue: mock<PasswordRepromptService>() },
{
provide: ConfigService,
useValue: {
getFeatureFlag,
},
},
{
provide: CipherService,
useValue: { ciphers$: () => ciphers$, getDecryptedAttachmentBuffer: jest.fn() },
},
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DownloadAttachmentComponent);
component = fixture.componentInstance;
component.attachment = attachment;
component.cipher = cipherView;
fixture.detectChanges();
});
it("renders delete button", () => {
const deleteButton = fixture.debugElement.query(By.css("button"));
expect(deleteButton.attributes["aria-label"]).toBe("downloadAttachmentName");
});
describe("download attachment", () => {
let fetchMock: jest.Mock;
beforeEach(() => {
fetchMock = jest.fn().mockResolvedValue({});
global.fetch = fetchMock;
// Request is not defined in the Jest runtime
// eslint-disable-next-line no-global-assign
Request = MockRequest as any;
});
it("hides download button when the attachment has decryption failure", () => {
component.attachment.fileName = DECRYPT_ERROR;
fixture.detectChanges();
expect(fixture.debugElement.query(By.css("button"))).toBeNull();
});
it("uses the attachment url when available when getAttachmentData returns a 404", async () => {
getAttachmentData.mockRejectedValue(new ErrorResponse({}, 404));
await component.download();
expect(fetchMock).toHaveBeenCalledWith({ url: attachment.url });
});
it("calls file download service with the attachment url", async () => {
getAttachmentData.mockResolvedValue({ url: "https://www.downloadattachement.com" });
fetchMock.mockResolvedValue({ status: 200 });
EncArrayBuffer.fromResponse = jest.fn().mockResolvedValue({});
await component.download();
expect(download).toHaveBeenCalledWith({ blobData: undefined, fileName: attachment.fileName });
});
describe("errors", () => {
it("shows an error toast when fetch fails", async () => {
getAttachmentData.mockResolvedValue({ url: "https://www.downloadattachement.com" });
fetchMock.mockResolvedValue({ status: 500 });
await component.download();
expect(showToast).toHaveBeenCalledWith({
message: "errorOccurred",
title: null,
variant: "error",
});
});
it("shows an error toast when getDecryptedAttachmentBuffer fails", async () => {
getAttachmentData.mockResolvedValue({ url: "https://www.downloadattachement.com" });
fetchMock.mockResolvedValue({ status: 200 });
const cipherService = TestBed.inject(CipherService) as jest.Mocked<CipherService>;
cipherService.getDecryptedAttachmentBuffer.mockRejectedValue(new Error());
await component.download();
expect(showToast).toHaveBeenCalledWith({
message: "errorOccurred",
title: null,
variant: "error",
});
});
});
});
});