diff --git a/src/components/views/dialogs/VerificationRequestDialog.tsx b/src/components/views/dialogs/VerificationRequestDialog.tsx index d8cef61036..cb4166b775 100644 --- a/src/components/views/dialogs/VerificationRequestDialog.tsx +++ b/src/components/views/dialogs/VerificationRequestDialog.tsx @@ -60,7 +60,7 @@ export default class VerificationRequestDialog extends React.Component { + function renderComponent(phase: VerificationPhase, method?: "emoji" | "qr"): ReturnType { + const member = User.createUser("@alice:example.org", stubClient()); + const request = createRequest(phase, method); + + return render( + , + ); + } + + it("Initially, asks how you would like to verify this device", async () => { + const dialog = renderComponent(VerificationPhase.Ready); + + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + expect(screen.getByText("Verify this device by completing one of the following:")).toBeInTheDocument(); + + expect(dialog.asFragment()).toMatchSnapshot(); + }); + + it("After we started verification here, says we are waiting for the other device", async () => { + const dialog = renderComponent(VerificationPhase.Requested); + + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + + expect( + screen.getByText("To proceed, please accept the verification request on your other device."), + ).toBeInTheDocument(); + + expect(dialog.asFragment()).toMatchSnapshot(); + }); + + it("When other device accepted emoji, displays emojis and asks for confirmation", async () => { + const dialog = renderComponent(VerificationPhase.Started, "emoji"); + + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + + expect( + screen.getByText("Confirm the emoji below are displayed on both devices, in the same order:"), + ).toBeInTheDocument(); + + expect(dialog.asFragment()).toMatchSnapshot(); + }); + + it("After scanning QR, shows confirmation dialog", async () => { + const dialog = renderComponent(VerificationPhase.Started, "qr"); + + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Verify by scanning" })).toBeInTheDocument(); + + expect(screen.getByText("Almost there! Is your other device showing the same shield?")).toBeInTheDocument(); + + expect(dialog.asFragment()).toMatchSnapshot(); + }); + + it("Shows a successful message if verification finished normally", async () => { + const dialog = renderComponent(VerificationPhase.Done); + + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + expect(screen.getByText("You've successfully verified your device!")).toBeInTheDocument(); + + expect(dialog.asFragment()).toMatchSnapshot(); + }); + + it("Shows a failure message if verification was cancelled", async () => { + const dialog = renderComponent(VerificationPhase.Cancelled); + + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument(); + + expect( + screen.getByText( + "You cancelled verification on your other device. Start verification again from the notification.", + ), + ).toBeInTheDocument(); + + expect(dialog.asFragment()).toMatchSnapshot(); + }); + + it("Renders correctly if the request is supplied later via a promise", async () => { + // Given we supply a promise of a request instead of a request + const member = User.createUser("@alice:example.org", stubClient()); + const requestPromise = Promise.resolve(createRequest(VerificationPhase.Cancelled)); + + // When we render the dialog + render( + , + ); + + // And wait for the component to mount, the promise to resolve and the component state to update + await act(async () => await new Promise(process.nextTick)); + + // Then it renders the resolved information + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument(); + + expect( + screen.getByText( + "You cancelled verification on your other device. Start verification again from the notification.", + ), + ).toBeInTheDocument(); + }); + + it("Renders the later promise request if both immediate and promise are supplied", async () => { + // Given we supply a promise of a request as well as a request + const member = User.createUser("@alice:example.org", stubClient()); + const request = createRequest(VerificationPhase.Ready); + const requestPromise = Promise.resolve(createRequest(VerificationPhase.Cancelled)); + + // When we render the dialog + render( + , + ); + + // And wait for the component to mount, the promise to resolve and the component state to update + await act(async () => await new Promise(process.nextTick)); + + // Then it renders the information from the request in the promise + expect(screen.getByRole("heading", { name: "Verify other device" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Verification cancelled" })).toBeInTheDocument(); + + expect( + screen.getByText( + "You cancelled verification on your other device. Start verification again from the notification.", + ), + ).toBeInTheDocument(); + }); +}); + +function createRequest(phase: VerificationPhase, method?: "emoji" | "qr"): VerificationRequest { + let verifier = undefined; + let chosenMethod = undefined; + + switch (method) { + case "emoji": + chosenMethod = VerificationMethod.Sas; + verifier = createEmojiVerifier(); + break; + case "qr": + chosenMethod = VerificationMethod.Reciprocate; + verifier = createQrVerifier(); + break; + } + + return { + phase: jest.fn().mockReturnValue(phase), + + // VerificationRequest is an emitter - ignore any events that are emitted. + on: jest.fn(), + off: jest.fn(), + + // These tests (so far) only check for when we are initiating a verificiation of our own device. + isSelfVerification: jest.fn().mockReturnValue(true), + initiatedByMe: jest.fn().mockReturnValue(true), + + // Always returning true means we can support QR code and emoji verification. + otherPartySupportsMethod: jest.fn().mockReturnValue(true), + + // If we asked for emoji, these are populated. + verifier, + chosenMethod, + } as unknown as VerificationRequest; +} + +function createEmojiVerifier(): Verifier { + const showSasCallbacks = { + sas: { + emoji: [ + // Example set of emoji to display. + ["🐶", "Dog"], + ["🐱", "Cat"], + ], + }, + } as ShowSasCallbacks; + + return { + getShowSasCallbacks: jest.fn().mockReturnValue(showSasCallbacks), + getReciprocateQrCodeCallbacks: jest.fn(), + on: jest.fn(), + off: jest.fn(), + verify: jest.fn(), + } as unknown as Verifier; +} + +function createQrVerifier(): Verifier { + const reciprocateQrCodeCallbacks = { + confirm: jest.fn(), + cancel: jest.fn(), + } as ShowQrCodeCallbacks; + + return { + getShowSasCallbacks: jest.fn(), + getReciprocateQrCodeCallbacks: jest.fn().mockReturnValue(reciprocateQrCodeCallbacks), + on: jest.fn(), + off: jest.fn(), + verify: jest.fn(), + } as unknown as Verifier; +} diff --git a/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap b/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap new file mode 100644 index 0000000000..f0336ef559 --- /dev/null +++ b/test/unit-tests/components/views/dialogs/__snapshots__/VerificationRequestDialog-test.tsx.snap @@ -0,0 +1,440 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`VerificationRequestDialog After scanning QR, shows confirmation dialog 1`] = ` + +
+