Skip to content

Gh2172 Add ShadowRoot() as a chainable Selector method #5560

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

Merged
merged 11 commits into from
Nov 2, 2020

Conversation

mostlyfabulous
Copy link
Contributor

@mostlyfabulous mostlyfabulous commented Sep 20, 2020

Purpose

Extend the Selector API to have a shadowRoot function that returns the elements inside the shadowRoot when the shadow DOM is open. These elements are only otherwise accessible with the shadowRoot property when using statements like document.querySelector('css-selector').shadowRoot.querySelectorAll('css-selector'); in a Selector, but this creates Selector chaining problems if there are child/nested elements with a shadowRoot on them as well.

e.g.

Selector('elem-with-shadowRoot').child('p').shadowRoot.withText('text'); will not work.

This PR should let developers chain existing Selector statements that target elements in the shadowRoot.

Approach

Add a new API shadowRoot that can be applied to a Selector which returns the contents of an element's shadowRoot if it exists and null otherwise.

References

#2172

https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM

Pre-Merge TODO

  • Write tests for your proposed changes
  • Add API tests
  • Add TS definition
  • Make sure that existing tests do not fail

Sorry, something went wrong.

@need-response-app need-response-app bot added the STATE: Need response An issue that requires a response or attention from the team. label Sep 20, 2020
@testcafe-build-bot
Copy link
Collaborator

@AlexKamaev
Copy link
Contributor

@mostlyfabulous
Thank you for your contribution.
As I understand at this moment the PR is not ready yet. I tried the following test and it does not work as expected:

import { Selector } from 'testcafe';

fixture `Selector`
    .page `../pages/index.html`;

test('shadowRoot selector API tests', async t => {
    await t.click(Selector('#hasShadowDOM').shadowRoot().child('p').withText('Text should be found!'));
});

I debugged your code and found a couple of issues to fix.
I added some modifications to your code to make the test work. Please take a look at them: 0e9e4cf

We would appreciate it if you review the recommendations from the following comment: #2172 (comment)

@need-response-app need-response-app bot removed the STATE: Need response An issue that requires a response or attention from the team. label Sep 22, 2020
@testcafe-build-bot
Copy link
Collaborator

@testcafe-build-bot
Copy link
Collaborator

✅ Tests for the commit 7272508 have passed. See details:

@testcafe-build-bot
Copy link
Collaborator

@mostlyfabulous
Copy link
Contributor Author

IE11 does not support attachShadow and must be polyfilled. Would prefer to skip IE11 tests for shadowRoot API method:

image

image

Unrelated PR build errors:

image

image

@need-response-app need-response-app bot added the STATE: Need response An issue that requires a response or attention from the team. label Sep 27, 2020
@testcafe-build-bot
Copy link
Collaborator

@AlexKamaev
Copy link
Contributor

AlexKamaev commented Sep 28, 2020

@mostlyfabulous
I agree that we need just to skip tests in IE11, since it does not support the Shadow API. You can skip the test using the skip property instead of using the only property as described here:

return runTests('testcafe-fixtures/index-test.js', 'choose value', { skip: 'ie,edge' });

Then, remove all IE related code in your test as follows:

