diff --git a/classes/helpers/FrmStylesPreviewHelper.php b/classes/helpers/FrmStylesPreviewHelper.php index 7ff39befcd..a9230d122c 100644 --- a/classes/helpers/FrmStylesPreviewHelper.php +++ b/classes/helpers/FrmStylesPreviewHelper.php @@ -208,6 +208,10 @@ public function get_html_for_form_preview() { // Force is_admin to false so the "Entry Key" field doesn't render in the preview. add_filter( 'frm_is_admin', '__return_false' ); + // The styler edit page also renders its own settings form, so this preview's
landmark + // needs a distinct name to avoid tripping the aria_landmark_name_unique a11y rule. + add_filter( 'frm_form_attributes', array( $this, 'add_preview_landmark_label' ) ); + $target_form_preview_html = FrmFormsController::show_form( $this->form_id, '', 'auto', 'auto' ); $this->form_includes_captcha = wp_script_is( 'captcha-api', 'enqueued' ); @@ -217,6 +221,8 @@ public function get_html_for_form_preview() { wp_dequeue_script( 'captcha-api' ); } + remove_filter( 'frm_form_attributes', array( $this, 'add_preview_landmark_label' ) ); + // Return the is_admin status. // Otherwise success messages won't use the proper mark up and will appear without the green background and padding. remove_filter( 'frm_is_admin', '__return_false' ); @@ -224,6 +230,17 @@ public function get_html_for_form_preview() { return $target_form_preview_html; } + /** + * @since x.x + * + * @param string $attributes + * + * @return string + */ + public function add_preview_landmark_label( $attributes ) { + return $attributes . ' aria-label="' . esc_attr__( 'Form preview', 'formidable' ) . '"'; + } + /** * @since 6.0 * diff --git a/classes/views/styles/_styles-edit.php b/classes/views/styles/_styles-edit.php index 5dbd016d66..96a4084092 100644 --- a/classes/views/styles/_styles-edit.php +++ b/classes/views/styles/_styles-edit.php @@ -7,7 +7,7 @@ // It is accessed from /wp-admin/admin.php?page=formidable-styles&frm_action=edit&form=782 ?>
- + diff --git a/classes/views/xml/import_form.php b/classes/views/xml/import_form.php index e0702edc58..30d0ed4257 100644 --- a/classes/views/xml/import_form.php +++ b/classes/views/xml/import_form.php @@ -30,7 +30,7 @@ ?>


- +

@@ -71,7 +71,7 @@

- + diff --git a/tests/phpunit/base/FrmUnitTest.php b/tests/phpunit/base/FrmUnitTest.php index 1dad220136..5f88fe462b 100644 --- a/tests/phpunit/base/FrmUnitTest.php +++ b/tests/phpunit/base/FrmUnitTest.php @@ -843,4 +843,32 @@ protected function use_frm_role( $role ) { break; } } + + /** + * Assert that every tag in some rendered HTML has a non-empty aria-label, + * and that no two forms share the same one (aria_landmark_name_unique). + * + * @since x.x + * + * @param string $html + * @param int $expected_count Required so an empty/short match list fails loudly instead of + * passing vacuously (assertNotContains/assertSame both pass on an + * empty array). + * + * @return void + */ + protected function assert_form_landmarks_have_unique_names( $html, $expected_count ) { + preg_match_all( '/]*>/', $html, $matches ); + $this->assertCount( $expected_count, $matches[0], 'Unexpected number of elements' ); + + $labels = array(); + + foreach ( $matches[0] as $form_tag ) { + preg_match( '/aria-label="([^"]*)"/', $form_tag, $label_match ); + $labels[] = $label_match[1] ?? ''; + } + + $this->assertNotContains( '', $labels, 'Every form landmark needs a non-empty accessible name' ); + $this->assertSame( array_unique( $labels ), $labels, 'Form landmarks must have distinct accessible names' ); + } } diff --git a/tests/phpunit/styles/test_FrmStylesController.php b/tests/phpunit/styles/test_FrmStylesController.php index d4061d4bdb..d0f8cb9fd0 100644 --- a/tests/phpunit/styles/test_FrmStylesController.php +++ b/tests/phpunit/styles/test_FrmStylesController.php @@ -48,6 +48,35 @@ private function get_custom_stylesheet() { return $stylesheet_urls; } + /** + * The styler edit view renders two elements on the same page: the style + * settings sidebar form, and the live form preview. Both need distinct + * accessible names or they violate the aria_landmark_name_unique a11y rule. + * + * @covers FrmStylesController::render_style_page + */ + public function test_render_style_page_has_unique_landmark_names_for_both_forms() { + $this->set_current_user_to_1(); + + // render_style_page() reads $_GET to decide the view ('edit' vs 'list'); a leftover + // 'form'/'style_id' from another test would silently switch this to the list view. + $_GET = array(); + + $form_id = $this->factory->form->create(); + $form = FrmForm::getOne( $form_id ); + $frm_style = new FrmStyle( 'default' ); + $active_style = $frm_style->get_one(); + + ob_start(); + $this->run_private_method( + array( 'FrmStylesController', 'render_style_page' ), + array( $active_style, $form, $active_style ) + ); + $html = ob_get_clean(); + + $this->assert_form_landmarks_have_unique_names( $html, 2 ); + } + /** * @covers FrmStylesController::save_style * @covers FrmStyle::update diff --git a/tests/phpunit/xml/test_FrmXMLController.php b/tests/phpunit/xml/test_FrmXMLController.php index dff62ba233..87dd91e066 100644 --- a/tests/phpunit/xml/test_FrmXMLController.php +++ b/tests/phpunit/xml/test_FrmXMLController.php @@ -23,4 +23,19 @@ public function test_validate_xml_url() { private function validate_xml_url( $url ) { return $this->run_private_method( array( 'FrmXMLController', 'validate_xml_url' ), array( $url ) ); } + + /** + * The Import/Export page renders two elements (Import, Export). Both + * need distinct accessible names or they violate the aria_landmark_name_unique + * a11y rule. + * + * @covers FrmXMLController::form + */ + public function test_form_has_unique_landmark_names_for_import_and_export_forms() { + ob_start(); + FrmXMLController::form(); + $html = ob_get_clean(); + + $this->assert_form_landmarks_have_unique_names( $html, 2 ); + } }