Skip to content

Commit 456fe24

Browse files
Added Part 2 to workshop folder
1 parent e483849 commit 456fe24

File tree

4 files changed

+223
-0
lines changed

4 files changed

+223
-0
lines changed

workshop/2-first-steps.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# Part 2: First steps with Playwright
2+
3+
Part 2 of the workshop shows how to take your first steps with Playwright calls.
4+
It will explain browsers, contexts, and pages.
5+
It will also cover basic Playwright API calls.
6+
7+
8+
## Browsers, contexts, and pages
9+
10+
Before we can automate interactions using Playwright's API, we must first understand how Playwright interacts with browsers.
11+
There are three main layers to automation: *browsers*, *browser contexts*, and *pages*:
12+
13+
1. A [browser](https://playwright.dev/python/docs/browsers/)
14+
is a single instance of a web browser.
15+
Playwright will automatically launch a browser instance specified by code or by inputs.
16+
Typically, this is either the Chromium, Firefox, or WebKit instance installed via `playwright install`,
17+
but it may also be other browsers installed on your local machine.
18+
2. A [browser context](https://playwright.dev/python/docs/browser-contexts/)
19+
is an isolated incognito-alike session within a browser instance.
20+
They are fast and cheap to create.
21+
One browser may have multiple browser contexts.
22+
The recommended practice is for all tests to share one browser instance but for each test to have its own browser context.
23+
3. A [page](https://playwright.dev/python/docs/pages/)
24+
is a single tab or window within a browser context.
25+
A browser context may have multiple pages.
26+
Typically, an individual test should interact with only one page.
27+
28+
Below is a diagram illustrating how these three pieces work together:
29+
30+
![Browser Diagram](images/browser-diagram.png)
31+
32+
Typically, we would need the following Playwright calls to set up a browser, browser context, and page:
33+
34+
```python
35+
from playwright.sync_api import sync_playwright
36+
37+
with sync_playwright() as p:
38+
browser = p.chromium.launch()
39+
context = browser.new_context()
40+
page = context.new_page()
41+
```
42+
43+
However, the `pytest-playwright` plugin takes care of these things automatically with the following fixtures:
44+
45+
* The `browser` fixture provides the browser instance launched by Playwright.
46+
* The `context` fixture provides a new browser context for a test.
47+
* The `page` fixture provides a new browser page for a test.
48+
49+
All of the Playwright calls with `pytest-playwright` use the synchronous API instead of the async API.
50+
The `browser` fixture has session scope, meaning all tests will share one browser instance.
51+
The `context` and `page` fixtures have function scope, meaning each test gets new ones.
52+
Typically, a test will only need to call the `page` fixture directly.
53+
These fixtures will also automatically clean up everything after testing is complete.
54+
You do not need to explicitly close the browser.
55+
56+
Let's update our test stub to call the `page` fixture.
57+
In `tests/test_search.py`, change the test function signature from this:
58+
59+
```python
60+
def test_basic_duckduckgo_search():
61+
```
62+
63+
To this:
64+
65+
```python
66+
def test_basic_duckduckgo_search(page):
67+
```
68+
69+
70+
## Navigating to a web page
71+
72+
Now the test has access to a fresh page in a new browser context.
73+
If we write multiple tests, each test will get its own page and context,
74+
but they will all share the same browser instance.
75+
76+
Now that we have a page, let's do something on it!
77+
Our first test step is, "Given the DuckDuckGo home page is displayed".
78+
Let's [navigate](https://playwright.dev/python/docs/navigations) to the DuckDuckGo home page like this:
79+
80+
```python
81+
def test_basic_duckduckgo_search(page):
82+
# Given the DuckDuckGo home page is displayed
83+
page.goto('https://www.duckduckgo.com')
84+
```
85+
86+
If you are familiar with Selenium WebDriver, then this command probably looks similar to the `driver.get(...)` method.
87+
However, Playwright's [`goto`](https://playwright.dev/python/docs/api/class-page#page-goto) method is more sophisticated:
88+
it waits for the page to fire the `load` event.
89+
Selenium WebDriver does not automatically wait for any event,
90+
which frequently leads to race conditions that cause flaky tests.
91+
92+
In Playwright, you can also wait for other page events like this:
93+
94+
```python
95+
def test_basic_duckduckgo_search(page):
96+
# Given the DuckDuckGo home page is displayed
97+
page.goto('https://www.duckduckgo.com', wait_until='networkidle')
98+
```
99+
100+
For our test, however, the default `load` event will suffice.
101+
102+
Let's try running our test to make sure Playwright works.
103+
Launch pytest using the following command:
104+
105+
```bash
106+
$ python3 -m pytest tests --headed --slowmo 1000
107+
```
108+
109+
This invocation has two new arguments.
110+
The first one is `--headed`.
111+
By default, Playwright runs tests in *headless* mode, in which the browser is not visibly rendered.
112+
Headless mode is faster than headed mode and thus ideal for "real" testing (like in CI).
113+
However, *headed* mode is better when developing tests so that you can see what is happening.
114+
115+
The second new argument is `--slowmo`.
116+
By default, Playwright runs interactions as fast as it can.
117+
Again, this is great for "real" testing, but it might be too fast for humans to watch when developing and debugging.
118+
The `--slowmo` option lets the caller set a hard sleep time after every Playwright call.
119+
For example, `--slowmo 1000` will pause execution for 1 second (1000 ms) after each call.
120+
This option is a much better way to slow down tests than to add `time.sleep(...)` calls everywhere!
121+
122+
When you launch pytest, Chromium should pop up, navigate to the DuckDuckGo home page, and close.
123+
Try running it with and without the `--headed` and `--slowmo` options, too.
124+
Verify that Playwright calls work and the test passes before moving on.
125+
126+
127+
## Performing a search
128+
129+
Next, let's try to interact with page elements.
130+
Playwright is able to locate any element on the page using [selectors](https://playwright.dev/python/docs/selectors).
131+
Out of the box, Playwright supports the following types of selectors:
132+
133+
* Text
134+
* CSS
135+
* XPath
136+
* N-th element
137+
* React
138+
* Vue
139+
140+
Text and CSS selectors also pierce the Shadow DOM by default!
141+
142+
In general, you should keep selectors as simple as possible.
143+
Try to stick to text, IDs, or CSS selectors.
144+
Use more complicated selectors only as necessary.
145+
146+
This workshop will not cover recommended practices for element selectors deeply.
147+
If you want to learn more about selectors,
148+
read the [Element selectors](https://playwright.dev/python/docs/selectors) page in the Playwright docs,
149+
or take the [Web Element Locator Strategies](https://testautomationu.applitools.com/web-element-locator-strategies/) course
150+
from [Test Automation University](https://testautomationu.applitools.com/).
151+
152+
The next step in our test case is, "When the user searches for a phrase".
153+
This is actually a compound interaction with two parts:
154+
155+
1. Entering text into the search input field
156+
2. Clicking the search button
157+
158+
Let's start with the first part of the interaction: entering text into the search input field.
159+
We need to find a selector for the search input.
160+
One of the best ways to find selectors is to inspect elements through Chrome DevTools.
161+
In Chrome, simply right-click any page and select "Inspect" to open DevTools.
162+
163+
Here's the inspection panel for the search input element:
164+
165+
![Inspecting the search input element](images/inspect-search-input.png)
166+
167+
Thankfully, this element has an ID.
168+
We can use the selector `#search_form_input_homepage` to uniquely identify this element.
169+
170+
To enter text into this input element, we must use Playwright's
171+
[`fill`](https://playwright.dev/python/docs/api/class-page#page-fill) method.
172+
Append the following line to the test case:
173+
174+
```python
175+
page.fill('#search_form_input_homepage', 'panda')
176+
```
177+
178+
Using Selenium WebDriver, we would need to locate the element and then send the interaction to it.
179+
However, in Playwright, these two parts are combined into a single call.
180+
We are arbitrarily using the phrase `'panda'` as our search phrase because, well, why not?
181+
182+
Let's handle the second part of the interaction: clicking the search button.
183+
Here's the inspection panel for the search button:
184+
185+
![Inspecting the search button element](images/inspect-search-button.png)
186+
187+
This element also has an ID: `#search_button_homepage`. Nice!
188+
189+
To click an element, we must use Playwright's
190+
[`click`](https://playwright.dev/python/docs/api/class-page#page-click) method.
191+
Append the following line to the test case:
192+
193+
```python
194+
page.click('#search_button_homepage')
195+
```
196+
197+
Again, Playwright is nice and concise.
198+
199+
Our test case should now look like this:
200+
201+
```python
202+
def test_basic_duckduckgo_search(page):
203+
204+
# Given the DuckDuckGo home page is displayed
205+
page.goto('https://www.duckduckgo.com')
206+
207+
# When the user searches for a phrase
208+
page.fill('#search_form_input_homepage', 'panda')
209+
page.click('#search_button_homepage')
210+
211+
# Then the search result query is the phrase
212+
# And the search result links pertain to the phrase
213+
# And the search result title contains the phrase
214+
pass
215+
```
216+
217+
Rerun the test using `python3 -m pytest tests --headed --slowmo 1000`.
218+
Now, you should see the test actually perform the search!
219+
220+
Navigation, input filling, and clicking are only three of many page interactions you can do with Playwright.
221+
Anything a user can do on a web page, Playwright can do as well.
222+
Check out the Playwright [Page](https://playwright.dev/python/docs/api/class-page) API to see *all* methods and attributes.
223+
We will use more of these calls in the next workshop parts.

workshop/images/browser-diagram.png

429 KB
Loading
1.12 MB
Loading
1.08 MB
Loading

0 commit comments

Comments
 (0)