Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a850f81

Browse files
Merge branch '4-page-objects' into 5-playwright-tricks
2 parents 7be2074 + 3ca9472 commit a850f81

File tree

9 files changed

+214
-182
lines changed

9 files changed

+214
-182
lines changed

‎pages/result.py‎

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,22 @@
33
the page object for the DuckDuckGo result page.
44
"""
55

6+
from playwright.sync_api import Page
7+
from typing import List
8+
9+
610
class DuckDuckGoResultPage:
711

8-
RESULT_LINKS = '.result__title a.result__a'
9-
SEARCH_INPUT = '#search_form_input'
10-
11-
def __init__(self, page):
12+
def __init__(self, page: Page) -> None:
1213
self.page = page
14+
self.result_links = page.locator('.result__title a.result__a')
15+
self.search_input = page.locator('#search_form_input')
1316

14-
def result_link_titles(self):
15-
self.page.locator(f'{self.RESULT_LINKS} >> nth=4').wait_for()
16-
titles = self.page.locator(self.RESULT_LINKS).all_text_contents()
17-
return titles
17+
def result_link_titles(self) -> List[str]:
18+
self.result_links.nth(4).wait_for()
19+
return self.result_links.all_text_contents()
1820

19-
def result_link_titles_contain_phrase(self, phrase, minimum=1):
21+
def result_link_titles_contain_phrase(self, phrase: str, minimum: int=1) ->bool:
2022
titles = self.result_link_titles()
2123
matches = [t for t in titles if phrase.lower() in t.lower()]
2224
return len(matches) >= minimum
23-
24-
def search_input_value(self):
25-
return self.page.input_value(self.SEARCH_INPUT)
26-
27-
def title(self):
28-
return self.page.title()

‎pages/search.py‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@
33
the page object for the DuckDuckGo search page.
44
"""
55

6-
class DuckDuckGoSearchPage:
6+
from playwright.sync_api import Page
7+
78

8-
SEARCH_BUTTON = '#search_button_homepage'
9-
SEARCH_INPUT = '#search_form_input_homepage'
9+
class DuckDuckGoSearchPage:
1010

1111
URL = 'https://www.duckduckgo.com'
1212

13-
def __init__(self, page):
13+
def __init__(self, page: Page) ->None:
1414
self.page = page
15+
self.search_button = page.locator('#search_button_homepage')
16+
self.search_input = page.locator('#search_form_input_homepage')
1517

16-
def load(self):
18+
def load(self)->None:
1719
self.page.goto(self.URL)
1820

19-
def search(self, phrase):
20-
self.page.fill(self.SEARCH_INPUT, phrase)
21-
self.page.click(self.SEARCH_BUTTON)
21+
def search(self, phrase: str) ->None:
22+
self.search_input.fill(phrase)
23+
self.search_button.click()

‎tests/conftest.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66

77
from pages.result import DuckDuckGoResultPage
88
from pages.search import DuckDuckGoSearchPage
9+
from playwright.sync_api import Page
10+
911

1012

1113
@pytest.fixture
12-
def result_page(page):
14+
def result_page(page: Page) ->DuckDuckGoResultPage:
1315
return DuckDuckGoResultPage(page)
1416

1517

1618
@pytest.fixture
17-
def search_page(page):
19+
def search_page(page: Page) ->DuckDuckGoSearchPage:
1820
return DuckDuckGoSearchPage(page)

‎tests/test_search.py‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
import pytest
66

7+
from pages.result import DuckDuckGoResultPage
8+
from pages.search import DuckDuckGoSearchPage
9+
from playwright.sync_api import expect, Page
10+
711

