454 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| // Copyright 2004-present Facebook. All Rights Reserved.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| namespace Facebook\WebDriver\Remote;
 | |
| 
 | |
| use Facebook\WebDriver\Exception\WebDriverException;
 | |
| use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
 | |
| use Facebook\WebDriver\Internal\WebDriverLocatable;
 | |
| use Facebook\WebDriver\WebDriverBy;
 | |
| use Facebook\WebDriver\WebDriverDimension;
 | |
| use Facebook\WebDriver\WebDriverElement;
 | |
| use Facebook\WebDriver\WebDriverKeys;
 | |
| use Facebook\WebDriver\WebDriverPoint;
 | |
| use ZipArchive;
 | |
| 
 | |
| /**
 | |
|  * Represents an HTML element.
 | |
|  */
 | |
| class RemoteWebElement implements WebDriverElement, WebDriverLocatable
 | |
| {
 | |
|     /**
 | |
|      * @var RemoteExecuteMethod
 | |
|      */
 | |
|     protected $executor;
 | |
|     /**
 | |
|      * @var string
 | |
|      */
 | |
|     protected $id;
 | |
|     /**
 | |
|      * @var UselessFileDetector
 | |
|      */
 | |
|     protected $fileDetector;
 | |
| 
 | |
|     /**
 | |
|      * @param RemoteExecuteMethod $executor
 | |
|      * @param string $id
 | |
|      */
 | |
|     public function __construct(RemoteExecuteMethod $executor, $id)
 | |
|     {
 | |
|         $this->executor = $executor;
 | |
|         $this->id = $id;
 | |
|         $this->fileDetector = new UselessFileDetector();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * If this element is a TEXTAREA or text INPUT element, this will clear the value.
 | |
|      *
 | |
|      * @return RemoteWebElement The current instance.
 | |
|      */
 | |
|     public function clear()
 | |
|     {
 | |
|         $this->executor->execute(
 | |
|             DriverCommand::CLEAR_ELEMENT,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Click this element.
 | |
|      *
 | |
|      * @return RemoteWebElement The current instance.
 | |
|      */
 | |
|     public function click()
 | |
|     {
 | |
|         $this->executor->execute(
 | |
|             DriverCommand::CLICK_ELEMENT,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Find the first WebDriverElement within this element using the given mechanism.
 | |
|      *
 | |
|      * @param WebDriverBy $by
 | |
|      * @return RemoteWebElement NoSuchElementException is thrown in
 | |
|      *    HttpCommandExecutor if no element is found.
 | |
|      * @see WebDriverBy
 | |
|      */
 | |
|     public function findElement(WebDriverBy $by)
 | |
|     {
 | |
|         $params = [
 | |
|             'using' => $by->getMechanism(),
 | |
|             'value' => $by->getValue(),
 | |
|             ':id' => $this->id,
 | |
|         ];
 | |
|         $raw_element = $this->executor->execute(
 | |
|             DriverCommand::FIND_CHILD_ELEMENT,
 | |
|             $params
 | |
|         );
 | |
| 
 | |
|         return $this->newElement($raw_element['ELEMENT']);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Find all WebDriverElements within this element using the given mechanism.
 | |
|      *
 | |
|      * @param WebDriverBy $by
 | |
|      * @return RemoteWebElement[] A list of all WebDriverElements, or an empty
 | |
|      *    array if nothing matches
 | |
|      * @see WebDriverBy
 | |
|      */
 | |
|     public function findElements(WebDriverBy $by)
 | |
|     {
 | |
|         $params = [
 | |
|             'using' => $by->getMechanism(),
 | |
|             'value' => $by->getValue(),
 | |
|             ':id' => $this->id,
 | |
|         ];
 | |
|         $raw_elements = $this->executor->execute(
 | |
|             DriverCommand::FIND_CHILD_ELEMENTS,
 | |
|             $params
 | |
|         );
 | |
| 
 | |
|         $elements = [];
 | |
|         foreach ($raw_elements as $raw_element) {
 | |
|             $elements[] = $this->newElement($raw_element['ELEMENT']);
 | |
|         }
 | |
| 
 | |
|         return $elements;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the value of a the given attribute of the element.
 | |
|      *
 | |
|      * @param string $attribute_name The name of the attribute.
 | |
|      * @return string|null The value of the attribute.
 | |
|      */
 | |
|     public function getAttribute($attribute_name)
 | |
|     {
 | |
|         $params = [
 | |
|             ':name' => $attribute_name,
 | |
|             ':id' => $this->id,
 | |
|         ];
 | |
| 
 | |
|         return $this->executor->execute(
 | |
|             DriverCommand::GET_ELEMENT_ATTRIBUTE,
 | |
|             $params
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the value of a given CSS property.
 | |
|      *
 | |
|      * @param string $css_property_name The name of the CSS property.
 | |
|      * @return string The value of the CSS property.
 | |
|      */
 | |
|     public function getCSSValue($css_property_name)
 | |
|     {
 | |
|         $params = [
 | |
|             ':propertyName' => $css_property_name,
 | |
|             ':id' => $this->id,
 | |
|         ];
 | |
| 
 | |
|         return $this->executor->execute(
 | |
|             DriverCommand::GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
 | |
|             $params
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the location of element relative to the top-left corner of the page.
 | |
|      *
 | |
|      * @return WebDriverPoint The location of the element.
 | |
|      */
 | |
|     public function getLocation()
 | |
|     {
 | |
|         $location = $this->executor->execute(
 | |
|             DriverCommand::GET_ELEMENT_LOCATION,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
| 
 | |
|         return new WebDriverPoint($location['x'], $location['y']);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Try scrolling the element into the view port and return the location of
 | |
|      * element relative to the top-left corner of the page afterwards.
 | |
|      *
 | |
|      * @return WebDriverPoint The location of the element.
 | |
|      */
 | |
|     public function getLocationOnScreenOnceScrolledIntoView()
 | |
|     {
 | |
|         $location = $this->executor->execute(
 | |
|             DriverCommand::GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
| 
 | |
|         return new WebDriverPoint($location['x'], $location['y']);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return WebDriverCoordinates
 | |
|      */
 | |
|     public function getCoordinates()
 | |
|     {
 | |
|         $element = $this;
 | |
| 
 | |
|         $on_screen = null; // planned but not yet implemented
 | |
|         $in_view_port = function () use ($element) {
 | |
|             return $element->getLocationOnScreenOnceScrolledIntoView();
 | |
|         };
 | |
|         $on_page = function () use ($element) {
 | |
|             return $element->getLocation();
 | |
|         };
 | |
|         $auxiliary = $this->getID();
 | |
| 
 | |
|         return new WebDriverCoordinates(
 | |
|             $on_screen,
 | |
|             $in_view_port,
 | |
|             $on_page,
 | |
|             $auxiliary
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the size of element.
 | |
|      *
 | |
|      * @return WebDriverDimension The dimension of the element.
 | |
|      */
 | |
|     public function getSize()
 | |
|     {
 | |
|         $size = $this->executor->execute(
 | |
|             DriverCommand::GET_ELEMENT_SIZE,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
| 
 | |
|         return new WebDriverDimension($size['width'], $size['height']);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the (lowercase) tag name of this element.
 | |
|      *
 | |
|      * @return string The tag name.
 | |
|      */
 | |
|     public function getTagName()
 | |
|     {
 | |
|         // Force tag name to be lowercase as expected by JsonWire protocol for Opera driver
 | |
|         // until this issue is not resolved :
 | |
|         // https://github.com/operasoftware/operadriver/issues/102
 | |
|         // Remove it when fixed to be consistent with the protocol.
 | |
|         return mb_strtolower($this->executor->execute(
 | |
|             DriverCommand::GET_ELEMENT_TAG_NAME,
 | |
|             [':id' => $this->id]
 | |
|         ));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the visible (i.e. not hidden by CSS) innerText of this element,
 | |
|      * including sub-elements, without any leading or trailing whitespace.
 | |
|      *
 | |
|      * @return string The visible innerText of this element.
 | |
|      */
 | |
|     public function getText()
 | |
|     {
 | |
|         return $this->executor->execute(
 | |
|             DriverCommand::GET_ELEMENT_TEXT,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Is this element displayed or not? This method avoids the problem of having
 | |
|      * to parse an element's "style" attribute.
 | |
|      *
 | |
|      * @return bool
 | |
|      */
 | |
|     public function isDisplayed()
 | |
|     {
 | |
|         return $this->executor->execute(
 | |
|             DriverCommand::IS_ELEMENT_DISPLAYED,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Is the element currently enabled or not? This will generally return true
 | |
|      * for everything but disabled input elements.
 | |
|      *
 | |
|      * @return bool
 | |
|      */
 | |
|     public function isEnabled()
 | |
|     {
 | |
|         return $this->executor->execute(
 | |
|             DriverCommand::IS_ELEMENT_ENABLED,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determine whether or not this element is selected or not.
 | |
|      *
 | |
|      * @return bool
 | |
|      */
 | |
|     public function isSelected()
 | |
|     {
 | |
|         return $this->executor->execute(
 | |
|             DriverCommand::IS_ELEMENT_SELECTED,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Simulate typing into an element, which may set its value.
 | |
|      *
 | |
|      * @param mixed $value The data to be typed.
 | |
|      * @return RemoteWebElement The current instance.
 | |
|      */
 | |
|     public function sendKeys($value)
 | |
|     {
 | |
|         $local_file = $this->fileDetector->getLocalFile($value);
 | |
|         if ($local_file === null) {
 | |
|             $params = [
 | |
|                 'value' => WebDriverKeys::encode($value),
 | |
|                 ':id' => $this->id,
 | |
|             ];
 | |
|             $this->executor->execute(DriverCommand::SEND_KEYS_TO_ELEMENT, $params);
 | |
|         } else {
 | |
|             $remote_path = $this->upload($local_file);
 | |
|             $params = [
 | |
|                 'value' => WebDriverKeys::encode($remote_path),
 | |
|                 ':id' => $this->id,
 | |
|             ];
 | |
|             $this->executor->execute(DriverCommand::SEND_KEYS_TO_ELEMENT, $params);
 | |
|         }
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Set the fileDetector in order to let the RemoteWebElement to know that
 | |
|      * you are going to upload a file.
 | |
|      *
 | |
|      * Basically, if you want WebDriver trying to send a file, set the fileDetector
 | |
|      * to be LocalFileDetector. Otherwise, keep it UselessFileDetector.
 | |
|      *
 | |
|      *   eg. `$element->setFileDetector(new LocalFileDetector);`
 | |
|      *
 | |
|      * @param FileDetector $detector
 | |
|      * @return RemoteWebElement
 | |
|      * @see FileDetector
 | |
|      * @see LocalFileDetector
 | |
|      * @see UselessFileDetector
 | |
|      */
 | |
|     public function setFileDetector(FileDetector $detector)
 | |
|     {
 | |
|         $this->fileDetector = $detector;
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * If this current element is a form, or an element within a form, then this will be submitted to the remote server.
 | |
|      *
 | |
|      * @return RemoteWebElement The current instance.
 | |
|      */
 | |
|     public function submit()
 | |
|     {
 | |
|         $this->executor->execute(
 | |
|             DriverCommand::SUBMIT_ELEMENT,
 | |
|             [':id' => $this->id]
 | |
|         );
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the opaque ID of the element.
 | |
|      *
 | |
|      * @return string The opaque ID.
 | |
|      */
 | |
|     public function getID()
 | |
|     {
 | |
|         return $this->id;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Test if two element IDs refer to the same DOM element.
 | |
|      *
 | |
|      * @param WebDriverElement $other
 | |
|      * @return bool
 | |
|      */
 | |
|     public function equals(WebDriverElement $other)
 | |
|     {
 | |
|         return $this->executor->execute(DriverCommand::ELEMENT_EQUALS, [
 | |
|             ':id' => $this->id,
 | |
|             ':other' => $other->getID(),
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return the WebDriverElement with $id
 | |
|      *
 | |
|      * @param string $id
 | |
|      *
 | |
|      * @return static
 | |
|      */
 | |
|     protected function newElement($id)
 | |
|     {
 | |
|         return new static($this->executor, $id);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Upload a local file to the server
 | |
|      *
 | |
|      * @param string $local_file
 | |
|      *
 | |
|      * @throws WebDriverException
 | |
|      * @return string The remote path of the file.
 | |
|      */
 | |
|     protected function upload($local_file)
 | |
|     {
 | |
|         if (!is_file($local_file)) {
 | |
|             throw new WebDriverException('You may only upload files: ' . $local_file);
 | |
|         }
 | |
| 
 | |
|         // Create a temporary file in the system temp directory.
 | |
|         $temp_zip = tempnam(sys_get_temp_dir(), 'WebDriverZip');
 | |
|         $zip = new ZipArchive();
 | |
|         if ($zip->open($temp_zip, ZipArchive::CREATE) !== true) {
 | |
|             return false;
 | |
|         }
 | |
|         $info = pathinfo($local_file);
 | |
|         $file_name = $info['basename'];
 | |
|         $zip->addFile($local_file, $file_name);
 | |
|         $zip->close();
 | |
|         $params = [
 | |
|             'file' => base64_encode(file_get_contents($temp_zip)),
 | |
|         ];
 | |
|         $remote_path = $this->executor->execute(
 | |
|             DriverCommand::UPLOAD_FILE,
 | |
|             $params
 | |
|         );
 | |
|         unlink($temp_zip);
 | |
| 
 | |
|         return $remote_path;
 | |
|     }
 | |
| }
 | 
