Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java: All searching interfaces are made generic #863

Closed
wants to merge 2 commits into from

Conversation

TikhomirovSergey
Copy link
Contributor

@TikhomirovSergey TikhomirovSergey commented Jul 31, 2015

The problem:

There are many Selenium-based projects such as Appium (java_client),
Selendroid, IOSDriver and so on. They are used not only
for the web/browser testing and they have their own WebElement
implementations. Also there is probably need to re-implement all changed
interfaces for some end user purposes.

Current design doesn't allow user to use their own WebElement subclasses
without additional casting. It is annoying and makes code dirty. Any
trying to avoid this causes problems.

This change is backward compatible with existing projects and
will allow to create factories or user's customized Webdrivers and
WebElements that return desired WebElement subclasses.


This change is Reviewable

@lukeis
Copy link
Member

lukeis commented Jul 31, 2015

have you and @alexec signed the CLA?

would you mind squashing the commits? (if we have 'two' authors here, I don't mind 2 commits).

Otherwise I'm generally 👍 but can wait for others ( @jleyba ? ) to comment too.

@TikhomirovSergey
Copy link
Contributor Author

@lukeis I'm already signed. @alexec is probably too. I've used changes that has been proposed by @alexec here: #604. I can try to squach commits.

@TikhomirovSergey
Copy link
Contributor Author

Ok guys, thanks for remarks
I'll make improvements as soon as possible (during next 2-3 days)

@lukeis
Copy link
Member

lukeis commented Jul 31, 2015

can't assume @alexec has, since he never mentioned it in the other PR and hasn't had a commit merged in the selenium tree.

@TikhomirovSergey
Copy link
Contributor Author

@lukeis Can I duplicate @alexec's changes with mine, close this PR and open new? Or
Can you advice me how to resolve the problem with unsigned user another way?

@lukeis
Copy link
Member

lukeis commented Jul 31, 2015

duplicating is the same as "copying" and since alexec's employer could potentially claim copyright on that code if we 'copied' since he hasn't signed the CLA.... then no, you can't duplicate it. And I would then suggest you remove his commits from your tree.

That being said, it would then be nice to have an additional test ;)

@TikhomirovSergey
Copy link
Contributor Author

Ok. So I'll make improvements that are required soon...

@alexec
Copy link

alexec commented Aug 1, 2015

Hi everyone, I'm not sure if I've signed the CLA. If you really need to me too, then just let me know.

@TikhomirovSergey
Copy link
Contributor Author

@alexec Yeah, we really need you. Btw. Can you squash your commits?

@alexec
Copy link

alexec commented Aug 2, 2015

CLA signed. I'm afraid I don't know how to squash commits.

@TikhomirovSergey
Copy link
Contributor Author

@alexec Please try something like that:

git reset --soft head~8
git commit -am "SearchContext interface is made generic"
git push -f

@alexec
Copy link

alexec commented Aug 2, 2015

I got weird merge issue. I don't know how to do this and it'll take me ages to figure out. Do you think you could do it please?

@TikhomirovSergey
Copy link
Contributor Author

ok. I'll try this later...

@TikhomirovSergey
Copy link
Contributor Author

@alexec I've improved this PR. I squashed all commits into one. I made you an author of all changes. Is it ok? If it is ok please close #604.

Later I'll work with remarks. I remember all remarks by @jleyba

@TikhomirovSergey
Copy link
Contributor Author

Oh my god! There were problems with the merging... I'll add soon all that was required

@TikhomirovSergey
Copy link
Contributor Author

There were problems with the merging... So the new commit was delayed

What I've done:

  • I made all searcing interfaces generic. Now they contain methods like following
<T extends WebElement> List<T> findElements
<T extends WebElement> T findElement

The same way were enhanced WebElement and WebDriver. Their common implementations work as usual. There shouldn't be backward compatibility issues.

  • @alexec 's test was added to suite.

Actually I don't know what test I can make up. All this project is like a test stand for this change.
@lukeis @jleyba
Please look at at these changes one more time. I'm waiting for your remarks. If everything is ok but a new test should be added or something has to be improved I'll do that soon.

@jleyba
Copy link
Contributor

jleyba commented Aug 17, 2015

No further comments on the actual code. At the design level, I'm not sure how I feel about the lack of type safety this introduces:

