@@ -74,7 +74,124 @@ The test should still pass.
74
74
75
75
## Checking the result links
76
76
77
- TBD
77
+ The next test step is, "And the search result links pertain to the phrase".
78
+ It makes sure that the result links returned actually relate to the search phrase entered.
79
+ An easy way to check this is to make sure the phrase appears in some of the result link titles.
80
+ This will only work for simple phrases (one or two words with no punctuation),
81
+ and not every result link may have a match.
82
+ Nevertheless, our test is a basic search test,
83
+ so its scope should cover only the most basic aspects of searching behavior.
84
+ We would need to write other, more advanced tests if we wanted to use sharper assertions.
85
+
86
+ Typically, elements in a list share a common DOM structure and common CSS classes.
87
+ Here's the inspection panel for result links:
88
+
89
+ ![ Inspecting the result link elements] ( images/inspect-result-links.png )
90
+
91
+ Result links are ` a ` elements with the class ` result__a ` .
92
+ They are under ` h2 ` elements with the class ` result__title ` .
93
+ We could use the selector ` .result__title a.result__a ` to identify all result links on the page.
94
+ (If you look in the DevTools search bar, you'll see that this selector locates 10 elements.)
95
+
96
+ Since we can get all elements with one selector,
97
+ we can take the following steps to verify that search result links pertain to the phrase:
98
+
99
+ 1 . Wait for the first few result links to appear on the page.
100
+ 2 . Scrape the text contents of the result link titles.
101
+ 3 . Filter the titles that contain the search phrase.
102
+ 4 . Verify that the list of filtered titles is nonempty.
103
+
104
+ In this case, we * must* do explicit waiting for multiple result links to appear.
105
+ If we try to get text contents without waiting for all targets to appear, then some (or all) might be left out!
106
+ This risk is small, but it is nonzero.
107
+ Always safely mitigate potential race conditions with proper waiting.
108
+
109
+ Explicit waiting will be tricky.
110
+ Add the following line to the test:
111
+
112
+ ``` python
113
+ page.locator(' .result__title a.result__a >> nth=4' ).wait_for()
114
+ ```
115
+
116
+ Let's break this down:
117
+
118
+ 1 . [ ` locator ` ] ( https://playwright.dev/python/docs/api/class-page#page-locator ) is a method that returns a
119
+ [ ` Locator ` ] ( https://playwright.dev/python/docs/api/class-locator ) object for the target element.
120
+ A ` Locator ` object can make many of the same calls as a page, like clicking and getting text.
121
+ However, it can also make calls for explicit waiting and calls that target multiple elements.
122
+ 2 . ` .result__title a.result__a ` is the selector for the result links.
123
+ 3 . ` >> nth=4 ` is an [ N-th element selector] ( https://playwright.dev/python/docs/selectors#n-th-element-selector ) .
124
+ N-th element selectors are zero-indexed and may be appended to any selector.
125
+ In this ` locator ` call, it will fetch the fifth result link element.
126
+ 4 . [ ` wait_for ` ] ( https://playwright.dev/python/docs/api/class-locator#locator-wait-for )
127
+ is a method that will wait for the target element to be visible.
128
+
129
+ In summary, this line will wait for the fifth result link to become visible on the page.
130
+ Why check for the fifth?
131
+ Most DuckDuckGo searches return ten links, but sometimes, they return fewer.
132
+ Waiting for five links to appear should be good enough for our testing purposes.
133
+
134
+ After the links appear, we can scrape their text contents like this:
135
+
136
+ ``` python
137
+ titles = page.locator(' .result__title a.result__a' ).all_text_contents()
138
+ ```
139
+
140
+ Again, we must use the ` locator ` method because we want to target a list of elements instead of one.
141
+ This selector does * not* include the N-th element selector because we want to target all result links.
142
+ The [ ` all_text_contents ` ] ( https://playwright.dev/python/docs/api/class-locator#locator-all-text-contents ) method
143
+ returns a list containing the text content of each located element.
144
+ After this call, ` titles ` will be a list of strings of the titles we need to check!
145
+
146
+ Next, we can filter the list of titles to find the ones that contain the search phrase.
147
+ For proper comparisons, the titles and the search phrase should all be lowercase.
148
+ Add this line to the test:
149
+
150
+ ``` python
151
+ matches = [t for t in titles if ' panda' in t.lower()]
152
+ ```
153
+
154
+ This is a Python [ list comprehension] ( https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions ) .
155
+ Basically, it filters ` titles ` for elements that contain the search phrase.
156
+ If the lowercase version of a title contains the search phrase, then it is added to ` matches ` .
157
+ Otherwise, it is omitted.
158
+
159
+ In the best case, all titles will match.
160
+ However, in the real world, some titles may not include the full search phrase.
161
+ Since ours is merely a basic search test, it should verify that at least one title matches.
162
+ Add this assertion to the test:
163
+
164
+ ``` python
165
+ assert len (matches) > 0
166
+ ```
167
+
168
+ The full test case should now look like this:
169
+
170
+ ``` python
171
+ def test_basic_duckduckgo_search (page ):
172
+
173
+ # Given the DuckDuckGo home page is displayed
174
+ page.goto(' https://www.duckduckgo.com' )
175
+
176
+ # When the user searches for a phrase
177
+ page.fill(' #search_form_input_homepage' , ' panda' )
178
+ page.click(' #search_button_homepage' )
179
+
180
+ # Then the search result query is the phrase
181
+ assert ' panda' == page.input_value(' #search_form_input' )
182
+
183
+ # And the search result links pertain to the phrase
184
+ page.locator(' .result__title a.result__a >> nth=4' ).wait_for()
185
+ titles = page.locator(' .result__title a.result__a' ).all_text_contents()
186
+ matches = [t for t in titles if ' panda' in t.lower()]
187
+ assert len (matches) > 0
188
+
189
+ # And the search result title contains the phrase
190
+ pass
191
+ ```
192
+
193
+ This step had the most complex code so far, but it still wasn't too bad.
194
+ Rerun the test to make sure things still pass.
78
195
79
196
80
197
## Checking the title
0 commit comments