Skip to content

feat: allow @OverrideWidget fields declared as interfaces - #2486

Merged
valfirst merged 4 commits into
appium:masterfrom
Ganesh2325:cursor/overridden-widget-interfaces-407e
Nov 11, 2026
Merged

valfirst merged 4 commits into
appium:masterfrom
Ganesh2325:cursor/overridden-widget-interfaces-407e

Conversation

@Ganesh2325

@Ganesh2325 Ganesh2325 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Change list
Fixes #1926. An @OverrideWidget field, or a List of that field, may be declared as an interface implemented by the platform widget classes. Calls are proxied to the concrete Widget for the current content type. Fields declared as Widget classes, including abstract classes, behave as they do today.

The override class must implement the interface and extend Widget.
A content type with no override stays unresolved until that content is used, then throws InstantiationException.
Locators are still taken from the selected widget class, including class-level @AndroidFindBy and @iOSXCUITFindBy.
An interface that extends WebElement is left to the normal element decorator.

Types of changes
New feature (non-breaking change which adds functionality)

Details

public interface UsersWidget {
void doWork();
}
public class IosUsersWidget extends Widget implements UsersWidget {
protected IosUsersWidget(WebElement element) {
super(element);
}
@OverRide
public void doWork() {
// iOS behavior
}
}
@findby(id = "users")
@OverrideWidget(iOSXCUITAutomation = IosUsersWidget.class,
androidUIAutomator = AndroidUsersWidget.class,
html = HtmlUsersWidget.class)
UsersWidget widget;
Name a concrete class for every content type the field is used with. A partial override, such as only iOSXCUITAutomation, works on that content and throws InstantiationException on another.

./gradlew test checkstyleMain checkstyleTest javadoc

Local validation on a732abba: ./gradlew test checkstyleMain checkstyleTest javadoc passed. All 240 java-client tests and 12 selenium-bridge tests passed with no failures. Checkstyle and Javadoc also passed. These are local results, not upstream CI results.

The interface must be public. The library’s class loader must be able to load the interface, or the interface’s loader must be able to load the library. Unsupported loader setups and non-public interfaces throw IllegalArgumentException.

Rebased on #2480; widget interceptors created for interface fields use the configured default LocatorGroupStrategy.

@mykola-mokhnach

mykola-mokhnach commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

@Ganesh2325 please resolve conflicts in the PR

An @OverrideWidget field may be an interface implemented by the platform
widget classes. Calls are proxied to the concrete widget for the current
content type.
@cursor
cursor Bot force-pushed the cursor/overridden-widget-interfaces-407e branch from 30688b0 to 336d165 Compare November 3, 2026 06:31
@Ganesh2325

Copy link
Copy Markdown
Contributor Author

@mykola-mokhnach Thanks, conflicts are resolved. I rebased onto the latest master. The conflicts were with #2480 (default LocatorGroupStrategy), and both changes are kept: interface-typed @OverrideWidget fields and list items now also receive the decorator's defaultLocatorGroupStrategy. ./gradlew test checkstyleMain checkstyleTest javadoc passes locally (230 java-client + 12 selenium-bridge tests). Could you please approve the workflows when you get a chance?

@mykola-mokhnach mykola-mokhnach 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.

  • Test coverage: OverriddenWidgetInterfaceTest doesn't exercise the paths where interface proxies differ from class proxies. These are list items after the element reference is cleared (see the inline comment on WidgetListInterceptor), toString/equals/hashCode on an interface proxy, a widget with nested @FindBy fields, a non-public interface, and an interface with default methods. Please add tests for the ones you intend to support.

Hold a list item's element until its widget exists, generate interface
proxies in Helpers, and reject non-public interfaces or disconnected
class loaders with IllegalArgumentException.
Document that a public interface is accepted when either class loader can
see the other side, and that other cases throw IllegalArgumentException.
@Ganesh2325

Copy link
Copy Markdown
Contributor Author

Thanks! Added tests for cleared weak references, toString/equals/hashCode, nested @FindBy fields, and default methods—including one the widget does not override. Non-public interfaces are tested as unsupported and fail clearly. These tests pass on a732abba.

@valfirst

valfirst commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

@Ganesh2325 could you please resolve the conflicts?

@Ganesh2325

Copy link
Copy Markdown
Contributor Author

Thanks! I merged the latest master and resolved the conflicts in 8a414c5. All tests pass locally.

@valfirst
valfirst merged commit 4fe915f into appium:master Nov 11, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Overridden widget interfaces

3 participants