-
Notifications
You must be signed in to change notification settings - Fork 573
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
style: convert login page to carbon (#4724)
* chore: copy old login component to Carbon folder * style: carbonize login page * style: disable login button when any field is empty
- Loading branch information
Showing
9 changed files
with
610 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH | ||
* under one or more contributor license agreements. Licensed under a proprietary license. | ||
* See the License.txt file for more information. You may not use this file | ||
* except in compliance with the proprietary license. | ||
*/ | ||
|
||
import {render, screen} from 'modules/testing-library'; | ||
import {ThemeProvider} from 'modules/theme/ThemeProvider'; | ||
import {Disclaimer} from './index'; | ||
|
||
const DISCLAIMER_TEXT = | ||
'Non-Production License. If you would like information on production usage, please refer to our terms & conditions page or contact sales.'; | ||
|
||
describe('<Disclaimer />', () => { | ||
afterEach(() => { | ||
window.clientConfig = undefined; | ||
}); | ||
|
||
it('should show the disclaimer', () => { | ||
const {rerender} = render(<Disclaimer />, {wrapper: ThemeProvider}); | ||
|
||
// we need this custom selector because the text contains a link | ||
expect( | ||
screen.getByText((content, element) => { | ||
return content !== '' && element?.textContent === DISCLAIMER_TEXT; | ||
}) | ||
).toBeInTheDocument(); | ||
expect( | ||
screen.getByRole('link', {name: 'terms & conditions page'}) | ||
).toHaveAttribute( | ||
'href', | ||
'https://camunda.com/legal/terms/camunda-platform/camunda-platform-8-self-managed/' | ||
); | ||
expect(screen.getByRole('link', {name: 'contact sales'})).toHaveAttribute( | ||
'href', | ||
'https://camunda.com/contact/' | ||
); | ||
|
||
window.clientConfig = { | ||
isEnterprise: false, | ||
}; | ||
rerender(<Disclaimer />); | ||
|
||
expect( | ||
screen.getByText((content, element) => { | ||
return content !== '' && element?.textContent === DISCLAIMER_TEXT; | ||
}) | ||
).toBeInTheDocument(); | ||
expect( | ||
screen.getByRole('link', {name: 'terms & conditions page'}) | ||
).toHaveAttribute( | ||
'href', | ||
'https://camunda.com/legal/terms/camunda-platform/camunda-platform-8-self-managed/' | ||
); | ||
expect(screen.getByRole('link', {name: 'contact sales'})).toHaveAttribute( | ||
'href', | ||
'https://camunda.com/contact/' | ||
); | ||
}); | ||
|
||
it('should not render the disclaimer', () => { | ||
window.clientConfig = { | ||
isEnterprise: true, | ||
}; | ||
render(<Disclaimer />); | ||
|
||
expect( | ||
screen.queryByText((content, element) => { | ||
return content !== '' && element?.textContent === DISCLAIMER_TEXT; | ||
}) | ||
).not.toBeInTheDocument(); | ||
expect( | ||
screen.queryByRole('link', {name: 'terms & conditions page'}) | ||
).not.toBeInTheDocument(); | ||
expect( | ||
screen.queryByRole('link', {name: 'contact sales'}) | ||
).not.toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH | ||
* under one or more contributor license agreements. Licensed under a proprietary license. | ||
* See the License.txt file for more information. You may not use this file | ||
* except in compliance with the proprietary license. | ||
*/ | ||
|
||
import {Link} from '@carbon/react'; | ||
import {Container} from './styled'; | ||
|
||
const Disclaimer: React.FC = () => { | ||
return window.clientConfig?.isEnterprise ? null : ( | ||
<Container> | ||
Non-Production License. If you would like information on production usage, | ||
please refer to our{' '} | ||
<Link | ||
href="https://camunda.com/legal/terms/camunda-platform/camunda-platform-8-self-managed/" | ||
target="_blank" | ||
inline | ||
> | ||
terms & conditions page | ||
</Link>{' '} | ||
or{' '} | ||
<Link href="https://camunda.com/contact/" target="_blank" inline> | ||
contact sales | ||
</Link> | ||
. | ||
</Container> | ||
); | ||
}; | ||
|
||
export {Disclaimer}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH | ||
* under one or more contributor license agreements. Licensed under a proprietary license. | ||
* See the License.txt file for more information. You may not use this file | ||
* except in compliance with the proprietary license. | ||
*/ | ||
|
||
import styled from 'styled-components'; | ||
import {styles} from '@carbon/elements'; | ||
|
||
const Container = styled.span` | ||
color: var(--cds-text-secondary); | ||
text-align: center; | ||
padding-top: var(--cds-spacing-05); | ||
&, | ||
& a { | ||
${styles.legal01}; | ||
} | ||
`; | ||
|
||
export {Container}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH | ||
* under one or more contributor license agreements. Licensed under a proprietary license. | ||
* See the License.txt file for more information. You may not use this file | ||
* except in compliance with the proprietary license. | ||
*/ | ||
|
||
import {Loading} from '@carbon/react'; | ||
|
||
const LoadingSpinner: typeof Loading = (props) => { | ||
return <Loading data-testid="spinner" withOverlay={false} {...props} />; | ||
}; | ||
|
||
export {LoadingSpinner}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* | ||
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH | ||
* under one or more contributor license agreements. Licensed under a proprietary license. | ||
* See the License.txt file for more information. You may not use this file | ||
* except in compliance with the proprietary license. | ||
*/ | ||
|
||
const LOGIN_ERROR = 'Username and Password do not match'; | ||
const GENERIC_ERROR = 'Credentials could not be verified'; | ||
|
||
export {LOGIN_ERROR, GENERIC_ERROR}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* | ||
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH | ||
* under one or more contributor license agreements. Licensed under a proprietary license. | ||
* See the License.txt file for more information. You may not use this file | ||
* except in compliance with the proprietary license. | ||
*/ | ||
|
||
import { | ||
render, | ||
screen, | ||
waitForElementToBeRemoved, | ||
waitFor, | ||
} from 'modules/testing-library'; | ||
import {Link, MemoryRouter, To} from 'react-router-dom'; | ||
import {ThemeProvider} from 'modules/theme/ThemeProvider'; | ||
import {Login} from './index'; | ||
import {LOGIN_ERROR, GENERIC_ERROR} from './constants'; | ||
import {LocationLog} from 'modules/utils/LocationLog'; | ||
import {authenticationStore} from 'modules/stores/authentication'; | ||
import {mockLogin} from 'modules/mocks/api/login'; | ||
|
||
function createWrapper( | ||
initialPath: string = '/carbon/login', | ||
referrer: To = {pathname: '/carbon/processes'} | ||
) { | ||
const Wrapper: React.FC<{children?: React.ReactNode}> = ({children}) => { | ||
return ( | ||
<ThemeProvider> | ||
<MemoryRouter initialEntries={[initialPath]}> | ||
{children} | ||
<Link | ||
to="/carbon/login" | ||
state={{ | ||
referrer, | ||
}} | ||
> | ||
emulate auth check | ||
</Link> | ||
<LocationLog /> | ||
</MemoryRouter> | ||
</ThemeProvider> | ||
); | ||
}; | ||
|
||
return Wrapper; | ||
} | ||
|
||
describe('<Login />', () => { | ||
afterEach(() => { | ||
authenticationStore.reset(); | ||
}); | ||
|
||
it('should login', async () => { | ||
mockLogin().withSuccess(null); | ||
|
||
const {user} = render(<Login />, { | ||
wrapper: createWrapper('/carbon/login'), | ||
}); | ||
|
||
await user.type(screen.getByLabelText(/username/i), 'demo'); | ||
await user.type(screen.getByLabelText(/password/i), 'demo'); | ||
await user.click(screen.getByRole('button', {name: 'Login'})); | ||
|
||
await waitFor(() => | ||
expect(screen.getByTestId('pathname')).toHaveTextContent(/^\/carbon$/) | ||
); | ||
}); | ||
|
||
it('should show a loading spinner', async () => { | ||
mockLogin().withServerError(); | ||
|
||
const {user} = render(<Login />, { | ||
wrapper: createWrapper(), | ||
}); | ||
|
||
await user.type(screen.getByLabelText(/username/i), 'demo'); | ||
await user.type(screen.getByLabelText(/password/i), 'demo'); | ||
await user.click(screen.getByRole('button', {name: 'Login'})); | ||
|
||
expect(screen.getByTestId('spinner')).toBeInTheDocument(); | ||
await waitForElementToBeRemoved(screen.getByTestId('spinner')); | ||
|
||
mockLogin().withSuccess(null); | ||
|
||
await user.click(screen.getByRole('button', {name: 'Login'})); | ||
|
||
expect(screen.getByTestId('spinner')).toBeInTheDocument(); | ||
await waitForElementToBeRemoved(screen.getByTestId('spinner')); | ||
}); | ||
|
||
it('should redirect to the previous page', async () => { | ||
mockLogin().withSuccess(null); | ||
|
||
const {user} = render(<Login />, { | ||
wrapper: createWrapper('/carbon/login'), | ||
}); | ||
|
||
await user.click(screen.getByText(/emulate auth check/i)); | ||
|
||
await user.type(screen.getByLabelText(/username/i), 'demo'); | ||
await user.type(screen.getByLabelText(/password/i), 'demo'); | ||
await user.click(screen.getByRole('button', {name: 'Login'})); | ||
|
||
await waitFor(() => | ||
expect(screen.getByTestId('pathname')).toHaveTextContent( | ||
/^\/carbon\/processes$/ | ||
) | ||
); | ||
}); | ||
|
||
it('should disable the login button when any field is empty', async () => { | ||
const {user} = render(<Login />, { | ||
wrapper: createWrapper(), | ||
}); | ||
|
||
expect(screen.getByRole('button', {name: 'Login'})).toBeDisabled(); | ||
|
||
await user.type(screen.getByLabelText(/username/i), 'demo'); | ||
|
||
expect(screen.getByRole('button', {name: 'Login'})).toBeDisabled(); | ||
|
||
await user.type(screen.getByLabelText(/password/i), 'demo'); | ||
|
||
expect(screen.getByRole('button', {name: 'Login'})).toBeEnabled(); | ||
|
||
await user.clear(screen.getByLabelText(/password/i)); | ||
await user.clear(screen.getByLabelText(/username/i)); | ||
|
||
expect(screen.getByRole('button', {name: 'Login'})).toBeDisabled(); | ||
}); | ||
|
||
it('should handle wrong credentials', async () => { | ||
mockLogin().withServerError(401); | ||
|
||
const {user} = render(<Login />, { | ||
wrapper: createWrapper(), | ||
}); | ||
|
||
await user.type(screen.getByLabelText(/username/i), 'wrong'); | ||
await user.type(screen.getByLabelText(/password/i), 'credentials'); | ||
await user.click(screen.getByRole('button', {name: 'Login'})); | ||
|
||
expect(await screen.findByText(LOGIN_ERROR)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should handle generic errors', async () => { | ||
mockLogin().withServerError(); | ||
|
||
const {user} = render(<Login />, { | ||
wrapper: createWrapper(), | ||
}); | ||
|
||
await user.type(screen.getByLabelText(/username/i), 'demo'); | ||
await user.type(screen.getByLabelText(/password/i), 'demo'); | ||
await user.click(screen.getByRole('button', {name: 'Login'})); | ||
|
||
expect(await screen.findByText(GENERIC_ERROR)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should handle request failures', async () => { | ||
mockLogin().withNetworkError(); | ||
|
||
const {user} = render(<Login />, { | ||
wrapper: createWrapper(), | ||
}); | ||
|
||
await user.type(screen.getByLabelText(/username/i), 'demo'); | ||
await user.type(screen.getByLabelText(/password/i), 'demo'); | ||
await user.click(screen.getByRole('button', {name: 'Login'})); | ||
|
||
expect(await screen.findByText(GENERIC_ERROR)).toBeInTheDocument(); | ||
}); | ||
}); |
Oops, something went wrong.