WebDriver driver = createDriver();
SomeCustomElement el = driver.findElement(By.id("foo"));

This is now perfectly valid (and I guess the point), but the WebDriver interface provides no clues that it can actually produce a SomeCustomElement. We're making it easy for users to silently introduce ClassCastExceptions.

@TikhomirovSergey
Copy link
Contributor Author

@jleyba Ok. Does it makes sense to declare the following:

<T extends WebElement> List<T> findElements(By by) throws ClassCastException;
<T extends WebElement> T findElement(By by) throws ClassCastException;

?

Actially this enhancement is supposed to be used when there is need to implement user's customized WebDriver/WebElement.

I think that it makes sense to point these special things out at documentation if this is supposed to be merged...

alexec and others added 2 commits September 13, 2015 19:11
The problem:

There are many Selenium-based projects such as Appium (java_client),
Selendroid, IOSDriver and so on. They are used not only
for the web/browser testing and they have their own WebElement
implementations. Also there is probably need to re-implement all changed
interfaces for some end user purposes.

Current design doesn't allow user to use their own WebElement subclasses
without additional casting. It is annoying and makes code dirty. Any
trying to avoid this causes problems.

This change is backward compatible with existing projects and
will allow to create factories or user's customized Webdrivers and
WebElements that return desired WebElement subclasses.
@TikhomirovSergey
Copy link
Contributor Author

@ddavison
I improved the space :)
One more conflict was resolved

* @return A list of all {@link WebElement}s, or an empty list if nothing matches
*
* @throws ClassCastException The WebDriver interface is generic.
Copy link

Choose a reason for hiding this comment

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

Are these comments about ClassCastException unnecessary? ClassCastException is unchecked.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alexec this is just information. If end users will re-implement the proposed API then there won't be any problem. An end user will face a problem when they use common Selenium implementations, because it not so flexible like API.

@cyril265
Copy link

cyril265 commented Oct 9, 2015

Why is there a need to make the interfaces generic?
You can write:

WebDriverExt driver = createDriver(); //your own webdriver implementation
WebDriverElementExt el = driver.findElement(By.id("foo")); // your webdriver implementation can declare 'WebDriverElementExt' as the return class

My only issue is with the List<WebElement> findElements(By by); method, because it doesn't allow returning extending classes of WebElement. This could be fixed by using a wildcard (as you did): List<? extends WebElement> findElements(By by);

The following will break without compile error or warning if you change the implementation of the webdriver:

WebDriver driver = createDriver();
SomeCustomElement el = driver.findElement(By.id("foo"));

Or am I missing something?

@TikhomirovSergey
Copy link
Contributor Author

@squallified

There are some problems:

  • The first and the most annoying is the problem is the necessity to cast to the target type each time. There are many cases when you are forced to use only API.

Current design doesn't allow user to use their own WebElement subclasses
without additional casting. It is annoying and makes code dirty.

1
fix
1

and

1
fix
1

  • Yep. Problems with returned lists when you are trying to resolve the problem above on your own:

1

1

There shouldn't be any compilation problem if API will be changed the proposed way. As you see all tests have passed.

There are possible "warnings" when you are trying to re-implement the proposed API forcing finders (WebDriver/WebElement) to return your own WebElement subclass.

yes, but they will probably get the dreaded "Unchecked overriding: return type requires unchecked conversion (required 'T') 😄
a small price to pay though..

This is just an attempt to make API a bit more convenient and flexible. 😄

@cyril265
Copy link

You can fix the first example by changing the return type of the findElement method in your subclass:

public class CustomWebElement implements WebElement {
    @Override
    public CustomWebElement findElement(By by) {
        return ...;
    }
...

This doesn't work for the findElements method though. For this to work the method signature in the interface needs to be changed to:
List<? extends WebElement> findElements(By by). Then you can change the return type in your subclass to List<CustomWebElement>.
This forces you to use the concrete type of WebDriver if you want to use the concrete implementation of WebElement:

        CustomWebDriver customDriver = new CustomWebDriver();
        CustomWebElement customElement = customDriver.findElement(By.id("test")); // works

