diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.tsx b/src/components/views/auth/InteractiveAuthEntryComponents.tsx index 78fcdf7c2e..4a995e4d06 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.tsx +++ b/src/components/views/auth/InteractiveAuthEntryComponents.tsx @@ -692,6 +692,89 @@ export class MsisdnAuthEntry extends React.Component { + public static readonly LOGIN_TYPE = AuthType.RegistrationToken; + + public constructor(props: IAuthEntryProps) { + super(props); + + this.state = { + registrationToken: "", + }; + } + + public componentDidMount(): void { + this.props.onPhaseChange(DEFAULT_PHASE); + } + + private onSubmit = (e: FormEvent): void => { + e.preventDefault(); + if (this.props.busy) return; + + this.props.submitAuthDict({ + // Could be AuthType.RegistrationToken or AuthType.UnstableRegistrationToken + type: this.props.loginType, + token: this.state.registrationToken, + }); + }; + + private onRegistrationTokenFieldChange = (ev: ChangeEvent): void => { + // enable the submit button if the registration token is non-empty + this.setState({ + registrationToken: ev.target.value, + }); + }; + + public render(): JSX.Element { + const registrationTokenBoxClass = classNames({ + error: this.props.errorText, + }); + + let submitButtonOrSpinner; + if (this.props.busy) { + submitButtonOrSpinner = ; + } else { + submitButtonOrSpinner = ( + + {_t("Continue")} + + ); + } + + let errorSection; + if (this.props.errorText) { + errorSection = ( +
+ {this.props.errorText} +
+ ); + } + + return ( +
+

{_t("Enter a registration token provided by the homeserver administrator.")}

+
+ + {errorSection} +
{submitButtonOrSpinner}
+ +
+ ); + } +} + interface ISSOAuthEntryProps extends IAuthEntryProps { continueText?: string; continueKind?: string; @@ -713,7 +796,7 @@ export class SSOAuthEntry extends React.Component mount(); + + beforeEach(function () { + jest.clearAllMocks(); + }); + + afterAll(() => { + unmockClientPeg(); + }); + + const getSubmitButton = (wrapper: ReactWrapper) => wrapper.find('AccessibleButton[kind="primary"]').at(0); + const getRegistrationTokenInput = (wrapper: ReactWrapper) => + wrapper.find('input[name="registrationTokenField"]').at(0); + + it("Should successfully complete a registration token flow", async () => { + const onAuthFinished = jest.fn(); + const makeRequest = jest.fn().mockResolvedValue({ a: 1 }); + + const authData = { + session: "sess", + flows: [{ stages: ["m.login.registration_token"] }], + }; + + const wrapper = getComponent({ makeRequest, onAuthFinished, authData }); + + const registrationTokenNode = getRegistrationTokenInput(wrapper); + const submitNode = getSubmitButton(wrapper); + const formNode = wrapper.find("form").at(0); + + expect(registrationTokenNode).toBeTruthy(); + expect(submitNode).toBeTruthy(); + expect(formNode).toBeTruthy(); + + // submit should be disabled + expect(submitNode.props().disabled).toBe(true); + + // put something in the registration token box + act(() => { + registrationTokenNode.simulate("change", { target: { value: "s3kr3t" } }); + wrapper.setProps({}); + }); + + expect(getRegistrationTokenInput(wrapper).props().value).toEqual("s3kr3t"); + expect(getSubmitButton(wrapper).props().disabled).toBe(false); + + // hit enter; that should trigger a request + act(() => { + formNode.simulate("submit"); + }); + + // wait for auth request to resolve + await flushPromises(); + + expect(makeRequest).toHaveBeenCalledTimes(1); + expect(makeRequest).toBeCalledWith( + expect.objectContaining({ + session: "sess", + type: "m.login.registration_token", + token: "s3kr3t", + }), + ); + + expect(onAuthFinished).toBeCalledTimes(1); + expect(onAuthFinished).toBeCalledWith( + true, + { a: 1 }, + { clientSecret: "t35tcl1Ent5ECr3T", emailSid: undefined }, + ); + }); +});