const isIEFunction = ClientFunction(() => {

In addition, there is no need to create/copy tests for all selector properties. A test with one property is enough.

@need-response-app need-response-app bot removed the STATE: Need response An issue that requires a response or attention from the team. label Sep 28, 2020
@mostlyfabulous
Copy link
Contributor Author

mostlyfabulous commented Sep 29, 2020

Then, remove all IE related code in your test as follows:

Do you mean to say, wrap any code specific to IE in the function you provided?

@need-response-app need-response-app bot added the STATE: Need response An issue that requires a response or attention from the team. label Sep 29, 2020
@testcafe-build-bot
Copy link
Collaborator

@testcafe-build-bot
Copy link
Collaborator

@AlexKamaev
Copy link
Contributor

Then, remove all IE related code in your test as follows:

Do you mean to say, wrap any code specific to IE in the function you provided?

No. I saw this function in your code. Probably you accidentally copied it from another test file. I mean, that we do not need any additional code for IE, since IE does not support shadow DOM.
The only thing that we need to do with IE for Shadow DOM tests - is only skipping IE in our test suites. Thus, this isIEFunction should be deleted.

@need-response-app need-response-app bot added STATE: Need response An issue that requires a response or attention from the team. and removed STATE: Need response An issue that requires a response or attention from the team. labels Sep 29, 2020
@mostlyfabulous
Copy link
Contributor Author

@testcafe-build-bot restart

@AlexKamaev
Copy link
Contributor

@mostlyfabulous
I see the failed tests. This happens because the /fixtures/api/es-next/selector/pages/index.html page is used for Chrome, Firefox, and IE. So, I don't recommend adding your changes to the /fixtures/api/es-next/selector/pages/index.html page. Please create a separate page for your test.

Please also rebase your commit to the DevExpress/master branch since some npm packages need to be refreshed.

@need-response-app need-response-app bot removed the STATE: Need response An issue that requires a response or attention from the team. label Oct 5, 2020
@AlexKamaev
Copy link
Contributor

@mostlyfabulous
Hello. The PR is almost ready. Do you need any assistance to complete it? If you have any difficulties, feel free to ask questions here.

@mostlyfabulous
Copy link
Contributor Author

I should be able to get round to the changes you suggested this week but feel free to finish the outstanding changes so this can be released!

@need-response-app need-response-app bot added the STATE: Need response An issue that requires a response or attention from the team. label Oct 19, 2020
@AlexKamaev
Copy link
Contributor

Ok,
If I complete my current tasks before you're able to continue working this PR, I'll finish it.

@need-response-app need-response-app bot removed the STATE: Need response An issue that requires a response or attention from the team. label Oct 20, 2020
@testcafe-build-bot
Copy link
Collaborator

@AlexKamaev
Copy link
Contributor

@testcafe-build-bot retest

@testcafe-build-bot
Copy link
Collaborator

@mostlyfabulous mostlyfabulous marked this pull request as ready for review October 27, 2020 18:20
Copy link
Contributor

@AndreyBelym AndreyBelym left a comment

Choose a reason for hiding this comment

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

An excellent PR, thank you. Just some moments to discuss :)

// NOTE: we can search for elements only in document or element.
if (querySelectorRoot.nodeType !== 1 && querySelectorRoot.nodeType !== 9)
// NOTE: we can search for elements only in document/element/shadow root.
if (querySelectorRoot.nodeType !== 1 && querySelectorRoot.nodeType !== 9 && querySelectorRoot.toString() !== '[object ShadowRoot]')
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess we can check here for nodeType === 11 (document fragments' node type) since shadow roots have this node type and document fragments have the querySelectorAll methods:
https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment

Copy link
Contributor

Choose a reason for hiding this comment

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

That was my initial approach, but then I saw the hammerhead implementation =)

@@ -762,6 +762,28 @@ function addHierarchicalSelectors (options) {

return createDerivativeSelectorWithFilter(args);
};

// ShadowRoot
obj.shadowRoot = (filter, dependencies) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

filter can be used when a method can produce multiple results from a single element (e.g. the parent method that matches all parent nodes from the current element up to the document element). dependencies can be used when filter is a function or a ClientFunction to pass some context variables. Since an element can have only a single shadowRoot, I think we do not need filter and dependencies here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense to me now

@@ -63,6 +64,9 @@ export default class SelectorExecutor extends ClientFunctionExecutor {
const isElementVisible = !this.command.visibilityCheck || visible(el);
const isTimeout = new DateCtor() - startTime >= this.timeout;

if (domUtils.isShadowRoot(el))
throw new ActionElementIsShadowRootElementError();
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't feel that here is the right place for this check. By doing it here, we prevent using expressions like t.expect(element.shadowRoot()).contains('text') that are perfectly valid in my opinion. I think it's better to do it here:
https://github.com/DevExpress/testcafe/blob/master/src/client/driver/command-executors/execute-action.js#L157

@testcafe-build-bot
Copy link
Collaborator

@AlexKamaev
Copy link
Contributor

@testcafe-build-bot retest

1 similar comment
@AlexKamaev
Copy link
Contributor

@testcafe-build-bot retest

@testcafe-build-bot
Copy link
Collaborator

@AlexKamaev
Copy link
Contributor

@AndreyBelym FPR

@AndreyBelym AndreyBelym merged commit 7a38865 into DevExpress:master Nov 2, 2020
@mostlyfabulous mostlyfabulous changed the title Gh2172 Gh2172 Add ShadowRoot() as a chainable Selector method Nov 5, 2020
AlexSkorkin added a commit that referenced this pull request Dec 9, 2020
[AlexanderMoiseev](https://github.com/AlexanderMoiseev)
[augustomezencio-hotmart](https://github.com/augustomezencio-hotmart) - [Fix tree-kill vulnerability from old version.](#5690)
[bdwain](https://github.com/bdwain) - [Fixes header types for request and response mocks (#5529)](#5530)
[mostlyfabulous](https://github.com/mostlyfabulous) - [Gh2172 Add `ShadowRoot()` as a chainable Selector method](#5560)
[danielroe](https://github.com/danielroe) - [Use polyfill if `location.origin` doesn't exist (in IE11)](DevExpress/testcafe-hammerhead#2441)
testcafe-build-bot added a commit to testcafe-build-bot/testcafe that referenced this pull request Dec 9, 2020
[AlexanderMoiseev](https://github.com/AlexanderMoiseev)
[augustomezencio-hotmart](https://github.com/augustomezencio-hotmart) - [Fix tree-kill vulnerability from old version.](DevExpress#5690)
[bdwain](https://github.com/bdwain) - [Fixes header types for request and response mocks (DevExpress#5529)](DevExpress#5530)
[mostlyfabulous](https://github.com/mostlyfabulous) - [Gh2172 Add `ShadowRoot()` as a chainable Selector method](DevExpress#5560)
[danielroe](https://github.com/danielroe) - [Use polyfill if `location.origin` doesn't exist (in IE11)](DevExpress/testcafe-hammerhead#2441)
testcafe-build-bot added a commit to testcafe-build-bot/testcafe that referenced this pull request Dec 9, 2020
[AlexanderMoiseev](https://github.com/AlexanderMoiseev)
[augustomezencio-hotmart](https://github.com/augustomezencio-hotmart) - [Fix tree-kill vulnerability from old version.](DevExpress#5690)
[bdwain](https://github.com/bdwain) - [Fixes header types for request and response mocks (DevExpress#5529)](DevExpress#5530)
[mostlyfabulous](https://github.com/mostlyfabulous) - [Gh2172 Add `ShadowRoot()` as a chainable Selector method](DevExpress#5560)
[danielroe](https://github.com/danielroe) - [Use polyfill if `location.origin` doesn't exist (in IE11)](DevExpress/testcafe-hammerhead#2441)
AndreyBelym pushed a commit that referenced this pull request Dec 11, 2020
[AlexanderMoiseev](https://github.com/AlexanderMoiseev)
[augustomezencio-hotmart](https://github.com/augustomezencio-hotmart) - [Fix tree-kill vulnerability from old version.](#5690)
[bdwain](https://github.com/bdwain) - [Fixes header types for request and response mocks (#5529)](#5530)
[mostlyfabulous](https://github.com/mostlyfabulous) - [Gh2172 Add `ShadowRoot()` as a chainable Selector method](#5560)
[danielroe](https://github.com/danielroe) - [Use polyfill if `location.origin` doesn't exist (in IE11)](DevExpress/testcafe-hammerhead#2441)
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.

None yet

4 participants