Skip to content

Commit 70a31ca

Browse files
committed
wip
1 parent 0697b05 commit 70a31ca

6 files changed

Lines changed: 514 additions & 2 deletions

File tree

playground/resources/views/test-pages/form-inputs.blade.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
</div>
1212

1313
<div class="flex space-x-4">
14-
<input id="checked-checkbox" name="checked-checkbox" type="checkbox" checked />
14+
<input id="checked-checkbox" aria-label="checked-checkbox" name="checked-checkbox" type="checkbox" checked />
1515
<label for="checked-checkbox" class="">Checked checkbox</label>
1616
</div>
1717
</div>
1818
<div>
1919
<h1 class="text-2xl font-bold my-2">E-mail</h1>
2020
<div class="flex space-x-4">
2121
<input id="email" name="email" type="text" class="bg-white text-black p-2" value="john.doe@pestphp.com" />
22-
</div>
22+
</div>
2323
</div>
2424
</div>
2525
</section>

src/Playwright/Frame.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace Pest\Browser\Playwright;
66

7+
use Pest\Browser\Support\Selector;
8+
79
/**
810
* @internal
911
*/
@@ -109,6 +111,66 @@ public function querySelector(string $selector): ?Element
109111
return null;
110112
}
111113

114+
/**
115+
* Finds an element by the specified role.
116+
*
117+
* @param array<string, string|bool> $params
118+
*/
119+
public function getByRole(string $role, array $params): ?Element
120+
{
121+
return $this->querySelector(Selector::getByRoleSelector($role, $params));
122+
}
123+
124+
/**
125+
* Finds an element by test ID.
126+
*/
127+
public function getByTestId(string $testId): ?Element
128+
{
129+
$testIdAttributeName = 'data-testid';
130+
131+
return $this->querySelector(Selector::getByTestIdSelector($testIdAttributeName, $testId));
132+
}
133+
134+
/**
135+
* Finds an element by alt text.
136+
*/
137+
public function getByAltText(string $text, bool $exact = false): ?Element
138+
{
139+
return $this->querySelector(Selector::getByAltTextSelector($text, $exact));
140+
}
141+
142+
/**
143+
* Finds an element by label text.
144+
*/
145+
public function getByLabel(string $text, bool $exact = false): ?Element
146+
{
147+
return $this->querySelector(Selector::getByLabelSelector($text, $exact));
148+
}
149+
150+
/**
151+
* Finds an element by placeholder text.
152+
*/
153+
public function getByPlaceholder(string $text, bool $exact = false): ?Element
154+
{
155+
return $this->querySelector(Selector::getByPlaceholderSelector($text, $exact));
156+
}
157+
158+
/**
159+
* Finds an element by its text content.
160+
*/
161+
public function getByText(string $text, bool $exact = false): ?Element
162+
{
163+
return $this->querySelector(Selector::getByTextSelector($text, $exact));
164+
}
165+
166+
/**
167+
* Finds an element by its title attribute.
168+
*/
169+
public function getByTitle(string $text, bool $exact = false): ?Element
170+
{
171+
return $this->querySelector(Selector::getByTitleSelector($text, $exact));
172+
}
173+
112174
/**
113175
* Clicks the element matching the specified selector.
114176
*/

src/Playwright/Page.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,64 @@ public function querySelector(string $selector): ?Element
128128
return $this->frame->querySelector($selector);
129129
}
130130

131+
/**
132+
* Finds an element by the specified role.
133+
*
134+
* @param array<string, string|bool> $params
135+
*/
136+
public function getByRole(string $role, array $params): ?Element
137+
{
138+
return $this->frame->getByRole($role, $params);
139+
}
140+
141+
/**
142+
* Finds an element by test ID.
143+
*/
144+
public function getByTestId(string $testId): ?Element
145+
{
146+
return $this->frame->getByTestId($testId);
147+
}
148+
149+
/**
150+
* Finds an element by alt text.
151+
*/
152+
public function getByAltText(string $text, bool $exact = false): ?Element
153+
{
154+
return $this->frame->getByAltText($text, $exact);
155+
}
156+
157+
/**
158+
* Finds an element by label text.
159+
*/
160+
public function getByLabel(string $text, bool $exact = false): ?Element
161+
{
162+
return $this->frame->getByLabel($text, $exact);
163+
}
164+
165+
/**
166+
* Finds an element by placeholder text.
167+
*/
168+
public function getByPlaceholder(string $text, bool $exact = false): ?Element
169+
{
170+
return $this->frame->getByPlaceholder($text, $exact);
171+
}
172+
173+
/**
174+
* Finds an element by its text content.
175+
*/
176+
public function getByText(string $text, bool $exact = false): ?Element
177+
{
178+
return $this->frame->getByText($text, $exact);
179+
}
180+
181+
/**
182+
* Finds an element by its title attribute.
183+
*/
184+
public function getByTitle(string $text, bool $exact = false): ?Element
185+
{
186+
return $this->frame->getByTitle($text, $exact);
187+
}
188+
131189
/**
132190
* Returns the page's title.
133191
*/