812
ANIMALS = [
913
'panda',
@@ -20,19 +24,23 @@
2024

2125

2226
@pytest.mark.parametrize('phrase', ANIMALS)
23-
def test_basic_duckduckgo_search(search_page, result_page, phrase):
24-
27+
def test_basic_duckduckgo_search(
28+
phrase: str,
29+
page: Page,
30+
search_page: DuckDuckGoSearchPage,
31+
result_page: DuckDuckGoResultPage) -> None:
32+
2533
# Given the DuckDuckGo home page is displayed
2634
search_page.load()
2735

2836
# When the user searches for a phrase
2937
search_page.search(phrase)
3038

3139
# Then the search result query is the phrase
32-
assertphrase==result_page.search_input_value()
40+
expect(result_page.search_input).to_have_value(phrase)
3341

3442
# And the search result links pertain to the phrase
3543
assert result_page.result_link_titles_contain_phrase(phrase)
3644

3745
# And the search result title contains the phrase
38-
assertphraseinresult_page.title()
46+
expect(page).to_have_title(f'{phrase} at DuckDuckGo')

‎tutorial/1-getting-started.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ $ touch tests/test_search.py
162162
Add the following code to `tests/test_search.py`:
163163

164164
```python
165-
def test_basic_duckduckgo_search():
165+
def test_basic_duckduckgo_search() -> None:
166166
# Given the DuckDuckGo home page is displayed
167167
# When the user searches for a phrase
168168
# Then the search result query is the phrase

‎tutorial/2-first-steps.md‎

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,22 @@ You do not need to explicitly close the browser.
5858
> Asynchronous calls could be useful for other types of automation, such as web scraping.
5959
6060
Let's update our test stub to call the `page` fixture.
61-
In `tests/test_search.py`, change the test function signature from this:
61+
In `tests/test_search.py`, add the following import statement:
6262

6363
```python
64-
def test_basic_duckduckgo_search():
64+
from playwright.sync_api import Page
65+
```
66+
67+
Then, change the test function signature from this:
68+
69+
```python
70+
def test_basic_duckduckgo_search() -> None:
6571
```
6672

6773
To this:
6874

6975
```python
70-
def test_basic_duckduckgo_search(page):
76+
def test_basic_duckduckgo_search(page: Page) -> None:
7177
```
7278

7379
Now the test has access to a fresh page in a new browser context.
@@ -172,22 +178,32 @@ Here's the inspection panel for the search input element:
172178
Thankfully, this element has an ID.
173179
We can use the selector `#search_form_input_homepage` to uniquely identify this element.
174180

175-
To enter text into this input element, we must use Playwright's
176-
[`fill`](https://playwright.dev/python/docs/api/class-page#page-fill) method.
181+
To interact with elements with Playwright, we must use [locators](https://playwright.dev/python/docs/locators).
182+
The [Locator](https://playwright.dev/python/docs/next/api/class-locator) class
183+
takes in a selector and produces an object that can interact with the target element.
184+
185+
For example, to enter text into this input element, we must use `Locator`'s
186+
[`fill`](https://playwright.dev/python/docs/next/api/class-locator#locator-fill) method.
177187
Append the following line to the test case:
178188

179189
```python
180-
page.fill('#search_form_input_homepage', 'panda')
190+
page.locator('#search_form_input_homepage').fill('panda')
181191
```
182192

183193
> Since `search_form_input_homepage` is an ID, we could also use Playwright's
184194
> [ID attribute selector](https://playwright.dev/python/docs/selectors#id-data-testid-data-test-id-data-test-selectors):
185195
>
186-
> `page.fill('id=search_form_input_homepage', 'panda')`
196+
> `page.locator('id=search_form_input_homepage').fill('panda')`
187197
188-
Using Selenium WebDriver, we would need to locate the element and then send the interaction to it.
189-
However, in Playwright, these two parts are combined into a single call.
190-
Furthermore, Playwright waits for the target element to be visible and editable before it attempts to enter the text.
198+
> Playwright's `Page` class also provides methods for element interactions like this:
199+
>
200+
> `page.fill('#search_form_input_homepage', 'panda')`
201+
>
202+
> However, using locators is recommended over direct page calls.
203+
> Locators use "strict" mode - a locator raises an exception if its selector finds more than one element.
204+
> Locators are also more reusable, especially when using page object classes.
205+
206+
Playwright waits for the target element to be visible and editable before it attempts to enter the text.
191207
We are arbitrarily using the phrase `'panda'` as our search phrase because, well, why not?
192208

193209
Let's handle the second part of the interaction: clicking the search button.
@@ -198,11 +214,11 @@ Here's the inspection panel for the search button:
198214
This element also has an ID: `#search_button_homepage`. Nice!
199215

200216
To click an element, we must use Playwright's
201-
[`click`](https://playwright.dev/python/docs/api/class-page#page-click) method.
217+
[`click`](https://playwright.dev/python/docs/next/api/class-locator#locator-click) method.
202218
Append the following line to the test case:
203219

204220
```python
205-
page.click('#search_button_homepage')
221+
page.locator('#search_button_homepage').click()
206222
```
207223

208224
Again, Playwright is nice and concise.
@@ -211,14 +227,15 @@ The `click` method waits for the target element to be ready to receive clicks, t
211227
Our test case should now look like this:
212228

213229
```python
214-
deftest_basic_duckduckgo_search(page):
230+
from playwright.sync_api import Page
215231

232+
def test_basic_duckduckgo_search(page: Page) -> None:
216233
# Given the DuckDuckGo home page is displayed
217234
page.goto('https://www.duckduckgo.com')
218235

219236
# When the user searches for a phrase
220-
page.fill('#search_form_input_homepage', 'panda')
221-
page.click('#search_button_homepage')
237+
page.locator('#search_form_input_homepage').fill('panda')
238+
page.locator('#search_button_homepage').click()
222239

223240
# Then the search result query is the phrase
224241
# And the search result links pertain to the phrase
@@ -231,5 +248,7 @@ Now, you should see the test actually perform the search!
231248

232249
Navigation, input filling, and clicking are only three of many page interactions you can do with Playwright.
233250
Anything a user can do on a web page, Playwright can do as well.
234-
Check out the Playwright [Page](https://playwright.dev/python/docs/api/class-page) API to see *all* methods and attributes.
251+
Check out the Playwright [Page](https://playwright.dev/python/docs/api/class-page)
252+
and [Locator](https://playwright.dev/python/docs/next/api/class-locator) classes
253+
to see *all* methods and attributes.
235254
We will use more of these calls in the next tutorial parts.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /