Skip to content

Commit 845e7e3

Browse files
authored
Merge pull request #318 from seleniumbase/multithreaded-link-checking
Add multithreading to link checking; and other improvements
2 parents a7dda58 + 2755390 commit 845e7e3

10 files changed

+115
-11
lines changed

examples/run_my_first_test_in_chrome.sh

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/run_my_first_test_in_firefox.sh

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/run_rate_limiting_test.sh

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/run_test_fail_with_logging.sh

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/swag_labs_suite.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import pytest
2+
from parameterized import parameterized
3+
from seleniumbase import BaseCase
4+
5+
6+
class SwagLabsTests(BaseCase):
7+
8+
def login(self, user="standard_user"):
9+
""" Login to Swag Labs and assert that the login was successful. """
10+
if user not in (["standard_user", "problem_user"]):
11+
raise Exception("Invalid user!")
12+
self.open("https://www.saucedemo.com/")
13+
self.update_text("#user-name", user)
14+
self.update_text("#password", "secret_sauce")
15+
self.click('input[type="submit"]')
16+
self.assert_text("Products", "div.product_label")
17+
self.assert_element("#inventory_container")
18+
19+
@parameterized.expand([
20+
["standard_user"],
21+
["problem_user"],
22+
])
23+
@pytest.mark.run(order=1)
24+
def test_swag_labs_basic_functional_flow(self, user):
25+
""" This test checks for basic functional flow in the Swag Labs store.
26+
The test is parameterized, and receives the user to use for login.
27+
"""
28+
self.login(user)
29+
30+
# Verify that the "Test.allTheThings() T-Shirt" appears on the page
31+
item_name = "Test.allTheThings() T-Shirt"
32+
self.assert_text(item_name)
33+
34+
# Verify that a reverse-alphabetical sort works as expected
35+
self.select_option_by_value("select.product_sort_container", "za")
36+
if item_name not in self.get_text("div.inventory_item"):
37+
raise Exception('Sort Failed! Expecting "%s" on top!' % item_name)
38+
39+
# Add the "Test.allTheThings() T-Shirt" to the cart
40+
self.assert_exact_text("ADD TO CART", "button.btn_inventory")
41+
item_price = self.get_text("div.inventory_item_price")
42+
self.click("button.btn_inventory")
43+
self.assert_exact_text("REMOVE", "button.btn_inventory")
44+
self.assert_exact_text("1", "span.shopping_cart_badge")
45+
46+
# Verify your cart
47+
self.click("#shopping_cart_container path")
48+
self.assert_exact_text("Your Cart", "div.subheader")
49+
self.assert_text(item_name, "div.inventory_item_name")
50+
self.assert_exact_text("1", "div.cart_quantity")
51+
self.assert_exact_text("REMOVE", "button.cart_button")
52+
self.assert_element("link=CONTINUE SHOPPING")
53+
54+
# Checkout - Add info
55+
self.click("link=CHECKOUT")
56+
self.assert_exact_text("Checkout: Your Information", "div.subheader")
57+
self.assert_element("a.cart_cancel_link")
58+
self.update_text("#first-name", "SeleniumBase")
59+
self.update_text("#last-name", "Rocks")
60+
self.update_text("#postal-code", "01720")
61+
62+
# Checkout - Overview
63+
self.click('input.btn_primary')
64+
self.assert_exact_text("Checkout: Overview", "div.subheader")
65+
self.assert_element("link=CANCEL")
66+
self.assert_text(item_name, "div.inventory_item_name")
67+
self.assert_text(item_price, "div.inventory_item_price")
68+
self.assert_exact_text("1", "div.summary_quantity")
69+
70+
# Finish Checkout and verify item is no longer in cart
71+
self.click("link=FINISH")
72+
self.assert_exact_text("THANK YOU FOR YOUR ORDER", "h2")
73+
self.assert_element("div.pony_express")
74+
self.click("#shopping_cart_container path")
75+
self.assert_element_absent("div.inventory_item_name")
76+
self.click("link=CONTINUE SHOPPING")
77+
self.assert_element_absent("span.shopping_cart_badge")
78+
79+
@parameterized.expand([
80+
["standard_user"],
81+
["problem_user"],
82+
])
83+
@pytest.mark.run(order=2)
84+
def test_swag_labs_products_page_resource_verification(self, user):
85+
""" This test checks for 404 errors on the Swag Labs products page.
86+
The test is parameterized, and receives the user to use for login.
87+
"""
88+
self.login(user)
89+
self.assert_no_404_errors()

