@@ -287,12 +287,7 @@ public function openUri(string $sessionIdentifier, string $uri): PromiseInterfac
287287 $ navigationPromise = $ responsePromise
288288 ->then (
289289 function (ResponseInterface $ response ) {
290- $ responseBody = (string ) $ response ->getBody ();
291-
292- // todo: locate an error message or set it as "undefined error"
293- if ('{"value":null} ' !== $ responseBody ) {
294- throw new RuntimeException ('URI navigation is not confirmed. ' );
295- }
290+ $ this ->onCommandConfirmation ($ response , 'URI navigation is not confirmed. ' );
296291
297292 return null ;
298293 }
@@ -432,19 +427,78 @@ public function mouseMove(
432427 int $ moveDuration = 100 ,
433428 array $ startingPoint = null
434429 ): PromiseInterface {
435- // TODO: Implement mouseMove() method.
430+ if (is_array ($ startingPoint )) {
431+ $ actionOrigin = $ startingPoint ;
432+ } else {
433+ $ actionOrigin = 'pointer ' ;
434+ }
435+
436+ $ mouseActions = [
437+ [
438+ 'type ' => 'pointerMove ' ,
439+ 'origin ' => $ actionOrigin ,
440+ 'x ' => $ offsetX ,
441+ 'y ' => $ offsetY ,
442+ 'duration ' => $ moveDuration ,
443+ ],
444+ ];
436445
437- return reject (new RuntimeException ('Not implemented. ' ));
446+ $ responsePromise = $ this ->requestMouseActions ($ sessionIdentifier , $ mouseActions );
447+
448+ $ moveConfirmationPromise = $ responsePromise
449+ ->then (
450+ function (ResponseInterface $ response ) {
451+ $ this ->onCommandConfirmation ($ response , 'Mouse move command is not confirmed. ' );
452+
453+ return null ;
454+ }
455+ )
456+ ->then (
457+ null ,
458+ function (Throwable $ rejectionReason ) {
459+ throw new RuntimeException ('Unable to confirm mouse move action (request). ' , 0 , $ rejectionReason );
460+ }
461+ )
462+ ;
463+
464+ return $ moveConfirmationPromise ;
438465 }
439466
440467 /**
441468 * {@inheritDoc}
442469 */
443470 public function mouseLeftClick (string $ sessionIdentifier ): PromiseInterface
444471 {
445- // TODO: Implement mouseLeftClick() method.
472+ $ mouseActions = [
473+ [
474+ 'type ' => 'pointerDown ' ,
475+ 'button ' => 0 ,
476+ ],
477+ [
478+ 'type ' => 'pointerUp ' ,
479+ 'button ' => 0 ,
480+ ],
481+ ];
446482
447- return reject (new RuntimeException ('Not implemented. ' ));
483+ $ responsePromise = $ this ->requestMouseActions ($ sessionIdentifier , $ mouseActions );
484+
485+ $ clickConfirmationPromise = $ responsePromise
486+ ->then (
487+ function (ResponseInterface $ response ) {
488+ $ this ->onCommandConfirmation ($ response , 'Mouse click command is not confirmed. ' );
489+
490+ return null ;
491+ }
492+ )
493+ ->then (
494+ null ,
495+ function (Throwable $ rejectionReason ) {
496+ throw new RuntimeException ('Unable to confirm mouse click action (request). ' , 0 , $ rejectionReason );
497+ }
498+ )
499+ ;
500+
501+ return $ clickConfirmationPromise ;
448502 }
449503
450504 /**
@@ -456,4 +510,69 @@ public function getScreenshot(string $sessionIdentifier): PromiseInterface
456510
457511 return reject (new RuntimeException ('Not implemented. ' ));
458512 }
513+
514+ /**
515+ * Initializes a request to execute a sequence of mouse-specific actions in the remote browser
516+ *
517+ * @param string $sessionIdentifier Session identifier for Selenium Grid server (hub)
518+ * @param array $mouseActions A data structure that describes a sequence of actions to be performed by the
519+ * internal pointer with type "mouse"
520+ *
521+ * @return PromiseInterface<ResponseInterface>
522+ */
523+ private function requestMouseActions (string $ sessionIdentifier , array $ mouseActions ): PromiseInterface
524+ {
525+ $ requestUri = sprintf (
526+ 'http://%s:%d/wd/hub/session/%s/actions ' ,
527+ $ this ->_options ['server ' ]['host ' ],
528+ $ this ->_options ['server ' ]['port ' ],
529+ $ sessionIdentifier
530+ );
531+
532+ $ requestHeaders = [
533+ 'Content-Type ' => 'application/json; charset=UTF-8 ' ,
534+ ];
535+
536+ $ requestContents = json_encode (
537+ [
538+ 'actions ' => [
539+ [
540+ 'type ' => 'pointer ' ,
541+ 'id ' => 'mouse ' ,
542+ 'parameters ' => [
543+ 'pointerType ' => 'mouse ' ,
544+ ],
545+ 'actions ' => $ mouseActions ,
546+ ],
547+ ],
548+ ]
549+ );
550+
551+ $ responsePromise = $ this ->httpClient ->post ($ requestUri , $ requestHeaders , $ requestContents );
552+
553+ return $ responsePromise ;
554+ }
555+
556+ /**
557+ * Ensures that a related action is properly executed (confirmed) by the remote server, triggers an error otherwise.
558+ *
559+ * Is used when no specific context to check is required (some methods will use more advanced confirmation checks
560+ * instead this "default").
561+ *
562+ * @param ResponseInterface $response PSR-7 response message from the Selenium hub with action results
563+ * @param string $errorMessage Will be used if an error is registered during confirmation check
564+ *
565+ * @return void
566+ *
567+ * @throws RuntimeException Whenever an error has occurred during confirmation check for a remote action
568+ */
569+ private function onCommandConfirmation (ResponseInterface $ response , string $ errorMessage ): void
570+ {
571+ $ responseBody = (string ) $ response ->getBody ();
572+
573+ // todo: locate an error message or set it as "undefined error"
574+ if ('{"value":null} ' !== $ responseBody ) {
575+ throw new RuntimeException ($ errorMessage );
576+ }
577+ }
459578}
0 commit comments