- {{ "sendCreatedDescriptionV2" | i18n: formattedExpirationTime }}
+ @let translationKey =
+ send.authType === AuthType.Email
+ ? "sendCreatedDescriptionEmail"
+ : send.authType === AuthType.Password
+ ? "sendCreatedDescriptionPassword"
+ : "sendCreatedDescriptionV2";
+ {{ translationKey | i18n: formattedExpirationTime }}
diff --git a/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.spec.ts b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.spec.ts
new file mode 100644
index 00000000000..bfc35f208ed
--- /dev/null
+++ b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.spec.ts
@@ -0,0 +1,162 @@
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { mock, MockProxy } from "jest-mock-extended";
+import { of } from "rxjs";
+
+import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
+import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
+import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
+import { AuthType } from "@bitwarden/common/tools/send/types/auth-type";
+import { SendType } from "@bitwarden/common/tools/send/types/send-type";
+import {
+ DIALOG_DATA,
+ DialogModule,
+ I18nMockService,
+ ToastService,
+ TypographyModule,
+} from "@bitwarden/components";
+import { SharedModule } from "@bitwarden/web-vault/app/shared";
+
+import { SendSuccessDrawerDialogComponent } from "./send-success-drawer-dialog.component";
+
+describe("SendSuccessDrawerDialogComponent", () => {
+ let fixture: ComponentFixture;
+ let component: SendSuccessDrawerDialogComponent;
+ let environmentService: MockProxy;
+ let platformUtilsService: MockProxy;
+ let toastService: MockProxy;
+
+ let sendView: SendView;
+
+ // Translation Keys
+ const newTextSend = "New Text Send";
+ const newFileSend = "New File Send";
+ const oneHour = "1 hour";
+ const oneDay = "1 day";
+ const sendCreatedSuccessfully = "Send has been created successfully";
+ const sendCreatedDescriptionV2 = "Send ready to share with anyone";
+ const sendCreatedDescriptionEmail = "Email-verified Send ready to share";
+ const sendCreatedDescriptionPassword = "Password-protected Send ready to share";
+
+ beforeEach(async () => {
+ environmentService = mock();
+ platformUtilsService = mock();
+ toastService = mock();
+
+ sendView = {
+ id: "test-send-id",
+ authType: AuthType.None,
+ deletionDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
+ type: SendType.Text,
+ accessId: "abc",
+ urlB64Key: "123",
+ } as SendView;
+
+ Object.defineProperty(environmentService, "environment$", {
+ configurable: true,
+ get: () => of(new SelfHostedEnvironment({ webVault: "https://example.com" })),
+ });
+
+ await TestBed.configureTestingModule({
+ imports: [SharedModule, DialogModule, TypographyModule],
+ providers: [
+ {
+ provide: DIALOG_DATA,
+ useValue: sendView,
+ },
+ { provide: EnvironmentService, useValue: environmentService },
+ {
+ provide: I18nService,
+ useFactory: () => {
+ return new I18nMockService({
+ newTextSend,
+ newFileSend,
+ sendCreatedSuccessfully,
+ sendCreatedDescriptionEmail,
+ sendCreatedDescriptionPassword,
+ sendCreatedDescriptionV2,
+ sendLink: "Send link",
+ copyLink: "Copy Send Link",
+ close: "Close",
+ oneHour,
+ durationTimeHours: (hours) => `${hours} hours`,
+ oneDay,
+ days: (days) => `${days} days`,
+ loading: "loading",
+ });
+ },
+ },
+ { provide: PlatformUtilsService, useValue: platformUtilsService },
+ { provide: ToastService, useValue: toastService },
+ ],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SendSuccessDrawerDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it("should create", () => {
+ expect(component).toBeTruthy();
+ });
+
+ it("should have the correct title for text Sends", () => {
+ sendView.type = SendType.Text;
+ fixture.detectChanges();
+ expect(component.dialogTitle).toBe("newTextSend");
+ });
+
+ it("should have the correct title for file Sends", () => {
+ fixture.componentInstance.send.type = SendType.File;
+ fixture.detectChanges();
+ expect(component.dialogTitle).toBe("newFileSend");
+ });
+
+ it("should show the correct message for Sends with an expiration time of one hour from now", () => {
+ sendView.deletionDate = new Date(Date.now() + 1 * 60 * 60 * 1000);
+ fixture.detectChanges();
+ expect(component.formattedExpirationTime).toBe(oneHour);
+ });
+
+ it("should show the correct message for Sends with an expiration time more than an hour but less than a day from now", () => {
+ const numHours = 8;
+ sendView.deletionDate = new Date(Date.now() + numHours * 60 * 60 * 1000);
+ fixture.detectChanges();
+ expect(component.formattedExpirationTime).toBe(`${numHours} hours`);
+ });
+
+ it("should have the correct title for Sends with an expiration time of one day from now", () => {
+ sendView.deletionDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
+ fixture.detectChanges();
+ expect(component.formattedExpirationTime).toBe(oneDay);
+ });
+
+ it("should have the correct title for Sends with an expiration time of multiple days from now", () => {
+ const numDays = 3;
+ sendView.deletionDate = new Date(Date.now() + numDays * 24 * 60 * 60 * 1000);
+ fixture.detectChanges();
+ expect(component.formattedExpirationTime).toBe(`${numDays} days`);
+ });
+
+ it("should show the correct message for successfully-created Sends with no authentication", () => {
+ sendView.authType = AuthType.None;
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toContain(sendCreatedSuccessfully);
+ expect(fixture.nativeElement.textContent).toContain(sendCreatedDescriptionV2);
+ });
+
+ it("should show the correct message for successfully-created Sends with password authentication", () => {
+ sendView.authType = AuthType.Password;
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toContain(sendCreatedSuccessfully);
+ expect(fixture.nativeElement.textContent).toContain(sendCreatedDescriptionPassword);
+ });
+
+ it("should show the correct message for successfully-created Sends with email authentication", () => {
+ sendView.authType = AuthType.Email;
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toContain(sendCreatedSuccessfully);
+ expect(fixture.nativeElement.textContent).toContain(sendCreatedDescriptionEmail);
+ });
+});
diff --git a/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts
index 67e01cd9ff0..9d812bc77ba 100644
--- a/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts
+++ b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts
@@ -1,4 +1,4 @@
-import { Component, ChangeDetectionStrategy, Inject, signal, computed } from "@angular/core";
+import { Component, ChangeDetectionStrategy, Inject, signal } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { ActiveSendIcon } from "@bitwarden/assets/svg";
@@ -6,6 +6,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
+import { AuthType } from "@bitwarden/common/tools/send/types/auth-type";
import { SendType } from "@bitwarden/common/tools/send/types/send-type";
import { DIALOG_DATA, DialogModule, ToastService, TypographyModule } from "@bitwarden/components";
import { SharedModule } from "@bitwarden/web-vault/app/shared";
@@ -16,13 +17,13 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared";
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SendSuccessDrawerDialogComponent {
+ readonly AuthType = AuthType;
readonly sendLink = signal("");
activeSendIcon = ActiveSendIcon;
- // Computed property to get the dialog title based on send type
- readonly dialogTitle = computed(() => {
+ get dialogTitle(): string {
return this.send.type === SendType.Text ? "newTextSend" : "newFileSend";
- });
+ }
constructor(
@Inject(DIALOG_DATA) public send: SendView,
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index fe93d419035..a894b328d56 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -5675,6 +5675,26 @@
}
}
},
+ "sendCreatedDescriptionPassword": {
+ "message": "Copy and share this Send link. The Send will be available to anyone with the link and password you set for the next $TIME$.",
+ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
+ "placeholders": {
+ "time": {
+ "content": "$1",
+ "example": "7 days, 1 hour, 1 day"
+ }
+ }
+ },
+ "sendCreatedDescriptionEmail": {
+ "message": "Copy and share this Send link. It can be viewed by the people you specified for the next $TIME$.",
+ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
+ "placeholders": {
+ "time": {
+ "content": "$1",
+ "example": "7 days, 1 hour, 1 day"
+ }
+ }
+ },
"durationTimeHours": {
"message": "$HOURS$ hours",
"placeholders": {