help_docs/method_summary.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ self.get_unique_links()
176176

177177
self.get_link_status_code(link, allow_redirects=False, timeout=5)
178178

179-
self.assert_no_404_errors()
179+
self.assert_link_status_code_is_not_404(link)
180+
181+
self.assert_no_404_errors(multithreaded=True)
180182

181183
self.print_unique_links_with_status_codes()
182184

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pytest-html>=1.20.0
1515
pytest-metadata>=1.8.0
1616
pytest-rerunfailures>=7.0
1717
pytest-xdist>=1.28.0
18+
pytest-ordering>=0.6
1819
parameterized>=0.7.0
1920
beautifulsoup4>=4.6.0
2021
colorama==0.4.1

seleniumbase/fixtures/base_case.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,14 +1640,24 @@ def get_link_status_code(self, link, allow_redirects=False, timeout=5):
16401640
link, allow_redirects=allow_redirects, timeout=timeout)
16411641
return status_code
16421642

1643-
def assert_no_404_errors(self):
1643+
def assert_link_status_code_is_not_404(self, link):
1644+
status_code = str(self.get_link_status_code(link))
1645+
bad_link_str = 'Error: "%s" returned a 404!' % link
1646+
self.assert_not_equal(status_code, "404", bad_link_str)
1647+
1648+
def assert_no_404_errors(self, multithreaded=True):
16441649
""" Assert no 404 errors from page links obtained from:
16451650
"a"->"href", "img"->"src", "link"->"href", and "script"->"src". """
16461651
links = self.get_unique_links()
1647-
for link in links:
1648-
status_code = str(self.get_link_status_code(link))
1649-
bad_link_str = 'Error: "%s" returned a 404!' % link
1650-
self.assert_not_equal(status_code, "404", bad_link_str)
1652+
if multithreaded:
1653+
from multiprocessing.dummy import Pool as ThreadPool
1654+
pool = ThreadPool(10)
1655+
pool.map(self.assert_link_status_code_is_not_404, links)
1656+
pool.close()
1657+
pool.join()
1658+
else:
1659+
for link in links:
1660+
self.assert_link_status_code_is_not_404(link)
16511661

16521662
def print_unique_links_with_status_codes(self):
16531663
""" Finds all unique links in the html of the page source
@@ -3031,6 +3041,7 @@ def __highlight_with_assert_success(
30313041
except Exception:
30323042
# Don't highlight if can't convert to CSS_SELECTOR
30333043
return
3044+
self.__slow_scroll_to_element(element)
30343045

30353046
o_bs = '' # original_box_shadow
30363047
style = element.get_attribute('style')

seleniumbase/fixtures/page_utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ def _get_unique_links(page_url, soup):
116116
link = prefix + link
117117
elif link.startswith('/'):
118118
link = full_base_url + link
119+
elif link.startswith('./'):
120+
link = full_base_url + link[1:]
119121
elif link.startswith('#'):
120122
link = full_base_url + link
123+
elif '//' not in link:
124+
link = full_base_url + "/" + link
121125
else:
122126
pass
123127
unique_links.append(link)

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
setup(
1919
name='seleniumbase',
20-
version='1.23.2',
20+
version='1.23.3',
2121
description='Reliable Browser Automation & Testing Framework',
2222
long_description=long_description,
2323
long_description_content_type='text/markdown',
@@ -68,6 +68,7 @@
6868
'pytest-metadata>=1.8.0',
6969
'pytest-rerunfailures>=7.0',
7070
'pytest-xdist>=1.28.0',
71+
'pytest-ordering>=0.6',
7172
'parameterized>=0.7.0',
7273
'beautifulsoup4>=4.6.0', # Keep at >=4.6.0 while using bs4
7374
'colorama==0.4.1',

0 commit comments

Comments
 (0)