Codeception / module-webdriver

WebDriver module for Codeception
MIT License
36 stars 25 forks source link

Shadow dom #101

Open tejas-elsner opened 2 years ago

tejas-elsner commented 2 years ago

What are you trying to achieve?

Get the Shadow Dom

What do you get instead?

Not able to get the result from Shadow root

Provide console output if related. Use -vvv mode for more details.

# paste output here

Provide test source code if related

// paste test

Details

# paste suite config here
Naktibalda commented 2 years ago

I guess that you want it in WebDriver module.

You are welcome to implement this feature and raise pull request.

DanielSiepmann commented 9 months ago

The underlying library added support: https://github.com/php-webdriver/php-webdriver/pull/1010 So someone with the necessary knowledge should be able to add this to the module. I need this right now and will play around with this. I'll share my insights if they are helpful.

It looks like Firefox added ShadowDom support as well, but still lacks support for some locators, see: https://github.com/w3c/webdriver/issues/1610 so adding support within the module might end in requests why things aren't working which is an underlying issue.

DanielSiepmann commented 9 months ago

I came up with the following:

diff --git a/src/Codeception/Module/WebDriver.php b/src/Codeception/Module/WebDriver.php
index 2c5b578..51492f1 100644
--- a/src/Codeception/Module/WebDriver.php
+++ b/src/Codeception/Module/WebDriver.php
@@ -1176,9 +1176,9 @@ class WebDriver extends CodeceptionModule implements

     public function click($link, $context = null): void
     {
-        $page = $this->webDriver;
+        $page = $this->getBaseElement();
         if ($context) {
-            $page = $this->matchFirstOrFail($this->webDriver, $context);
+            $page = $this->matchFirstOrFail($page, $context);
         }

         $el = $this->_findClickable($page, $link);
@@ -3661,6 +3661,32 @@ class WebDriver extends CodeceptionModule implements
         $this->setBaseElement();
     }

+    public function performOnShadowRoot($element, $actions, int $timeout = 10): void
+    {
+        $this->waitForElement($element, $timeout);
+        $this->setBaseElement($element);
+        $this->debugSection('InnerText', $this->getBaseElement()->getText());
+
+        $this->baseElement = $this->getBaseElement()->getShadowRoot();
+
+        if (is_callable($actions)) {
+            $actions($this);
+            $this->setBaseElement();
+            return;
+        }
+
+        if (is_array($actions)) {
+            $actions = ActionSequence::build()->fromArray($actions);
+        }
+
+        if (!$actions instanceof ActionSequence) {
+            throw new InvalidArgumentException("2nd parameter, actions should be callback, ActionSequence or array");
+        }
+
+        $actions->run($this);
+        $this->setBaseElement();
+    }
+
     /**
      * @param string|array|WebDriverBy $element
      */

That way it could be used like this:

        $this->tester->performOnShadowRoot(
            'typo3-backend-new-content-element-wizard',
            ActionSequence::build()
                ->click("//span[normalize-space(.)='$tabName']")
                ->click("//span[normalize-space(.)='$contentElementLabel']")
        );

Following the existing performOn() approach.

I had to fix the click() method that does not seem to respect the base Element in current version. I also noticed that the performOn won't work with additions like step decorators, e.g. Codeception\Step\Retry. Also ShadowDom via WebDriver in Browsers currently doesn't seem to support various locators, e..g XPath. So our above example won't work.

Due to all those issues I didn't open a PR. I don't have the feeling general shadow dom support is there yet … But feel free to use the patch and provide a PR or patch your own code.