/* * Wire * Copyright (C) 2020 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * */ import {fireEvent, waitFor} from '@testing-library/react'; import {TypeUtil} from '@wireapp/commons'; import {SingleSignOnForm} from './SingleSignOnForm'; import {Config, Configuration} from '../../Config'; import {actionRoot} from '../module/action'; import {ValidationError} from '../module/action/ValidationError'; import {initialRootState} from '../module/reducer'; import {ROUTE, QUERY_KEY} from '../route'; import {mockStoreFactory} from '../util/test/mockStoreFactory'; import {mountComponent} from '../util/test/TestUtil'; const codeOrEmailInputId = 'enter-code'; const submitButtonId = 'do-sso-sign-in'; const temporaryCheckboxId = 'enter-public-computer-sso-sign-in'; const errorId = 'error-message'; describe('SingleSignOnForm', () => { it('prefills code and disables input with initial SSO code', async () => { spyOn<{getConfig: () => TypeUtil.RecursivePartial}>(Config, 'getConfig').and.returnValue({ FEATURE: { ENABLE_DOMAIN_DISCOVERY: false, }, }); const doLogin = jasmine.createSpy().and.returnValue((code: string) => Promise.resolve()); const initialCode = 'wire-cb6e4dfc-a4b0-4c59-a31d-303a7f5eb5ab'; spyOn(actionRoot.authAction, 'validateSSOCode').and.returnValue(() => Promise.resolve()); spyOn(actionRoot.authAction, 'doFinalizeSSOLogin').and.returnValue(() => Promise.resolve()); const {getByTestId} = mountComponent( , mockStoreFactory()(initialRootState), ); const codeOrEmailInput = getByTestId(codeOrEmailInputId) as HTMLInputElement; const submitButton = getByTestId(submitButtonId) as HTMLInputElement; expect(codeOrEmailInput.value).toEqual(initialCode); expect(codeOrEmailInput.disabled).toBe(true); expect(submitButton.disabled).toEqual(false); }); it('successfully logs into account with SSO code', async () => { spyOn<{getConfig: () => TypeUtil.RecursivePartial}>(Config, 'getConfig').and.returnValue({ FEATURE: { ENABLE_DOMAIN_DISCOVERY: false, }, }); const historyPushSpy = spyOn(history, 'pushState'); const doLogin = jasmine.createSpy().and.returnValue((code: string) => Promise.resolve()); const code = 'wire-cb6e4dfc-a4b0-4c59-a31d-303a7f5eb5ab'; spyOn(actionRoot.authAction, 'validateSSOCode').and.returnValue(() => Promise.resolve()); spyOn(actionRoot.authAction, 'doFinalizeSSOLogin').and.returnValue(() => Promise.resolve()); const {getByTestId} = mountComponent(, mockStoreFactory()(initialRootState)); const codeOrEmailInput = getByTestId(codeOrEmailInputId) as HTMLInputElement; const submitButton = getByTestId(submitButtonId) as HTMLInputElement; fireEvent.change(codeOrEmailInput, {target: {value: code}}); fireEvent.click(submitButton); expect(actionRoot.authAction.validateSSOCode).toHaveBeenCalledTimes(1); await waitFor(() => { expect(doLogin).toHaveBeenCalledTimes(1); }); expect(actionRoot.authAction.doFinalizeSSOLogin).toHaveBeenCalledTimes(1); expect(historyPushSpy).toHaveBeenCalledWith(expect.any(Object), expect.any(String), `#${ROUTE.HISTORY_INFO}`); }); it('shows invalid code or email error', async () => { spyOn<{getConfig: () => TypeUtil.RecursivePartial}>(Config, 'getConfig').and.returnValue({ FEATURE: { ENABLE_DOMAIN_DISCOVERY: true, }, }); const code = 'invalid-code'; const {getByTestId, container} = mountComponent( Promise.reject()}} />, mockStoreFactory()(initialRootState), ); const codeOrEmailInput = getByTestId(codeOrEmailInputId) as HTMLInputElement; const submitButton = getByTestId(submitButtonId) as HTMLInputElement; expect(submitButton.disabled).toBe(true); fireEvent.change(codeOrEmailInput, {target: {value: code}}); expect(submitButton.disabled).toBe(false); fireEvent.submit(container.querySelector('form')!); const errorMessage = getByTestId(errorId); expect(errorMessage.dataset.uieValue).toBe(ValidationError.FIELD.SSO_EMAIL_CODE.PATTERN_MISMATCH); }); it('disallows email when domain discovery is disabled', async () => { spyOn<{getConfig: () => TypeUtil.RecursivePartial}>(Config, 'getConfig').and.returnValue({ FEATURE: { ENABLE_DOMAIN_DISCOVERY: false, }, }); const email = 'email@mail.com'; const {getByTestId, container} = mountComponent( Promise.reject()}} />, mockStoreFactory()(initialRootState), ); const codeOrEmailInput = getByTestId(codeOrEmailInputId) as HTMLInputElement; const submitButton = getByTestId(submitButtonId) as HTMLInputElement; expect(submitButton.disabled).toBe(true); fireEvent.change(codeOrEmailInput, {target: {value: email}}); expect(submitButton.disabled).toBe(false); fireEvent.submit(container.querySelector('form')!); const errorMessage = getByTestId(errorId); expect(errorMessage.dataset.uieValue).toBe(ValidationError.FIELD.SSO_CODE.PATTERN_MISMATCH); }); it('successfully redirects with registered domain and permanent client', async () => { spyOn<{getConfig: () => TypeUtil.RecursivePartial}>(Config, 'getConfig').and.returnValue({ FEATURE: { ENABLE_DOMAIN_DISCOVERY: true, }, }); const email = ' mail@mail.com '; const inputHost = 'http://localhost:8080?test=true'; const expectedHost = `/auth?${QUERY_KEY.DESTINATION_URL}=${encodeURIComponent( `${inputHost}&clienttype=permanent&sso_auto_login=true`, )}#${ROUTE.CUSTOM_ENV_REDIRECT}`; spyOn(actionRoot.authAction, 'doGetDomainInfo').and.returnValue(() => Promise.resolve({config_json_url: '', webapp_welcome_url: inputHost}), ); spyOn(actionRoot.navigationAction, 'doNavigate').and.returnValue(() => {}); const {getByTestId, container} = mountComponent( Promise.reject()}} />, mockStoreFactory()(initialRootState), ); const codeOrEmailInput = getByTestId(codeOrEmailInputId) as HTMLInputElement; const submitButton = getByTestId(submitButtonId) as HTMLInputElement; expect(submitButton.disabled).toBe(true); fireEvent.change(codeOrEmailInput, {target: {value: email}}); expect(submitButton.disabled).toBe(false); fireEvent.submit(container.querySelector('form')!); expect(actionRoot.authAction.doGetDomainInfo).toHaveBeenCalledTimes(1); await waitFor(() => { expect(actionRoot.navigationAction.doNavigate).toHaveBeenCalledWith(expectedHost); }); }); it('successfully redirects with registered domain and temporary client', async () => { spyOn<{getConfig: () => TypeUtil.RecursivePartial}>(Config, 'getConfig').and.returnValue({ FEATURE: { ENABLE_DOMAIN_DISCOVERY: true, }, }); const email = ' mail@mail.com '; const inputHost = 'http://localhost:8080?test=true'; const expectedHost = `/auth?${QUERY_KEY.DESTINATION_URL}=${encodeURIComponent( `${inputHost}&clienttype=temporary&sso_auto_login=true`, )}#${ROUTE.CUSTOM_ENV_REDIRECT}`; spyOn(actionRoot.authAction, 'doGetDomainInfo').and.returnValue(() => Promise.resolve({config_json_url: '', webapp_welcome_url: inputHost}), ); spyOn(actionRoot.navigationAction, 'doNavigate').and.returnValue(() => {}); const {getByTestId, container} = mountComponent( Promise.reject()}} />, mockStoreFactory()(initialRootState), ); const codeOrEmailInput = getByTestId(codeOrEmailInputId) as HTMLInputElement; const submitButton = getByTestId(submitButtonId) as HTMLInputElement; const temporaryCheckbox = getByTestId(temporaryCheckboxId) as HTMLInputElement; expect(submitButton.disabled).toBe(true); fireEvent.change(codeOrEmailInput, {target: {value: email}}); fireEvent.click(temporaryCheckbox); expect(submitButton.disabled).toBe(false); fireEvent.submit(container.querySelector('form')!); expect(actionRoot.authAction.doGetDomainInfo).toHaveBeenCalledTimes(1); await waitFor(() => { expect(actionRoot.navigationAction.doNavigate).toHaveBeenCalledWith(expectedHost); }); }); });