src/Support/Selector.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Pest\Browser\Support;
6+
7+
final class Selector
8+
{
9+
/**
10+
* Get selector by attribute text.
11+
*/
12+
public static function getByAttributeTextSelector(string $attrName, string $text, bool $exact = false): string
13+
{
14+
return "attr=[{$attrName}=".self::escapeForAttributeSelectorOrRegex($text, $exact).']';
15+
}
16+
17+
/**
18+
* Get selector by test ID.
19+
*/
20+
public static function getByTestIdSelector(string $testIdAttributeName, string $testId): string
21+
{
22+
return "testid=[{$testIdAttributeName}=".self::escapeForAttributeSelectorOrRegex($testId, true).']';
23+
}
24+
25+
/**
26+
* Get selector by label.
27+
*/
28+
public static function getByLabelSelector(string $text, bool $exact): string
29+
{
30+
return 'label='.self::escapeForTextSelector($text, $exact);
31+
}
32+
33+
/**
34+
* Get selector by alt text.
35+
*/
36+
public static function getByAltTextSelector(string $text, bool $exact): string
37+
{
38+
return self::getByAttributeTextSelector('alt', $text, $exact);
39+
}
40+
41+
/**
42+
* Get selector by title.
43+
*/
44+
public static function getByTitleSelector(string $text, bool $exact): string
45+
{
46+
return self::getByAttributeTextSelector('title', $text, $exact);
47+
}
48+
49+
/**
50+
* Get selector by placeholder.
51+
*/
52+
public static function getByPlaceholderSelector(string $text, bool $exact): string
53+
{
54+
return self::getByAttributeTextSelector('placeholder', $text, $exact);
55+
}
56+
57+
/**
58+
* Get selector by text.
59+
*/
60+
public static function getByTextSelector(string $text, bool $exact): string
61+
{
62+
return 'text='.self::escapeForTextSelector($text, $exact);
63+
}
64+
65+
/**
66+
* Escape text for regex.
67+
*/
68+
public static function escapeForRegex(string $text): string
69+
{
70+
return preg_quote($text, '/');
71+
}
72+
73+
/**
74+
* Escape for text selector.
75+
*/
76+
public static function escapeForTextSelector(string $text, bool $exact = false): string
77+
{
78+
if ($exact) {
79+
return json_encode($text).'s';
80+
}
81+
82+
return json_encode($text).'i';
83+
}
84+
85+
/**
86+
* Escape for attribute selector or regex.
87+
*/
88+
public static function escapeForAttributeSelectorOrRegex(string $text, bool $exact = false): string
89+
{
90+
return self::escapeForAttributeSelector($text, $exact);
91+
}
92+
93+
/**
94+
* Escape for attribute selector.
95+
*/
96+
public static function escapeForAttributeSelector(string $text, bool $exact = false): string
97+
{
98+
$escapedText = str_replace('\\', '\\\\', $text);
99+
$escapedText = str_replace('"', '\\"', $escapedText);
100+
101+
if ($exact) {
102+
return "\"{$escapedText}\"";
103+
}
104+
105+
return "\"{$escapedText}\"i";
106+
107+
}
108+
109+
public static function getByRoleSelector(string $role, array $options = []): string
110+
{
111+
$props = [];
112+
113+
if (isset($options['checked'])) {
114+
$props['checked'] = $options['checked'] ? 'true' : 'false';
115+
}
116+
if (isset($options['disabled'])) {
117+
$props['disabled'] = $options['disabled'] ? 'true' : 'false';
118+
}
119+
if (isset($options['selected'])) {
120+
$props['selected'] = $options['selected'] ? 'true' : 'false';
121+
}
122+
if (isset($options['expanded'])) {
123+
$props['expanded'] = $options['expanded'] ? 'true' : 'false';
124+
}
125+
if (isset($options['includeHidden'])) {
126+
$props['include-hidden'] = $options['includeHidden'] ? 'true' : 'false';
127+
}
128+
if (isset($options['level'])) {
129+
$props['level'] = (string) $options['level'];
130+
}
131+
if (isset($options['name'])) {
132+
$exact = $options['exact'] ?? false;
133+
$props['name'] = self::escapeForAttributeSelector($options['name'], $exact);
134+
}
135+
if (isset($options['pressed'])) {
136+
$props['pressed'] = $options['pressed'] ? 'true' : 'false';
137+
}
138+
139+
$propsStr = '';
140+
foreach ($props as $k => $v) {
141+
$propsStr .= '['.$k.'='.$v.']';
142+
}
143+
144+
return "role={$role}{$propsStr}";
145+
}
146+
}

tests/Browser/Operations/AssertCheckedTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@
99
$page = $this->page(playgroundUrl('/test/form-inputs'));
1010

1111
expect($page->querySelector('input[name="checked-checkbox"]'))->toBeChecked();
12+
13+
expect($page->getByRole('checkbox', ['name' => 'checked-checkbox']))->toBeChecked();
14+
});
15+
16+
it('fails when checkbox is checked', function () {
17+
$page = $this->page(playgroundUrl('/test/form-inputs'));
18+
19+
expect($page->querySelector('input[name="checked-checkbox"]'))->not->toBeChecked();
20+
})->throws(ExpectationFailedException::class);
21+
22+
it('passes when checkbox is not checked', function () {
23+
$page = $this->page(playgroundUrl('/test/form-inputs'));
24+
25+
expect($page->querySelector('input[name="unchecked-checkbox"]'))->not->toBeChecked();
1226
});
1327

1428
it('fails when checkbox is not checked', function () {

0 commit comments

Comments
 (0)