        WebDriver driver = new CustomWebDriver();
        CustomWebElement element = driver.findElement(By.id("test")); // not allowed

I agree that findElements needs to be changed (either generic interface or a wildcard). I guess it's just a preference thing.

@TikhomirovSergey
Copy link
Contributor Author

@squallified

So the proposed PR allows end user to use API and it don't force user to use only certain implementations if their project contains a customized WebElement implementation which their own WebDrivers return:

WebDriver d;

CustomWebElement = d.findElement(By.id("test")) //works with these changes
List<CustomWebElement> = d.findElements(By.id("test")) //works with these changes

So it is one more advantage of proposed changes.

@coding-yogi
Copy link

Following

@bootstraponline
Copy link
Contributor

👍

@jleyba
Copy link
Contributor

jleyba commented Nov 12, 2015

I prefer @squallified 's suggestion of changing findElements to List<? extends WebElement> findElements(By by). This would allow individual implementations to return a narrower type without weakening the contract on the core WebDriver interface.

@TikhomirovSergey
Copy link
Contributor Author

@jleyba
Ok. I'll try this variant soon. If it is successive and doesn't complicate user's work than I'll update my changes :)

@TikhomirovSergey
Copy link
Contributor Author

@jleyba
I've tried the solution suggested by @squallified and I'm against it.
Why?

The simple sample:

  • there is a class with wildcard

  • there is a subclass

As you can see the first class was extended without problems.

Ok. Lets go ahead.

but

The same is true for our case. I was able to build API artefact. There were lots of compilation errors. And now we can see:

Summary:
Changes suggested by @squallified will force users to use wildcards like above. Though there is no problem with class extension. Anyway this change will send users and their projects to hell because there are many users and projects which use API (not only implementors)

So I'll keep this PR as is.

Advantages:

  • flexible API
  • it is easy to reimplement API in the way required by user.

Disadvantages:

  • casting problems if default implementations are in use.

    But it this case I think that most of users have no need to force default web drivers to fetch customized elements. This restriction could be described in project documentation and javadocs (It is already has been done at this PR).

Related threads where the mentioned wildcard problem was discussed:
#604
#934

@TikhomirovSergey
Copy link
Contributor Author

It is possible that proposed code will be changed the way below:

public interface SearchContext<T extends WebElement> {
    List<T> findElements(By by);
    T findElement(By by);
}

public interface Webdriver<T extends WebElement> extends SearchContext<T> {
...
    List<T> findElements(By by);
    T findElement(By by);
...
}

public class RemoteWebDriver implements Webdriver<WebElement> {
...
}

How do you feel about that change? It alows to avoid API contract problems but users will face rawtype warning at the first time. I think it is very major change...

@vsadineni1
Copy link

Hello there,
Any reason why this pull request is not getting merged yet? It is almost 2 years!!
Are there any outstanding TODOs? Otherwise please merge this.
Because currently I am running into some duplicate class issues when we try to use appium java-client & selenium jars together because there are some common classes between these 2 jars with the exact same package name & class name. (https://github.com/appium/java-client/tree/master/src/main/java/org/openqa/selenium)

Relevant discussion on the appium project - appium/java-client#1021

@lukeis
Copy link
Member

lukeis commented Sep 27, 2018

Venkata, your tone of voice in your comment is entitled. Please read what you write before you post on open source projects.

Your issue, shouldn't be an issue at all for the selenium project and you'll need to follow up with the appium developers. Please read my comment on this other issue raised and referenced in this PR:
#3211 (comment)

As it stands, I think this PR might never get merged (it currently has merge conflicts too). I've also come to dislike the style of generics used here as it can cause many unintended side effects and makes for refactoring code a very difficult chore to unwind.

@vsadineni1
Copy link

Luke,
Thanks for your response. I do not mean to sound rude. Sorry about that.

Yes, I checked with appium devs. They sounded like the PR is pending with selenium that is why we have to duplicate the classes in appium.

@CLAassistant
Copy link

CLAassistant commented Nov 23, 2019

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 2 committers have signed the CLA.

❌ TikhomirovSergey
❌ alexec
You have signed the CLA already but the status is still pending? Let us recheck it.

@diemol
Copy link
Member

diemol commented May 19, 2020

Apologies for the poor handling of this PR by our side, nevertheless it looks like the approach was never fully accepted. On the other hand, I can see that the purpose of this PR was achieved in the end through the implementation of generics in the Java bindings for Appium, in this PR appium/java-client#413.
Therefore, I'll close this one.

@diemol diemol closed this May 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet