Skip to content

Add iframe missing title fix (backend + frontend) with tests - #1586

Draft
SteveJonesDev wants to merge 1 commit into
developfrom
codex/identify-rule-without-a-fix-and-propose-solution
Draft

SteveJonesDev wants to merge 1 commit into
developfrom
codex/identify-rule-without-a-fix-and-propose-solution

Conversation

@SteveJonesDev

@SteveJonesDev SteveJonesDev commented Mar 20, 2026 •

Copy link
Copy Markdown
Member

Motivation

  • Provide an opt-in fix that adds descriptive fallback title attributes to <iframe> elements that are missing them to improve accessibility for screen reader users.

Description

  • Add a new backend fix class IframeMissingTitleFix that registers a settings field and exposes frontend data when enabled.
  • Register the new fix in FixesManager so it is loaded with other fixes.
  • Update IframeMissingTitleRule to reference the new fix in the rule definition and to update the how_to_fix guidance.
  • Implement the frontend behavior in src/frontendFixes/Fixes/iframeMissingTitleFix.js which computes a hostname-based or generic fallback and sets title on iframes without overwriting existing titles, and wire it into src/frontendFixes/index.js for lazy loading.
  • Add automated tests: a PHPUnit test tests/phpunit/.../IframeMissingTitleFixTest.php for the fix class and Jest tests tests/jest/frontendFixes/iframeMissingTitleFix.test.js for the frontend behavior.

Testing

  • Ran the new PHPUnit test tests/phpunit/includes/classes/Fixes/Fix/IframeMissingTitleFixTest.php, which succeeded.
  • Ran the new Jest tests tests/jest/frontendFixes/iframeMissingTitleFix.test.js, which succeeded.

Codex Task

Summary by CodeRabbit

Release Notes

  • New Features

    • Added automatic iframe title detection and assignment for improved accessibility. When enabled in Accessibility Checker settings, missing iframe titles are automatically populated with descriptive labels.
  • Tests

    • Added comprehensive test coverage for iframe title functionality across frontend and backend.

@SteveJonesDev SteveJonesDev added the codex Applied by automation label Mar 20, 2026 — with ChatGPT Codex Connector
@coderabbitai

coderabbitai Bot commented Mar 20, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This change introduces an iframe accessibility fix that automatically adds missing titles to iframe elements. It includes a PHP fix class registered with the manager, an associated rule update, frontend JavaScript implementation with URL-based fallback title generation, and comprehensive test coverage spanning both backend and frontend.

Changes

Cohort / File(s) Summary
PHP Backend Fix Infrastructure
includes/classes/Fixes/Fix/IframeMissingTitleFix.php, includes/classes/Fixes/FixesManager.php, includes/classes/Rules/Rule/IframeMissingTitleRule.php
New IframeMissingTitleFix class implementing FixInterface with registration, field configuration, and filter injection. Manager imports and registers the fix in default list. Rule adds fix slug association and extends how_to_fix text.
Frontend Implementation
src/frontendFixes/Fixes/iframeMissingTitleFix.js, src/frontendFixes/index.js
New frontend fix module that reads enabled flag from window.edac_frontend_fixes and conditionally applies titles to iframes. Uses URL parsing for hostname-based fallback titles. Index adds conditional lazy-loading via dynamic import.
Test Coverage
tests/phpunit/includes/classes/Fixes/Fix/IframeMissingTitleFixTest.php, tests/jest/frontendFixes/iframeMissingTitleFix.test.js
PHPUnit test verifying fix metadata and filter injection when enabled. Jest tests verify fallback title generation from iframe src URL and preservation of existing titles.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • pattonwebz

Poem

🐰 A curious fix hops into view,
Adding titles where iframes grew,
From URL roots, fallbacks bloom,
Accessible content fills the room,
Our frames now speak, bright and true! 🎯

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: implementing a new iframe missing title fix with both backend and frontend components plus tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/identify-rule-without-a-fix-and-propose-solution
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces an opt-in accessibility fix that automatically adds descriptive fallback title attributes to <iframe> elements that are missing them. This enhancement significantly improves the experience for screen reader users by providing context for embedded content, making web pages easier to navigate and understand. The changes span both backend and frontend components, ensuring proper configuration and dynamic application of the fix.

Highlights

  • New Backend Fix Class: Introduced a new backend PHP class, IframeMissingTitleFix, to manage the opt-in setting for adding missing iframe titles.
  • Fix Registration: Integrated the IframeMissingTitleFix into the FixesManager to ensure it is properly loaded and available.
  • Rule Update: Updated the IframeMissingTitleRule to include a reference to the new fix and enhance the how_to_fix guidance for users.
  • Frontend Implementation: Implemented frontend JavaScript logic in iframeMissingTitleFix.js to dynamically add descriptive fallback titles to iframes lacking them, prioritizing hostname-based titles.
  • Lazy Loading: Configured the frontend fix for lazy loading in src/frontendFixes/index.js to optimize performance.
  • Automated Tests: Added comprehensive PHPUnit and Jest tests to validate both the backend fix class and the frontend behavior.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable accessibility fix for <iframe> elements that are missing a title attribute. The implementation is well-structured, covering both backend (PHP) and frontend (JavaScript) aspects, and includes corresponding tests. My review includes a few suggestions for improvement, primarily focusing on enhancing code clarity, efficiency, and test coverage. Specifically, I've pointed out a redundant test in the PHPUnit suite, opportunities to improve the Jest test suite's coverage of edge cases, and a minor refactoring in the main PHP fix class for better performance and readability.

