Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions src/wp-login.php
Original file line number Diff line number Diff line change
Expand Up @@ -1496,17 +1496,34 @@ function wp_login_viewport_meta() {

$rememberme = ! empty( $_POST['rememberme'] );

$aria_describedby = '';
$has_errors = $errors->has_errors();
/*
* login_header() splits the errors into a separate notice per severity and
* can display both at once, so reference every notice that is displayed.
*/
$has_error_notice = false;
$has_message_notice = false;

foreach ( $errors->get_error_codes() as $code ) {
if ( 'message' === $errors->get_error_data( $code ) ) {
$has_message_notice = true;
} else {
$has_error_notice = true;
}
}

if ( $has_errors ) {
$aria_describedby = ' aria-describedby="login_error"';
// Ordered to match the order login_header() displays the notices in.
$describedby_ids = array();

if ( $has_error_notice ) {
$describedby_ids[] = 'login_error';
}

if ( $has_errors && 'message' === $errors->get_error_data() ) {
$aria_describedby = ' aria-describedby="login-message"';
if ( $has_message_notice ) {
$describedby_ids[] = 'login-message';
}

$aria_describedby = $describedby_ids ? ' aria-describedby="' . implode( ' ', $describedby_ids ) . '"' : '';

wp_enqueue_script( 'user-profile' );
wp_enqueue_script( 'wp-tooltip' );
?>
Expand Down
77 changes: 77 additions & 0 deletions tests/e2e/specs/login-form-notices.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* WordPress dependencies
*/
import { test, expect } from '@wordpress/e2e-test-utils-playwright';

/*
* login_header() displays a separate notice per error severity and can display
* both at once. Every notice displayed must be referenced by the login fields.
*/
test.describe( 'Login form notices', () => {
// The login form is only displayed to logged out users.
test.use( { storageState: { cookies: [], origins: [] } } );

async function submitInvalidCredentials( page ) {
await page.locator( '#user_login' ).fill( 'admin' );
await page.locator( '#user_pass' ).fill( 'incorrect-password' );
await page.locator( '#wp-submit' ).click();
}

test( 'should reference both notices when an error and a message are displayed', async ( {
page,
} ) => {
// Redirecting back to the About page after an update adds a message.
await page.goto(
'/wp-login.php?redirect_to=' +
encodeURIComponent( '/wp-admin/about.php?updated' )
);
await submitInvalidCredentials( page );

await expect( page.locator( '#login_error' ) ).toBeVisible();
await expect( page.locator( '#login-message' ) ).toBeVisible();

await expect( page.locator( '#user_login' ) ).toHaveAttribute(
'aria-describedby',
'login_error login-message'
);
await expect( page.locator( '#user_pass' ) ).toHaveAttribute(
'aria-describedby',
'login_error login-message'
);
} );

test( 'should reference the error notice when only an error is displayed', async ( {
page,
} ) => {
await page.goto( '/wp-login.php' );
await submitInvalidCredentials( page );

await expect( page.locator( '#user_login' ) ).toHaveAttribute(
'aria-describedby',
'login_error'
);
} );

test( 'should reference the message notice when only a message is displayed', async ( {
page,
} ) => {
await page.goto( '/wp-login.php?loggedout=true' );

await expect( page.locator( '#user_login' ) ).toHaveAttribute(
'aria-describedby',
'login-message'
);
} );

test( 'should not reference a notice when none is displayed', async ( {
page,
} ) => {
await page.goto( '/wp-login.php' );

expect(
await page
.locator( '#user_login' )
.getAttribute( 'aria-describedby' )
).toBeNull();
} );
} );
Loading