diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..7245896 --- /dev/null +++ b/src/Exception/InvalidArgumentException.php @@ -0,0 +1,18 @@ + + */ +class InvalidArgumentException extends \InvalidArgumentException implements PlaywrightExceptionInterface +{ +} diff --git a/src/Input/ModifierKey.php b/src/Input/ModifierKey.php new file mode 100644 index 0000000..8641a31 --- /dev/null +++ b/src/Input/ModifierKey.php @@ -0,0 +1,35 @@ + + */ +enum ModifierKey: string +{ + case Alt = 'Alt'; + case Control = 'Control'; + case Meta = 'Meta'; + case Shift = 'Shift'; + + public static function fromString(string $modifier): self + { + return match (strtolower($modifier)) { + 'alt' => self::Alt, + 'control', 'ctrl' => self::Control, + 'meta', 'cmd', 'command' => self::Meta, + 'shift' => self::Shift, + default => throw new InvalidArgumentException(sprintf('Unknown modifier key: "%s".', $modifier)), + }; + } +} diff --git a/src/Network/Request.php b/src/Network/Request.php index c5bd7cc..99767f3 100644 --- a/src/Network/Request.php +++ b/src/Network/Request.php @@ -27,7 +27,7 @@ public function __construct( public function url(): string { - $url = $this->data['url']; + $url = $this->data['url'] ?? null; if (!is_string($url)) { throw new ProtocolErrorException('Invalid URL in request data', 0); } @@ -37,7 +37,7 @@ public function url(): string public function method(): string { - $method = $this->data['method']; + $method = $this->data['method'] ?? null; if (!is_string($method)) { throw new ProtocolErrorException('Invalid method in request data', 0); } @@ -45,9 +45,12 @@ public function method(): string return $method; } + /** + * @return array + */ public function headers(): array { - $headers = $this->data['headers']; + $headers = $this->data['headers'] ?? []; if (!is_array($headers)) { return []; } @@ -59,7 +62,6 @@ public function headers(): array } } - /* @var array $stringHeaders */ return $stringHeaders; } @@ -77,26 +79,22 @@ public function postDataJSON(): ?array return null; } - $decoded = json_decode($postData, true); - - if (!is_array($decoded)) { + if (!json_validate($postData)) { return null; } - $result = []; - foreach ($decoded as $key => $value) { - if (!is_string($key)) { - return null; - } - $result[$key] = $value; + $decoded = json_decode($postData, false, 512, JSON_THROW_ON_ERROR); + + if (!$decoded instanceof \stdClass) { + return null; } - return $result; + return (array) $decoded; } public function resourceType(): string { - $resourceType = $this->data['resourceType']; + $resourceType = $this->data['resourceType'] ?? null; if (!is_string($resourceType)) { throw new ProtocolErrorException('Invalid resourceType in request data', 0); } diff --git a/src/Page/Page.php b/src/Page/Page.php index 18c3a81..4f8940c 100644 --- a/src/Page/Page.php +++ b/src/Page/Page.php @@ -26,6 +26,7 @@ use PlaywrightPHP\Frame\FrameLocatorInterface; use PlaywrightPHP\Input\Keyboard; use PlaywrightPHP\Input\KeyboardInterface; +use PlaywrightPHP\Input\ModifierKey; use PlaywrightPHP\Input\Mouse; use PlaywrightPHP\Input\MouseInterface; use PlaywrightPHP\Locator\Locator; @@ -45,8 +46,11 @@ final class Page implements PageInterface, EventDispatcherInterface { private KeyboardInterface $keyboard; + private MouseInterface $mouse; + private PageEventHandlerInterface $eventHandler; + private LoggerInterface $logger; public function __construct( @@ -287,6 +291,30 @@ public function click(string $selector, array $options = []): self return $this; } + /** + * @param array $options + */ + public function altClick(string $selector, array $options = []): self + { + return $this->click($selector, [...$options, 'modifiers' => ModifierKey::Alt]); + } + + /** + * @param array $options + */ + public function controlClick(string $selector, array $options = []): self + { + return $this->click($selector, [...$options, 'modifiers' => ModifierKey::Control]); + } + + /** + * @param array $options + */ + public function shiftClick(string $selector, array $options = []): self + { + return $this->click($selector, [...$options, 'modifiers' => ModifierKey::Shift]); + } + /** * @param array $options */ diff --git a/src/Page/PageInterface.php b/src/Page/PageInterface.php index 9ea006a..4bf6840 100644 --- a/src/Page/PageInterface.php +++ b/src/Page/PageInterface.php @@ -35,6 +35,21 @@ public function goto(string $url, array $options = []): ?ResponseInterface; */ public function click(string $selector, array $options = []): self; + /** + * @param array $options + */ + public function altClick(string $selector, array $options = []): self; + + /** + * @param array $options + */ + public function controlClick(string $selector, array $options = []): self; + + /** + * @param array $options + */ + public function shiftClick(string $selector, array $options = []): self; + /** * @param array $options */ @@ -63,7 +78,8 @@ public function context(): BrowserContextInterface; /** * @param array|null $urls * - * @return array + * @return array */ public function cookies(?array $urls = null): array; diff --git a/tests/Unit/Input/ModifierKeyTest.php b/tests/Unit/Input/ModifierKeyTest.php new file mode 100644 index 0000000..4042bc1 --- /dev/null +++ b/tests/Unit/Input/ModifierKeyTest.php @@ -0,0 +1,60 @@ +assertSame('Alt', ModifierKey::Alt->value); + $this->assertSame('Control', ModifierKey::Control->value); + $this->assertSame('Meta', ModifierKey::Meta->value); + $this->assertSame('Shift', ModifierKey::Shift->value); + } + + #[DataProvider('provideFromStringData')] + public function testFromString(ModifierKey $expected, string $input): void + { + $this->assertSame($expected, ModifierKey::fromString($input)); + } + + public static function provideFromStringData(): array + { + return [ + 'alt lowercase' => [ModifierKey::Alt, 'alt'], + 'alt uppercase' => [ModifierKey::Alt, 'Alt'], + 'control lowercase' => [ModifierKey::Control, 'control'], + 'control uppercase' => [ModifierKey::Control, 'Control'], + 'control short' => [ModifierKey::Control, 'ctrl'], + 'meta lowercase' => [ModifierKey::Meta, 'meta'], + 'meta uppercase' => [ModifierKey::Meta, 'Meta'], + 'meta cmd' => [ModifierKey::Meta, 'cmd'], + 'meta command' => [ModifierKey::Meta, 'command'], + 'shift lowercase' => [ModifierKey::Shift, 'shift'], + 'shift uppercase' => [ModifierKey::Shift, 'Shift'], + ]; + } + + public function testFromStringWithInvalidModifier(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown modifier key: "invalid".'); + + ModifierKey::fromString('invalid'); + } +}