Chris@14
|
1 <?php
|
Chris@14
|
2 /**
|
Chris@14
|
3 * Copyright 2004-2017 Facebook. All Rights Reserved.
|
Chris@14
|
4 *
|
Chris@14
|
5 * Licensed under the Apache License, Version 2.0 (the "License");
|
Chris@14
|
6 * you may not use this file except in compliance with the License.
|
Chris@14
|
7 * You may obtain a copy of the License at
|
Chris@14
|
8 *
|
Chris@14
|
9 * http://www.apache.org/licenses/LICENSE-2.0
|
Chris@14
|
10 *
|
Chris@14
|
11 * Unless required by applicable law or agreed to in writing, software
|
Chris@14
|
12 * distributed under the License is distributed on an "AS IS" BASIS,
|
Chris@14
|
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
Chris@14
|
14 * See the License for the specific language governing permissions and
|
Chris@14
|
15 * limitations under the License.
|
Chris@14
|
16 *
|
Chris@14
|
17 * @package WebDriver
|
Chris@14
|
18 *
|
Chris@14
|
19 * @author Justin Bishop <jubishop@gmail.com>
|
Chris@14
|
20 * @author Anthon Pang <apang@softwaredevelopment.ca>
|
Chris@14
|
21 * @author Fabrizio Branca <mail@fabrizio-branca.de>
|
Chris@14
|
22 * @author Tsz Ming Wong <tszming@gmail.com>
|
Chris@14
|
23 */
|
Chris@14
|
24
|
Chris@14
|
25 namespace WebDriver;
|
Chris@14
|
26
|
Chris@14
|
27 use WebDriver\Exception as WebDriverException;
|
Chris@14
|
28
|
Chris@14
|
29 /**
|
Chris@14
|
30 * Abstract WebDriver\AbstractWebDriver class
|
Chris@14
|
31 *
|
Chris@14
|
32 * @package WebDriver
|
Chris@14
|
33 */
|
Chris@14
|
34 abstract class AbstractWebDriver
|
Chris@14
|
35 {
|
Chris@14
|
36 /**
|
Chris@14
|
37 * URL
|
Chris@14
|
38 *
|
Chris@14
|
39 * @var string
|
Chris@14
|
40 */
|
Chris@14
|
41 protected $url;
|
Chris@14
|
42
|
Chris@14
|
43 /**
|
Chris@14
|
44 * Return array of supported method names and corresponding HTTP request methods
|
Chris@14
|
45 *
|
Chris@14
|
46 * @return array
|
Chris@14
|
47 */
|
Chris@14
|
48 abstract protected function methods();
|
Chris@14
|
49
|
Chris@14
|
50 /**
|
Chris@14
|
51 * Return array of obsolete method names and corresponding HTTP request methods
|
Chris@14
|
52 *
|
Chris@14
|
53 * @return array
|
Chris@14
|
54 */
|
Chris@14
|
55 protected function obsoleteMethods()
|
Chris@14
|
56 {
|
Chris@14
|
57 return array();
|
Chris@14
|
58 }
|
Chris@14
|
59
|
Chris@14
|
60 /**
|
Chris@14
|
61 * Constructor
|
Chris@14
|
62 *
|
Chris@14
|
63 * @param string $url URL to Selenium server
|
Chris@14
|
64 */
|
Chris@14
|
65 public function __construct($url = 'http://localhost:4444/wd/hub')
|
Chris@14
|
66 {
|
Chris@14
|
67 $this->url = $url;
|
Chris@14
|
68 }
|
Chris@14
|
69
|
Chris@14
|
70 /**
|
Chris@14
|
71 * Magic method which returns URL to Selenium server
|
Chris@14
|
72 *
|
Chris@14
|
73 * @return string
|
Chris@14
|
74 */
|
Chris@14
|
75 public function __toString()
|
Chris@14
|
76 {
|
Chris@14
|
77 return $this->url;
|
Chris@14
|
78 }
|
Chris@14
|
79
|
Chris@14
|
80 /**
|
Chris@14
|
81 * Returns URL to Selenium server
|
Chris@14
|
82 *
|
Chris@14
|
83 * @return string
|
Chris@14
|
84 */
|
Chris@14
|
85 public function getURL()
|
Chris@14
|
86 {
|
Chris@14
|
87 return $this->url;
|
Chris@14
|
88 }
|
Chris@14
|
89
|
Chris@14
|
90 /**
|
Chris@14
|
91 * Curl request to webdriver server.
|
Chris@14
|
92 *
|
Chris@14
|
93 * @param string $requestMethod HTTP request method, e.g., 'GET', 'POST', or 'DELETE'
|
Chris@14
|
94 * @param string $command If not defined in methods() this function will throw.
|
Chris@14
|
95 * @param array $parameters If an array(), they will be posted as JSON parameters
|
Chris@14
|
96 * If a number or string, "/$params" is appended to url
|
Chris@14
|
97 * @param array $extraOptions key=>value pairs of curl options to pass to curl_setopt()
|
Chris@14
|
98 *
|
Chris@14
|
99 * @return array array('value' => ..., 'info' => ...)
|
Chris@14
|
100 *
|
Chris@14
|
101 * @throws \WebDriver\Exception if error
|
Chris@14
|
102 */
|
Chris@14
|
103 protected function curl($requestMethod, $command, $parameters = null, $extraOptions = array())
|
Chris@14
|
104 {
|
Chris@14
|
105 if ($parameters && is_array($parameters) && $requestMethod !== 'POST') {
|
Chris@14
|
106 throw WebDriverException::factory(
|
Chris@14
|
107 WebDriverException::NO_PARAMETERS_EXPECTED,
|
Chris@14
|
108 sprintf(
|
Chris@14
|
109 'The http request method called for %s is %s but it has to be POST if you want to pass the JSON parameters %s',
|
Chris@14
|
110 $command,
|
Chris@14
|
111 $requestMethod,
|
Chris@14
|
112 json_encode($parameters)
|
Chris@14
|
113 )
|
Chris@14
|
114 );
|
Chris@14
|
115 }
|
Chris@14
|
116
|
Chris@14
|
117 $url = sprintf('%s%s', $this->url, $command);
|
Chris@14
|
118
|
Chris@14
|
119 if ($parameters && (is_int($parameters) || is_string($parameters))) {
|
Chris@14
|
120 $url .= '/' . $parameters;
|
Chris@14
|
121 }
|
Chris@14
|
122
|
Chris@14
|
123 list($rawResult, $info) = ServiceFactory::getInstance()->getService('service.curl')->execute($requestMethod, $url, $parameters, $extraOptions);
|
Chris@14
|
124
|
Chris@14
|
125 $httpCode = $info['http_code'];
|
Chris@14
|
126
|
Chris@14
|
127 // According to https://w3c.github.io/webdriver/webdriver-spec.html all 4xx responses are to be considered
|
Chris@14
|
128 // an error and return plaintext, while 5xx responses are json encoded
|
Chris@14
|
129 if ($httpCode >= 400 && $httpCode <= 499) {
|
Chris@14
|
130 throw WebDriverException::factory(
|
Chris@14
|
131 WebDriverException::CURL_EXEC,
|
Chris@14
|
132 'Webdriver http error: ' . $httpCode . ', payload :' . substr($rawResult, 0, 1000)
|
Chris@14
|
133 );
|
Chris@14
|
134 }
|
Chris@14
|
135
|
Chris@14
|
136 $result = json_decode($rawResult, true);
|
Chris@14
|
137
|
Chris@14
|
138 if (!empty($rawResult) && $result === null && json_last_error() != JSON_ERROR_NONE) {
|
Chris@14
|
139 throw WebDriverException::factory(
|
Chris@14
|
140 WebDriverException::CURL_EXEC,
|
Chris@14
|
141 'Payload received from webdriver is not valid json: ' . substr($rawResult, 0, 1000)
|
Chris@14
|
142 );
|
Chris@14
|
143 }
|
Chris@14
|
144
|
Chris@14
|
145 if (is_array($result) && !array_key_exists('status', $result)) {
|
Chris@14
|
146 throw WebDriverException::factory(
|
Chris@14
|
147 WebDriverException::CURL_EXEC,
|
Chris@14
|
148 'Payload received from webdriver is valid but unexpected json: ' . substr($rawResult, 0, 1000)
|
Chris@14
|
149 );
|
Chris@14
|
150 }
|
Chris@14
|
151
|
Chris@14
|
152 $value = (is_array($result) && array_key_exists('value', $result)) ? $result['value'] : null;
|
Chris@14
|
153 $message = (is_array($value) && array_key_exists('message', $value)) ? $value['message'] : null;
|
Chris@14
|
154
|
Chris@14
|
155 // if not success, throw exception
|
Chris@14
|
156 if ((int) $result['status'] !== 0) {
|
Chris@14
|
157 throw WebDriverException::factory($result['status'], $message);
|
Chris@14
|
158 }
|
Chris@14
|
159
|
Chris@14
|
160 $sessionId = isset($result['sessionId'])
|
Chris@14
|
161 ? $result['sessionId']
|
Chris@14
|
162 : (isset($value['webdriver.remote.sessionid'])
|
Chris@14
|
163 ? $value['webdriver.remote.sessionid']
|
Chris@14
|
164 : null
|
Chris@14
|
165 );
|
Chris@14
|
166
|
Chris@14
|
167 return array(
|
Chris@14
|
168 'value' => $value,
|
Chris@14
|
169 'info' => $info,
|
Chris@14
|
170 'sessionId' => $sessionId,
|
Chris@14
|
171 'sessionUrl' => $sessionId ? $this->url . '/session/' . $sessionId : $info['url'],
|
Chris@14
|
172 );
|
Chris@14
|
173 }
|
Chris@14
|
174
|
Chris@14
|
175 /**
|
Chris@14
|
176 * Magic method that maps calls to class methods to execute WebDriver commands
|
Chris@14
|
177 *
|
Chris@14
|
178 * @param string $name Method name
|
Chris@14
|
179 * @param array $arguments Arguments
|
Chris@14
|
180 *
|
Chris@14
|
181 * @return mixed
|
Chris@14
|
182 *
|
Chris@14
|
183 * @throws \WebDriver\Exception if invalid WebDriver command
|
Chris@14
|
184 */
|
Chris@14
|
185 public function __call($name, $arguments)
|
Chris@14
|
186 {
|
Chris@14
|
187 if (count($arguments) > 1) {
|
Chris@14
|
188 throw WebDriverException::factory(
|
Chris@14
|
189 WebDriverException::JSON_PARAMETERS_EXPECTED,
|
Chris@14
|
190 'Commands should have at most only one parameter, which should be the JSON Parameter object'
|
Chris@14
|
191 );
|
Chris@14
|
192 }
|
Chris@14
|
193
|
Chris@14
|
194 if (preg_match('/^(get|post|delete)/', $name, $matches)) {
|
Chris@14
|
195 $requestMethod = strtoupper($matches[0]);
|
Chris@14
|
196 $webdriverCommand = strtolower(substr($name, strlen($requestMethod)));
|
Chris@14
|
197 } else {
|
Chris@14
|
198 $webdriverCommand = $name;
|
Chris@14
|
199 $requestMethod = $this->getRequestMethod($webdriverCommand);
|
Chris@14
|
200 }
|
Chris@14
|
201
|
Chris@14
|
202 $methods = $this->methods();
|
Chris@14
|
203
|
Chris@14
|
204 if (!in_array($requestMethod, (array) $methods[$webdriverCommand])) {
|
Chris@14
|
205 throw WebDriverException::factory(
|
Chris@14
|
206 WebDriverException::INVALID_REQUEST,
|
Chris@14
|
207 sprintf(
|
Chris@14
|
208 '%s is not an available http request method for the command %s.',
|
Chris@14
|
209 $requestMethod,
|
Chris@14
|
210 $webdriverCommand
|
Chris@14
|
211 )
|
Chris@14
|
212 );
|
Chris@14
|
213 }
|
Chris@14
|
214
|
Chris@14
|
215 $result = $this->curl(
|
Chris@14
|
216 $requestMethod,
|
Chris@14
|
217 '/' . $webdriverCommand,
|
Chris@14
|
218 array_shift($arguments)
|
Chris@14
|
219 );
|
Chris@14
|
220
|
Chris@14
|
221 return $result['value'];
|
Chris@14
|
222 }
|
Chris@14
|
223
|
Chris@14
|
224 /**
|
Chris@14
|
225 * Get default HTTP request method for a given WebDriver command
|
Chris@14
|
226 *
|
Chris@14
|
227 * @param string $webdriverCommand
|
Chris@14
|
228 *
|
Chris@14
|
229 * @return string
|
Chris@14
|
230 *
|
Chris@14
|
231 * @throws \WebDriver\Exception if invalid WebDriver command
|
Chris@14
|
232 */
|
Chris@14
|
233 private function getRequestMethod($webdriverCommand)
|
Chris@14
|
234 {
|
Chris@14
|
235 if (!array_key_exists($webdriverCommand, $this->methods())) {
|
Chris@14
|
236 throw WebDriverException::factory(
|
Chris@14
|
237 array_key_exists($webdriverCommand, $this->obsoleteMethods())
|
Chris@14
|
238 ? WebDriverException::OBSOLETE_COMMAND : WebDriverException::UNKNOWN_COMMAND,
|
Chris@14
|
239 sprintf('%s is not a valid WebDriver command.', $webdriverCommand)
|
Chris@14
|
240 );
|
Chris@14
|
241 }
|
Chris@14
|
242
|
Chris@14
|
243 $methods = $this->methods();
|
Chris@14
|
244 $requestMethods = (array) $methods[$webdriverCommand];
|
Chris@14
|
245
|
Chris@14
|
246 return array_shift($requestMethods);
|
Chris@14
|
247 }
|
Chris@14
|
248 }
|