Comment on lines +70 to +76
$fields[ 'edac_fix_' . $this->get_slug() ] = [
'label' => esc_html__( 'Add Missing iframe Titles', 'accessibility-checker' ),
'type' => 'checkbox',
'labelledby' => '',
'description' => esc_html__( 'Adds a generated, descriptive title to iframe elements that are missing one.', 'accessibility-checker' ),
'fix_slug' => $this->get_slug(),
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The labelledby property is set to an empty string. In HTML, the labelledby attribute is used to associate an element with its label via an ID. An empty value is not valid and serves no purpose here. For cleaner code, it's better to remove this line if it's not being used.

		$fields[ 'edac_fix_' . $this->get_slug() ] = [
			'label'       => esc_html__( 'Add Missing iframe Titles', 'accessibility-checker' ),
			'type'        => 'checkbox',
			'description' => esc_html__( 'Adds a generated, descriptive title to iframe elements that are missing one.', 'accessibility-checker' ),
			'fix_slug'    => $this->get_slug(),
		];

Comment on lines +86 to +100
public function run(): void {
if ( ! get_option( 'edac_fix_' . $this->get_slug(), false ) ) {
return;
}

add_filter(
'edac_filter_frontend_fixes_data',
function ( $data ) {
$data[ $this->get_slug() ] = [
'enabled' => get_option( 'edac_fix_' . $this->get_slug(), false ),
];
return $data;
}
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This method can be made more efficient and clearer:

  1. The get_option() call is performed twice. It's better to fetch the option value once at the start of the method.
  2. Since you've already confirmed the fix is enabled with the if condition, the 'enabled' key in the data array can be hardcoded to true inside the filter callback.
  3. get_slug() is a static method. It's a best practice to call it using self::get_slug() instead of $this->get_slug() to make it clear that it's a static call.

Here is a suggested refactoring that applies these points:

	public function run(): void {
		if ( ! get_option( 'edac_fix_' . self::get_slug(), false ) ) {
			return;
		}

		add_filter(
			'edac_filter_frontend_fixes_data',
			function ( $data ) {
				$data[ self::get_slug() ] = [
					'enabled' => true,
				];
				return $data;
			}
		);
	}

Comment on lines +1 to +47
/**
* Tests for iframe missing title frontend fix.
*/

describe( 'iframeMissingTitleFix', () => {
let iframeMissingTitleFix;

beforeEach( async () => {
document.body.innerHTML = '';
jest.resetModules();
window.edac_frontend_fixes = {
iframe_missing_title: {
enabled: true,
},
};

const module = await import( '../../../src/frontendFixes/Fixes/iframeMissingTitleFix.js' );
iframeMissingTitleFix = module.default;
} );

test( 'adds fallback title using iframe hostname when src exists', () => {
document.body.innerHTML = '<iframe src="https://www.youtube.com/embed/example"></iframe>';

iframeMissingTitleFix();

const iframe = document.querySelector( 'iframe' );
expect( iframe.getAttribute( 'title' ) ).toBe( 'Embedded content from www.youtube.com' );
} );

test( 'adds generic fallback title when src is missing', () => {
document.body.innerHTML = '<iframe></iframe>';

iframeMissingTitleFix();

const iframe = document.querySelector( 'iframe' );
expect( iframe.getAttribute( 'title' ) ).toBe( 'Embedded content' );
} );

test( 'does not overwrite existing title attributes', () => {
document.body.innerHTML = '<iframe src="https://example.com" title="Custom iframe title"></iframe>';

iframeMissingTitleFix();

const iframe = document.querySelector( 'iframe' );
expect( iframe.getAttribute( 'title' ) ).toBe( 'Custom iframe title' );
} );
} );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The existing tests cover the main functionality well. To make the tests more robust, consider adding a few more test cases for edge scenarios:

  • An iframe with an empty title attribute (e.g., title="").
  • An iframe with a title attribute containing only whitespace (e.g., title=" ").
  • An iframe with an invalid src attribute, to ensure it falls back to the generic title.

This will help ensure the fix behaves as expected in all situations.

Comment on lines +67 to +79
/**
* Test frontend data is provided when the setting is enabled.
*
* @return void
*/
public function test_iframe_missing_title_fix_data_is_added() {
update_option( 'edac_fix_iframe_missing_title', true );
$this->fix->run();

$data = apply_filters( 'edac_filter_frontend_fixes_data', [] );
$this->assertArrayHasKey( 'iframe_missing_title', $data );
$this->assertTrue( $data['iframe_missing_title']['enabled'] );
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test method test_iframe_missing_title_fix_data_is_added appears to be redundant. The FixTestTrait that this test class uses already provides a test test_frontend_data_filter_includes_slug which covers the same functionality. You can rely on the trait's test and remove this duplicate method to keep the test suite DRY (Don't Repeat Yourself).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f0b505bf5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if ( src ) {
try {
const parsedUrl = new URL( src, window.location.href );
return `Embedded content from ${ parsedUrl.hostname }`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use generic fallback for hostless iframe URLs

Handle URLs like about:blank, data: or javascript: before building the hostname-based title. new URL(src, window.location.href) parses these successfully but returns an empty hostname, so this code currently sets title="Embedded content from ", which is an awkward, non-descriptive label for screen readers instead of the intended generic fallback.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/jest/frontendFixes/iframeMissingTitleFix.test.js (1)

21-46: Good test coverage for core scenarios.

Consider adding tests for these edge cases to strengthen coverage:

  • Iframe with empty string title (title="")
  • Iframe with whitespace-only title (title=" ")
  • Iframe with invalid/malformed src URL
  • Behavior when fix is disabled (enabled: false)
📝 Example additional test cases
+	test( 'adds fallback title when title attribute is empty string', () => {
+		document.body.innerHTML = '<iframe src="https://example.com" title=""></iframe>';
+
+		iframeMissingTitleFix();
+
+		const iframe = document.querySelector( 'iframe' );
+		expect( iframe.getAttribute( 'title' ) ).toBe( 'Embedded content from example.com' );
+	} );
+
+	test( 'adds fallback title when title attribute is whitespace only', () => {
+		document.body.innerHTML = '<iframe src="https://example.com" title="   "></iframe>';
+
+		iframeMissingTitleFix();
+
+		const iframe = document.querySelector( 'iframe' );
+		expect( iframe.getAttribute( 'title' ) ).toBe( 'Embedded content from example.com' );
+	} );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/jest/frontendFixes/iframeMissingTitleFix.test.js` around lines 21 - 46,
Add tests to cover iframeMissingTitleFix edge cases: (1) an iframe with title=""
should be treated as missing and receive the fallback (either hostname-based if
src valid, or generic if not); (2) an iframe with a whitespace-only title (e.g.,
"   ") should be treated as missing and receive the fallback; (3) an iframe with
a malformed/invalid src (e.g., "not-a-url" or "http://") should not crash and
should get the generic fallback title; and (4) when the fix is disabled via the
function’s options (enabled: false) iframeMissingTitleFix should not change
existing titles or set fallbacks—add assertions using querySelector to check
title attributes for each case referencing iframeMissingTitleFix and its
options.
tests/phpunit/includes/classes/Fixes/Fix/IframeMissingTitleFixTest.php (1)

24-38: Method naming follows WP conventions.

The set_up() (snake_case) is correct per WordPress unit test conventions, while tearDown() (camelCase) is the PHPUnit standard. This mixed convention is typical in WordPress test suites.

Consider adding a test to verify that frontend data is not added when the fix is disabled:

📝 Optional test for disabled state
+	/**
+	 * Test frontend data is not provided when the setting is disabled.
+	 *
+	 * `@return` void
+	 */
+	public function test_iframe_missing_title_fix_data_not_added_when_disabled() {
+		update_option( 'edac_fix_iframe_missing_title', false );
+		$this->fix->run();
+
+		$data = apply_filters( 'edac_filter_frontend_fixes_data', [] );
+		$this->assertArrayNotHasKey( 'iframe_missing_title', $data );
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/phpunit/includes/classes/Fixes/Fix/IframeMissingTitleFixTest.php`
around lines 24 - 38, The test class mixes WP and PHPUnit naming correctly
(set_up and tearDown); add a new test method named
test_frontend_data_not_added_when_disabled in IframeMissingTitleFixTest that
mirrors the existing frontend-data-positive test but disables the fix first (use
the fix instance IframeMissingTitleFix via $this->fix and call the fix's
disable/turn-off API or set its enabled flag), execute the same action that
would normally add frontend data, and assert that no frontend data was produced
(use the same helper/assertion used by other tests in this suite—e.g., the
helper from common_setup/common_teardown—to check the absence of frontend data).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/frontendFixes/Fixes/iframeMissingTitleFix.js`:
- Around line 5-18: The fallback strings in getFallbackIframeTitle are not
internationalized; update getFallbackIframeTitle to use wp.i18n functions (e.g.,
const { __, sprintf } = wp.i18n) and replace the raw strings with translatable
calls: use sprintf( __('Embedded content from %s', 'your-text-domain'),
parsedUrl.hostname ) when hostname is available, and __('Embedded content',
'your-text-domain') as the generic fallback; ensure you import/destructure __
and sprintf from wp.i18n at the top of the file and replace the two literal
strings accordingly.

---

Nitpick comments:
In `@tests/jest/frontendFixes/iframeMissingTitleFix.test.js`:
- Around line 21-46: Add tests to cover iframeMissingTitleFix edge cases: (1) an
iframe with title="" should be treated as missing and receive the fallback
(either hostname-based if src valid, or generic if not); (2) an iframe with a
whitespace-only title (e.g., "   ") should be treated as missing and receive the
fallback; (3) an iframe with a malformed/invalid src (e.g., "not-a-url" or
"http://") should not crash and should get the generic fallback title; and (4)
when the fix is disabled via the function’s options (enabled: false)
iframeMissingTitleFix should not change existing titles or set fallbacks—add
assertions using querySelector to check title attributes for each case
referencing iframeMissingTitleFix and its options.

In `@tests/phpunit/includes/classes/Fixes/Fix/IframeMissingTitleFixTest.php`:
- Around line 24-38: The test class mixes WP and PHPUnit naming correctly
(set_up and tearDown); add a new test method named
test_frontend_data_not_added_when_disabled in IframeMissingTitleFixTest that
mirrors the existing frontend-data-positive test but disables the fix first (use
the fix instance IframeMissingTitleFix via $this->fix and call the fix's
disable/turn-off API or set its enabled flag), execute the same action that
would normally add frontend data, and assert that no frontend data was produced
(use the same helper/assertion used by other tests in this suite—e.g., the
helper from common_setup/common_teardown—to check the absence of frontend data).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5dae50fd-f5d8-4819-a155-7f2533e9cd39

📥 Commits

Reviewing files that changed from the base of the PR and between 7b6ef1f and 1f0b505.

📒 Files selected for processing (7)
  • includes/classes/Fixes/Fix/IframeMissingTitleFix.php
  • includes/classes/Fixes/FixesManager.php
  • includes/classes/Rules/Rule/IframeMissingTitleRule.php
  • src/frontendFixes/Fixes/iframeMissingTitleFix.js
  • src/frontendFixes/index.js
  • tests/jest/frontendFixes/iframeMissingTitleFix.test.js
  • tests/phpunit/includes/classes/Fixes/Fix/IframeMissingTitleFixTest.php

Comment on lines +5 to +18
const getFallbackIframeTitle = ( iframe ) => {
const src = iframe.getAttribute( 'src' )?.trim();

if ( src ) {
try {
const parsedUrl = new URL( src, window.location.href );
return `Embedded content from ${ parsedUrl.hostname }`;
} catch ( e ) {
// Continue to generic fallback if the URL can't be parsed.
}
}

return 'Embedded content';
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

User-facing strings should use WordPress i18n functions.

The fallback title strings "Embedded content from" and "Embedded content" are user-facing text that should be translatable for international users. As per coding guidelines, use wp.i18n functions for translation support.

🌐 Proposed fix for i18n support
+import { __, sprintf } from '@wordpress/i18n';
+
 const iframeMissingTitle = window.edac_frontend_fixes?.iframe_missing_title || {
 	enabled: false,
 };

 const getFallbackIframeTitle = ( iframe ) => {
 	const src = iframe.getAttribute( 'src' )?.trim();

 	if ( src ) {
 		try {
 			const parsedUrl = new URL( src, window.location.href );
-			return `Embedded content from ${ parsedUrl.hostname }`;
+			return sprintf(
+				/* translators: %s: hostname of the embedded content */
+				__( 'Embedded content from %s', 'accessibility-checker' ),
+				parsedUrl.hostname
+			);
 		} catch ( e ) {
 			// Continue to generic fallback if the URL can't be parsed.
 		}
 	}

-	return 'Embedded content';
+	return __( 'Embedded content', 'accessibility-checker' );
 };

As per coding guidelines: "Use WordPress i18n functions (wp.i18n) for translation support in JavaScript files".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/frontendFixes/Fixes/iframeMissingTitleFix.js` around lines 5 - 18, The
fallback strings in getFallbackIframeTitle are not internationalized; update
getFallbackIframeTitle to use wp.i18n functions (e.g., const { __, sprintf } =
wp.i18n) and replace the raw strings with translatable calls: use sprintf(
__('Embedded content from %s', 'your-text-domain'), parsedUrl.hostname ) when
hostname is available, and __('Embedded content', 'your-text-domain') as the
generic fallback; ensure you import/destructure __ and sprintf from wp.i18n at
the top of the file and replace the two literal strings accordingly.

@SteveJonesDev
SteveJonesDev marked this pull request as draft June 1, 2026 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex Applied by automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant