diff --git a/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html index 90210df4658..ce5b0e36728 100644 --- a/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html +++ b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html @@ -1,6 +1,6 @@ - {{ dialogTitle() | i18n }} + {{ dialogTitle | i18n }}

- {{ "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": {