mirror of
https://github.com/quay/quay.git
synced 2026-01-26 06:21:37 +03:00
[redhat-3.16] uibug: updated new UI to ask user to verify the mail when creating new account with email enabled (PROJQUAY-9655) (#4450)
* updated new UI to ask user to verify the mail when creating new account with email enabled * fixed failing cypress test --------- Co-authored-by: shudeshp <shudeshp@redhat.com>
This commit is contained in:
committed by
GitHub
parent
6eeb06dbfe
commit
ef66ec6920
@@ -195,4 +195,69 @@ describe('Create Account Page', () => {
|
||||
cy.contains('Already have an account?');
|
||||
cy.contains('Sign in');
|
||||
});
|
||||
|
||||
it('Shows email verification message when awaiting_verification is true', () => {
|
||||
const testUser = {
|
||||
username: `testuser${Date.now()}`,
|
||||
email: `test${Date.now()}@example.com`,
|
||||
password: 'validpassword123',
|
||||
};
|
||||
|
||||
// Setup account creation with awaiting_verification response
|
||||
cy.intercept('POST', '/api/v1/user/', {
|
||||
statusCode: 200,
|
||||
body: {awaiting_verification: true},
|
||||
}).as('createUserAwaitingVerification');
|
||||
|
||||
// Setup signin intercept to verify it's NOT called
|
||||
cy.intercept('POST', '/api/v1/signin', (req) => {
|
||||
throw new Error('Signin should not be called when awaiting verification');
|
||||
}).as('signinShouldNotBeCalled');
|
||||
|
||||
cy.visit('/createaccount');
|
||||
|
||||
// Fill form with valid data
|
||||
cy.get('#username').type(testUser.username);
|
||||
cy.get('#email').type(testUser.email);
|
||||
cy.get('#password').type(testUser.password);
|
||||
cy.get('#confirm-password').type(testUser.password);
|
||||
|
||||
// Submit form
|
||||
cy.get('button[type=submit]').click();
|
||||
|
||||
// Wait for create user API call
|
||||
cy.wait('@createUserAwaitingVerification').then((interception) => {
|
||||
expect(interception.request.body).to.deep.equal({
|
||||
username: testUser.username,
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
});
|
||||
});
|
||||
|
||||
// Should show verification message
|
||||
cy.get('[data-testid="awaiting-verification-alert"]').should('be.visible');
|
||||
cy.contains(
|
||||
'Thank you for registering! We have sent you an activation email.',
|
||||
);
|
||||
cy.contains('verify your email address').should('be.visible');
|
||||
|
||||
// Form should be hidden (check visibility, not existence since display:none keeps elements in DOM)
|
||||
cy.get('#username').should('not.be.visible');
|
||||
cy.get('#email').should('not.be.visible');
|
||||
cy.get('#password').should('not.be.visible');
|
||||
|
||||
// Should not redirect to organization page
|
||||
cy.url().should('include', '/createaccount');
|
||||
cy.url().should('not.include', '/organization');
|
||||
|
||||
// Should show sign in link (it's outside the form when awaiting verification)
|
||||
// Find the visible link that's not inside a hidden form
|
||||
cy.get('a[href="/signin"]').should('be.visible');
|
||||
// The text should also be visible (rendered outside the hidden form)
|
||||
cy.contains('body', 'Already have an account?').should('be.visible');
|
||||
|
||||
// Verify no auto-login was attempted - signin API should not be called
|
||||
// If signin was attempted, it would have thrown an error from the intercept
|
||||
cy.wait(500); // Small wait to ensure no signin API call is made
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,11 +24,17 @@ export function useCreateAccount() {
|
||||
|
||||
try {
|
||||
// Create the user account
|
||||
await createUser(username, password, email);
|
||||
const response = await createUser(username, password, email);
|
||||
|
||||
// Clear CSRF token after account creation (session state changed)
|
||||
GlobalAuthState.csrfToken = null;
|
||||
|
||||
// Check if email verification is required
|
||||
if (response.awaiting_verification === true) {
|
||||
// Email verification required, return success but indicate verification is needed
|
||||
return {success: true, awaitingVerification: true};
|
||||
}
|
||||
|
||||
// Auto-login after successful account creation
|
||||
try {
|
||||
const loginResponse = await loginUser(username, password);
|
||||
|
||||
@@ -122,11 +122,16 @@ export interface CreateUserRequest {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface CreateUserResponse {
|
||||
awaiting_verification?: boolean;
|
||||
[key: string]: any; // Allow other fields from the API response
|
||||
}
|
||||
|
||||
export async function createUser(
|
||||
username: string,
|
||||
password: string,
|
||||
email: string,
|
||||
) {
|
||||
): Promise<CreateUserResponse> {
|
||||
const response = await axios.post('/api/v1/user/', {
|
||||
username,
|
||||
password,
|
||||
|
||||
@@ -22,6 +22,7 @@ export function CreateAccount() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [awaitingVerification, setAwaitingVerification] = useState(false);
|
||||
|
||||
const {createAccountWithAutoLogin, isLoading, error, setError} =
|
||||
useCreateAccount();
|
||||
@@ -75,7 +76,10 @@ export function CreateAccount() {
|
||||
return;
|
||||
}
|
||||
|
||||
await createAccountWithAutoLogin(username, password, email);
|
||||
const result = await createAccountWithAutoLogin(username, password, email);
|
||||
if (result.success && result.awaitingVerification) {
|
||||
setAwaitingVerification(true);
|
||||
}
|
||||
};
|
||||
|
||||
const errMessage = (
|
||||
@@ -89,166 +93,194 @@ export function CreateAccount() {
|
||||
);
|
||||
|
||||
const createAccountForm = (
|
||||
<Form>
|
||||
<FormGroup
|
||||
label="Username"
|
||||
isRequired
|
||||
fieldId="username"
|
||||
validated={validateUsername(username)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
value={username}
|
||||
onChange={(_event, v) => setUsername(v)}
|
||||
validated={validateUsername(username)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validateUsername(username) === ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validateUsername(username) === ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Username must be at least 3 characters and contain only letters,
|
||||
numbers, hyphens, underscores, and periods
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label="Email"
|
||||
isRequired
|
||||
fieldId="email"
|
||||
validated={validateEmail(email)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={(_event, v) => setEmail(v)}
|
||||
validated={validateEmail(email)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validateEmail(email) === ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validateEmail(email) === ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Please enter a valid email address
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label="Password"
|
||||
isRequired
|
||||
fieldId="password"
|
||||
validated={validatePassword(password)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(_event, v) => setPassword(v)}
|
||||
validated={validatePassword(password)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validatePassword(password) === ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validatePassword(password) === ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Password must be at least 8 characters long
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label="Confirm Password"
|
||||
isRequired
|
||||
fieldId="confirm-password"
|
||||
validated={validateConfirmPassword(confirmPassword)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="password"
|
||||
id="confirm-password"
|
||||
name="confirm-password"
|
||||
value={confirmPassword}
|
||||
onChange={(_event, v) => setConfirmPassword(v)}
|
||||
validated={validateConfirmPassword(confirmPassword)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validateConfirmPassword(confirmPassword) ===
|
||||
ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validateConfirmPassword(confirmPassword) ===
|
||||
ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Passwords must match
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
{error && errMessage}
|
||||
|
||||
<FormGroup>
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
isBlock
|
||||
isDisabled={!isFormValid() || isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={onCreateAccountClick}
|
||||
<>
|
||||
{awaitingVerification && (
|
||||
<Alert
|
||||
variant="info"
|
||||
isInline
|
||||
title=""
|
||||
style={{marginBottom: '20px'}}
|
||||
data-testid="awaiting-verification-alert"
|
||||
>
|
||||
Create Account
|
||||
</Button>
|
||||
</FormGroup>
|
||||
Thank you for registering! We have sent you an activation email. You
|
||||
must <strong>verify your email address</strong> before you can
|
||||
continue.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<FormGroup>
|
||||
<Form style={{display: awaitingVerification ? 'none' : 'block'}}>
|
||||
<FormGroup
|
||||
label="Username"
|
||||
isRequired
|
||||
fieldId="username"
|
||||
validated={validateUsername(username)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
value={username}
|
||||
onChange={(_event, v) => setUsername(v)}
|
||||
validated={validateUsername(username)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validateUsername(username) === ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validateUsername(username) === ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Username must be at least 3 characters and contain only letters,
|
||||
numbers, hyphens, underscores, and periods
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label="Email"
|
||||
isRequired
|
||||
fieldId="email"
|
||||
validated={validateEmail(email)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={(_event, v) => setEmail(v)}
|
||||
validated={validateEmail(email)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validateEmail(email) === ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validateEmail(email) === ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Please enter a valid email address
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label="Password"
|
||||
isRequired
|
||||
fieldId="password"
|
||||
validated={validatePassword(password)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(_event, v) => setPassword(v)}
|
||||
validated={validatePassword(password)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validatePassword(password) === ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validatePassword(password) === ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Password must be at least 8 characters long
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label="Confirm Password"
|
||||
isRequired
|
||||
fieldId="confirm-password"
|
||||
validated={validateConfirmPassword(confirmPassword)}
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="password"
|
||||
id="confirm-password"
|
||||
name="confirm-password"
|
||||
value={confirmPassword}
|
||||
onChange={(_event, v) => setConfirmPassword(v)}
|
||||
validated={validateConfirmPassword(confirmPassword)}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
validateConfirmPassword(confirmPassword) ===
|
||||
ValidatedOptions.error
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
icon={
|
||||
validateConfirmPassword(confirmPassword) ===
|
||||
ValidatedOptions.error ? (
|
||||
<ExclamationCircleIcon />
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Passwords must match
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
|
||||
{error && errMessage}
|
||||
|
||||
<FormGroup>
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
isBlock
|
||||
isDisabled={!isFormValid() || isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={onCreateAccountClick}
|
||||
>
|
||||
Create Account
|
||||
</Button>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<div style={{textAlign: 'center', marginTop: '16px'}}>
|
||||
Already have an account?{' '}
|
||||
<Link
|
||||
to="/signin"
|
||||
style={{color: 'var(--pf-v5-global--link--Color)'}}
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
|
||||
{awaitingVerification && (
|
||||
<div style={{textAlign: 'center', marginTop: '16px'}}>
|
||||
Already have an account?{' '}
|
||||
<Link
|
||||
@@ -258,8 +290,8 @@ export function CreateAccount() {
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user