papercut-fix: perform login on enter button press for basic auth (#434)
Signed-off-by: Vishwas Rajashekar <vrajashe@cisco.com>
This commit is contained in:
parent
743d4fe073
commit
8311d59e53
@ -24,14 +24,14 @@ afterEach(() => {
|
|||||||
|
|
||||||
describe('Signin component automatic navigation', () => {
|
describe('Signin component automatic navigation', () => {
|
||||||
it('navigates to homepage when user is already logged in', async () => {
|
it('navigates to homepage when user is already logged in', async () => {
|
||||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={true} setIsLoggedIn={() => {}} />);
|
render(<SignIn isLoggedIn={true} setIsLoggedIn={() => {}} />);
|
||||||
await expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
await expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('navigates to homepage when auth is disabled', async () => {
|
it('navigates to homepage when auth is disabled', async () => {
|
||||||
// mock request to check auth
|
// mock request to check auth
|
||||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { http: {} } });
|
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { http: {} } });
|
||||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
||||||
});
|
});
|
||||||
@ -48,7 +48,7 @@ describe('Sign in form', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should change username and password values on user input', async () => {
|
it('should change username and password values on user input', async () => {
|
||||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
const usernameInput = await screen.findByLabelText(/^Username/i);
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
fireEvent.change(usernameInput, { target: { value: 'test' } });
|
fireEvent.change(usernameInput, { target: { value: 'test' } });
|
||||||
@ -59,7 +59,7 @@ describe('Sign in form', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display error if username and password values are empty after change', async () => {
|
it('should display error if username and password values are empty after change', async () => {
|
||||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
const usernameInput = await screen.findByLabelText(/^Username/i);
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
userEvent.click(usernameInput);
|
userEvent.click(usernameInput);
|
||||||
@ -74,24 +74,154 @@ describe('Sign in form', () => {
|
|||||||
await waitFor(() => expect(passwordError).toBeInTheDocument());
|
await waitFor(() => expect(passwordError).toBeInTheDocument());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log in the user and navigate to homepage if login is successful', async () => {
|
it('should log in the user and navigate to homepage if login is successful using button', async () => {
|
||||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
const submitButton = await screen.findByText('Continue');
|
|
||||||
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
|
userEvent.type(usernameInput, 'test');
|
||||||
|
userEvent.type(passwordInput, 'test');
|
||||||
|
|
||||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: {} } });
|
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: {} } });
|
||||||
|
const submitButton = await screen.findByText('Continue');
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should should display login error if login not successful', async () => {
|
it('should display an error if username is blank and login is attempted using button', async () => {
|
||||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
const submitButton = await screen.findByText('Continue');
|
|
||||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 401, data: {} });
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
|
userEvent.type(passwordInput, 'test');
|
||||||
|
const submitButton = await screen.findByTestId('basic-auth-submit-btn');
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
const errorDisplay = await screen.findByText(/Authentication Failed/i);
|
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a username/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a password/i)).not.toBeInTheDocument());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(errorDisplay).toBeInTheDocument();
|
expect(mockedUsedNavigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an error if password is blank and login is attempted using button', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
|
userEvent.type(usernameInput, 'test');
|
||||||
|
const submitButton = await screen.findByTestId('basic-auth-submit-btn');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a username/i)).not.toBeInTheDocument());
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a password/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an error if username and password are both blank and login is attempted using button', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const submitButton = await screen.findByTestId('basic-auth-submit-btn');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a username/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a password/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log in the user and navigate to homepage if login is successful using enter key on username field', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
|
userEvent.type(usernameInput, 'test');
|
||||||
|
userEvent.type(passwordInput, 'test');
|
||||||
|
|
||||||
|
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: {} } });
|
||||||
|
userEvent.type(usernameInput, '{enter}');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log in the user and navigate to homepage if login is successful using enter key on password field', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
|
userEvent.type(usernameInput, 'test');
|
||||||
|
userEvent.type(passwordInput, 'test');
|
||||||
|
|
||||||
|
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: {} } });
|
||||||
|
userEvent.type(passwordInput, '{enter}');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).toHaveBeenCalledWith('/home');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an error if username is blank and login is attempted using enter key', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
|
userEvent.type(passwordInput, 'test');
|
||||||
|
userEvent.type(passwordInput, '{enter}');
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a username/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a password/i)).not.toBeInTheDocument());
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an error if password is blank and login is attempted using enter key', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
|
userEvent.type(usernameInput, 'test');
|
||||||
|
userEvent.type(usernameInput, '{enter}');
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a username/i)).not.toBeInTheDocument());
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a password/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display an error if username and password are both blank and login is attempted using enter key', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
|
userEvent.type(passwordInput, '{enter}');
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a username/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => expect(screen.queryByText(/enter a password/i)).toBeInTheDocument());
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should should display login error if login not successful', async () => {
|
||||||
|
render(<SignIn isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||||
|
|
||||||
|
const usernameInput = await screen.findByLabelText(/^Username/i);
|
||||||
|
const passwordInput = await screen.findByLabelText(/^Enter Password/i);
|
||||||
|
userEvent.type(usernameInput, 'test');
|
||||||
|
userEvent.type(passwordInput, 'test');
|
||||||
|
|
||||||
|
jest.spyOn(api, 'get').mockRejectedValue({ status: 401, data: {} });
|
||||||
|
|
||||||
|
const submitButton = await screen.findByText('Continue');
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByText(/Authentication Failed/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedUsedNavigate).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -149,8 +149,8 @@ const useStyles = makeStyles(() => ({
|
|||||||
export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading = () => {} }) {
|
export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading = () => {} }) {
|
||||||
const [usernameError, setUsernameError] = useState(null);
|
const [usernameError, setUsernameError] = useState(null);
|
||||||
const [passwordError, setPasswordError] = useState(null);
|
const [passwordError, setPasswordError] = useState(null);
|
||||||
const [username, setUsername] = useState(null);
|
const [username, setUsername] = useState('');
|
||||||
const [password, setPassword] = useState(null);
|
const [password, setPassword] = useState('');
|
||||||
const [requestProcessing, setRequestProcessing] = useState(false);
|
const [requestProcessing, setRequestProcessing] = useState(false);
|
||||||
const [requestError, setRequestError] = useState(false);
|
const [requestError, setRequestError] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
@ -228,13 +228,20 @@ export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading =
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (event) => {
|
const handleBasicAuthSubmit = () => {
|
||||||
event.preventDefault();
|
setRequestError(false);
|
||||||
if (Object.keys(authMethods).includes('htpasswd')) {
|
const isUsernameValid = handleUsernameValidation(username);
|
||||||
|
const isPasswordValid = handlePasswordValidation(password);
|
||||||
|
if (Object.keys(authMethods).includes('htpasswd') && isUsernameValid && isPasswordValid) {
|
||||||
handleBasicAuth();
|
handleBasicAuth();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClick = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
handleBasicAuthSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
const handleGuestClick = () => {
|
const handleGuestClick = () => {
|
||||||
setRequestProcessing(false);
|
setRequestProcessing(false);
|
||||||
setRequestError(false);
|
setRequestError(false);
|
||||||
@ -251,35 +258,55 @@ export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading =
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUsernameValidation = (username) => {
|
||||||
|
let isValid = true;
|
||||||
|
if (username === '') {
|
||||||
|
setUsernameError('Please enter a username');
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
setUsernameError(null);
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePasswordValidation = (password) => {
|
||||||
|
let isValid = true;
|
||||||
|
if (password === '') {
|
||||||
|
setPasswordError('Please enter a password');
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
setPasswordError(null);
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
|
||||||
const handleChange = (event, type) => {
|
const handleChange = (event, type) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setRequestError(false);
|
setRequestError(false);
|
||||||
|
|
||||||
const val = event.target?.value;
|
const val = event.target?.value;
|
||||||
const isEmpty = val === '';
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'username':
|
case 'username':
|
||||||
setUsername(val);
|
setUsername(val);
|
||||||
if (isEmpty) {
|
handleUsernameValidation(val);
|
||||||
setUsernameError('Please enter a username');
|
|
||||||
} else {
|
|
||||||
setUsernameError(null);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'password':
|
case 'password':
|
||||||
setPassword(val);
|
setPassword(val);
|
||||||
if (isEmpty) {
|
handlePasswordValidation(val);
|
||||||
setPasswordError('Please enter a password');
|
|
||||||
} else {
|
|
||||||
setPasswordError(null);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLoginInputFieldKeyDown = (event) => {
|
||||||
|
const keyPressed = event.key;
|
||||||
|
if (keyPressed === 'Enter') {
|
||||||
|
handleBasicAuthSubmit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const renderThirdPartyLoginMethods = () => {
|
const renderThirdPartyLoginMethods = () => {
|
||||||
let isGoogle = isObject(authMethods.openid?.providers?.google);
|
let isGoogle = isObject(authMethods.openid?.providers?.google);
|
||||||
// let isGitlab = isObject(authMethods.openid?.providers?.gitlab);
|
// let isGitlab = isObject(authMethods.openid?.providers?.gitlab);
|
||||||
@ -315,7 +342,7 @@ export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading =
|
|||||||
{Object.keys(authMethods).length > 1 &&
|
{Object.keys(authMethods).length > 1 &&
|
||||||
Object.keys(authMethods).includes('openid') &&
|
Object.keys(authMethods).includes('openid') &&
|
||||||
Object.keys(authMethods.openid.providers).length > 0 && (
|
Object.keys(authMethods.openid.providers).length > 0 && (
|
||||||
<Divider className={classes.divider} data-testId="openid-divider">
|
<Divider className={classes.divider} data-testid="openid-divider">
|
||||||
or
|
or
|
||||||
</Divider>
|
</Divider>
|
||||||
)}
|
)}
|
||||||
@ -334,6 +361,7 @@ export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading =
|
|||||||
onInput={(e) => handleChange(e, 'username')}
|
onInput={(e) => handleChange(e, 'username')}
|
||||||
error={usernameError != null}
|
error={usernameError != null}
|
||||||
helperText={usernameError}
|
helperText={usernameError}
|
||||||
|
onKeyDown={(e) => handleLoginInputFieldKeyDown(e)}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
margin="normal"
|
margin="normal"
|
||||||
@ -349,6 +377,7 @@ export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading =
|
|||||||
onInput={(e) => handleChange(e, 'password')}
|
onInput={(e) => handleChange(e, 'password')}
|
||||||
error={passwordError != null}
|
error={passwordError != null}
|
||||||
helperText={passwordError}
|
helperText={passwordError}
|
||||||
|
onKeyDown={(e) => handleLoginInputFieldKeyDown(e)}
|
||||||
/>
|
/>
|
||||||
{requestProcessing && <CircularProgress style={{ marginTop: 20 }} color="secondary" />}
|
{requestProcessing && <CircularProgress style={{ marginTop: 20 }} color="secondary" />}
|
||||||
{requestError && (
|
{requestError && (
|
||||||
@ -357,7 +386,13 @@ export default function SignIn({ isLoggedIn, setIsLoggedIn, wrapperSetLoading =
|
|||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<Button fullWidth variant="contained" className={classes.continueButton} onClick={handleClick}>
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
className={classes.continueButton}
|
||||||
|
onClick={handleClick}
|
||||||
|
data-testid="basic-auth-submit-btn"
|
||||||
|
>
|
||||||
Continue
|
Continue
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user