Skip to content

Commit 6f2fabe

Browse files
authored
Merge pull request #1389 from seleniumbase/prefer-using-js-over-jquery
Prefer JS over jQuery, optimize timings, and refresh example tests
2 parents 57171f2 + 4dbf368 commit 6f2fabe

File tree

10 files changed

+225
-63
lines changed

10 files changed

+225
-63
lines changed

examples/boilerplates/samples/test_page_objects.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
""" Example test that uses the Page Object Model """
2-
1+
""" An example test using the Classic Page Object Model """
32
from seleniumbase import BaseCase
43

54

@@ -24,8 +23,6 @@ def click_seleniumbase_io_link(self, sb):
2423

2524
class SeleniumBaseIOPage:
2625
def do_search_and_click(self, sb, search_term):
27-
if sb.is_element_visible('[for="__search"] svg'):
28-
sb.click('[for="__search"] svg')
2926
sb.type('form[name="search"] input', search_term)
3027
sb.click("li.md-search-result__item h1:contains(%s)" % search_term)
3128

examples/github_test.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ def test_github(self):
1111
self.slow_click('a[href="/seleniumbase/SeleniumBase"]')
1212
self.click_if_visible('[data-action="click:signup-prompt#dismiss"]')
1313
self.assert_element("div.repository-content")
14-
self.assert_text("SeleniumBase", "h2 strong")
14+
self.assert_text("SeleniumBase", "strong a")
1515
self.slow_click('a[title="seleniumbase"]')
1616
self.slow_click('a[title="fixtures"]')
17-
self.slow_click('a[title="base_case.py"]')
18-
self.assert_text("Code", "nav a.selected")
17+
self.assert_element('a[title="base_case.py"]')

examples/test_download_files.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,8 @@ def test_download_chromedriver_notes(self):
1919
text = "Switching to nested frame fails with chrome/chromedriver 100"
2020
self.assert_true(text in notes_data) # Verify file has expected data
2121

22-
def test_download_files_from_pypi_with_edge(self):
23-
if self.browser != "edge":
24-
self.open("data:,")
25-
print("\n This test is only for Microsoft Edge (Chromium)!")
26-
print(' (Run this test using "--edge" or "--browser=edge")')
27-
self.skip('Use "--edge" or "--browser=edge"')
28-
29-
self.open("https://pypi.org/project/seleniumbase/#files")
22+
def test_download_files_from_pypi(self):
23+
self.open("https://pypi.org/project/sbvirtualdisplay/#files")
3024
pkg_header = self.get_text("h1.package-header__name").strip()
3125
pkg_name = pkg_header.replace(" ", "-")
3226
whl_file = pkg_name + "-py2.py3-none-any.whl"

examples/test_usefixtures.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ def test_usefixtures_on_class(self):
1212
sb.assert_text("integrations")
1313
sb.assert_element('a[title="help_docs"]')
1414
sb.click('a[title="examples"]')
15+
sb.assert_exact_text("examples", "strong.final-path")

examples/tour_examples/ReadMe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ from seleniumbase import BaseCase
105105
class MyTourClass(BaseCase):
106106

107107
def test_google_tour(self):
108-
self.open('https://google.com')
108+
self.open('https://google.com/ncr')
109109
self.wait_for_element('input[title="Search"]')
110110

111111
self.create_tour(theme="dark")

examples/wordle_archive_test.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
""" Solve the Wordle game using SeleniumBase.
2+
This test runs on archived versions of Wordle, containing Shadow-DOM. """
3+
4+
import ast
5+
import random
6+
import requests
7+
from seleniumbase import version_info
8+
from seleniumbase import BaseCase
9+
10+
11+
class WordleTests(BaseCase):
12+
13+
word_list = []
14+
15+
def initialize_word_list(self):
16+
txt_file = "https://seleniumbase.io/cdn/txt/wordle_words.txt"
17+
word_string = requests.get(txt_file).text
18+
self.word_list = ast.literal_eval(word_string)
19+
20+
def modify_word_list(self, word, letter_status):
21+
new_word_list = []
22+
correct_letters = []
23+
present_letters = []
24+
for i in range(len(word)):
25+
if letter_status[i] == "correct":
26+
correct_letters.append(word[i])
27+
for w in self.word_list:
28+
if w[i] == word[i]:
29+
new_word_list.append(w)
30+
self.word_list = new_word_list
31+
new_word_list = []
32+
for i in range(len(word)):
33+
if letter_status[i] == "present":
34+
present_letters.append(word[i])
35+
for w in self.word_list:
36+
if word[i] in w and word[i] != w[i]:
37+
new_word_list.append(w)
38+
self.word_list = new_word_list
39+
new_word_list = []
40+
for i in range(len(word)):
41+
if (
42+
letter_status[i] == "absent"
43+
and word[i] not in correct_letters
44+
and word[i] not in present_letters
45+
):
46+
for w in self.word_list:
47+
if word[i] not in w:
48+
new_word_list.append(w)
49+
self.word_list = new_word_list
50+
new_word_list = []
51+
52+
def skip_if_incorrect_env(self):
53+
if self.headless:
54+
message = "This test doesn't run in headless mode!"
55+
print(message)
56+
self.skip(message)
57+
if version_info < [2, 4, 4]:
58+
message = "This test requires SeleniumBase 2.4.4 or newer!"
59+
print(message)
60+
self.skip(message)
61+
62+
def test_wordle(self):
63+
self.skip_if_incorrect_env()
64+
random.seed()
65+
year = "2022"
66+
month = random.randint(3, 5)
67+
day = random.randint(1, 30)
68+
date = str(year) + "0" + str(month) + str(day)
69+
archive = "https://web.archive.org/web/"
70+
url = "https://www.nytimes.com/games/wordle/index.html"
71+
past_wordle = archive + date + "/" + url
72+
print(past_wordle)
73+
self.open(past_wordle)
74+
self.click("game-app::shadow game-modal::shadow game-icon")
75+
self.initialize_word_list()
76+
keyboard_base = "game-app::shadow game-keyboard::shadow "
77+
word = random.choice(self.word_list)
78+
total_attempts = 0
79+
success = False
80+
for attempt in range(6):
81+
total_attempts += 1
82+
word = random.choice(self.word_list)
83+
letters = []
84+
for letter in word:
85+
letters.append(letter)
86+
button = 'button[data-key="%s"]' % letter
87+
self.click(keyboard_base + button)
88+
button = "button.one-and-a-half"
89+
self.click(keyboard_base + button)
90+
row = 'game-app::shadow game-row[letters="%s"]::shadow ' % word
91+
tile = row + "game-tile:nth-of-type(%s)"
92+
self.wait_for_element(tile % "5" + '::shadow [data-state*="e"]')
93+
letter_status = []
94+
for i in range(1, 6):
95+
letter_eval = self.get_attribute(tile % str(i), "evaluation")
96+
letter_status.append(letter_eval)
97+
if letter_status.count("correct") == 5:
98+
success = True
99+
break
100+
self.word_list.remove(word)
101+
self.modify_word_list(word, letter_status)
102+
103+
self.save_screenshot_to_logs()
104+
print('\nWord: "%s"\nAttempts: %s' % (word.upper(), total_attempts))
105+
if not success:
106+
self.fail("Unable to solve for the correct word in 6 attempts!")
107+
self.sleep(3)

examples/wordle_test.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
""" Solve the Wordle game using SeleniumBase. """
1+
""" Solve the Wordle game using SeleniumBase.
2+
The latest version of Wordle no longer uses Shadow-DOM. """
23

34
import ast
45
import random
56
import requests
6-
from seleniumbase import __version__
77
from seleniumbase import BaseCase
88

99

@@ -53,18 +53,12 @@ def skip_if_incorrect_env(self):
5353
message = "This test doesn't run in headless mode!"
5454
print(message)
5555
self.skip(message)
56-
version = [int(i) for i in __version__.split(".") if i.isdigit()]
57-
if version < [2, 4, 4]:
58-
message = "This test requires SeleniumBase 2.4.4 or newer!"
59-
print(message)
60-
self.skip(message)
6156

6257
def test_wordle(self):
6358
self.skip_if_incorrect_env()
6459
self.open("https://www.nytimes.com/games/wordle/index.html")
65-
self.click("game-app::shadow game-modal::shadow game-icon")
60+
self.click('svg[data-testid="icon-close"]')
6661
self.initialize_word_list()
67-
keyboard_base = "game-app::shadow game-keyboard::shadow "
6862
word = random.choice(self.word_list)
6963
total_attempts = 0
7064
success = False
@@ -75,15 +69,18 @@ def test_wordle(self):
7569
for letter in word:
7670
letters.append(letter)
7771
button = 'button[data-key="%s"]' % letter
78-
self.click(keyboard_base + button)
79-
button = "button.one-and-a-half"
80-
self.click(keyboard_base + button)
81-
row = 'game-app::shadow game-row[letters="%s"]::shadow ' % word
82-
tile = row + "game-tile:nth-of-type(%s)"
83-
self.wait_for_element(tile % "5" + '::shadow [data-state*="e"]')
72+
self.click(button)
73+
button = 'button[class*="oneAndAHalf"]'
74+
self.click(button)
75+
row = (
76+
'div[class*="lbzlf"] div[class*="Row-module"]:nth-of-type(%s) '
77+
% total_attempts
78+
)
79+
tile = row + 'div:nth-child(%s) div[class*="module_tile__3ayIZ"]'
80+
self.wait_for_element(tile % "5" + '[data-state*="e"]')
8481
letter_status = []
8582
for i in range(1, 6):
86-
letter_eval = self.get_attribute(tile % str(i), "evaluation")
83+
letter_eval = self.get_attribute(tile % str(i), "data-state")
8784
letter_status.append(letter_eval)
8885
if letter_status.count("correct") == 5:
8986
success = True

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "3.3.4"
2+
__version__ = "3.3.5"

seleniumbase/fixtures/base_case.py

Lines changed: 88 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5036,52 +5036,119 @@ def hide_element(self, selector, by="css selector"):
50365036
"""Hide the first element on the page that matches the selector."""
50375037
self.__check_scope()
50385038
selector, by = self.__recalculate_selector(selector, by)
5039-
selector = self.convert_to_css_selector(selector, by=by)
5040-
selector = self.__make_css_match_first_element_only(selector)
5041-
hide_script = """jQuery('%s').hide();""" % selector
5042-
self.safe_execute_script(hide_script)
5039+
css_selector = self.convert_to_css_selector(selector, by=by)
5040+
if ":contains(" in css_selector:
5041+
selector = self.__make_css_match_first_element_only(css_selector)
5042+
script = """jQuery('%s').hide();""" % selector
5043+
self.safe_execute_script(script)
5044+
else:
5045+
css_selector = re.escape(css_selector) # Add "\\" to special chars
5046+
css_selector = self.__escape_quotes_if_needed(css_selector)
5047+
script = (
5048+
'const e = document.querySelector("%s");'
5049+
'e.style.display="none";e.style.visibility="hidden";'
5050+
% css_selector)
5051+
self.execute_script(script)
50435052

50445053
def hide_elements(self, selector, by="css selector"):
50455054
"""Hide all elements on the page that match the selector."""
50465055
self.__check_scope()
50475056
selector, by = self.__recalculate_selector(selector, by)
5048-
selector = self.convert_to_css_selector(selector, by=by)
5049-
hide_script = """jQuery('%s').hide();""" % selector
5050-
self.safe_execute_script(hide_script)
5057+
css_selector = self.convert_to_css_selector(selector, by=by)
5058+
if ":contains(" in css_selector:
5059+
script = """jQuery('%s').hide();""" % css_selector
5060+
self.safe_execute_script(script)
5061+
else:
5062+
css_selector = re.escape(css_selector) # Add "\\" to special chars
5063+
css_selector = self.__escape_quotes_if_needed(css_selector)
5064+
script = (
5065+
"""var $elements = document.querySelectorAll('%s');
5066+
var index = 0, length = $elements.length;
5067+
for(; index < length; index++){
5068+
$elements[index].style.display="none";
5069+
$elements[index].style.visibility="hidden";}"""
5070+
% css_selector
5071+
)
5072+
self.execute_script(script)
50515073

50525074
def show_element(self, selector, by="css selector"):
50535075
"""Show the first element on the page that matches the selector."""
50545076
self.__check_scope()
50555077
selector, by = self.__recalculate_selector(selector, by)
5056-
selector = self.convert_to_css_selector(selector, by=by)
5057-
selector = self.__make_css_match_first_element_only(selector)
5058-
show_script = """jQuery('%s').show(0);""" % selector
5059-
self.safe_execute_script(show_script)
5078+
css_selector = self.convert_to_css_selector(selector, by=by)
5079+
if ":contains(" in css_selector:
5080+
selector = self.__make_css_match_first_element_only(css_selector)
5081+
script = """jQuery('%s').show(0);""" % selector
5082+
self.safe_execute_script(script)
5083+
else:
5084+
css_selector = re.escape(css_selector) # Add "\\" to special chars
5085+
css_selector = self.__escape_quotes_if_needed(css_selector)
5086+
script = (
5087+
'const e = document.querySelector("%s");'
5088+
'e.style.display="";e.style.visibility="visible";'
5089+
% css_selector
5090+
)
5091+
self.execute_script(script)
50605092

50615093
def show_elements(self, selector, by="css selector"):
50625094
"""Show all elements on the page that match the selector."""
50635095
self.__check_scope()
50645096
selector, by = self.__recalculate_selector(selector, by)
5065-
selector = self.convert_to_css_selector(selector, by=by)
5066-
show_script = """jQuery('%s').show(0);""" % selector
5067-
self.safe_execute_script(show_script)
5097+
css_selector = self.convert_to_css_selector(selector, by=by)
5098+
if ":contains(" in css_selector:
5099+
script = """jQuery('%s').show(0);""" % css_selector
5100+
self.safe_execute_script(script)
5101+
else:
5102+
css_selector = re.escape(css_selector) # Add "\\" to special chars
5103+
css_selector = self.__escape_quotes_if_needed(css_selector)
5104+
script = (
5105+
"""var $elements = document.querySelectorAll('%s');
5106+
var index = 0, length = $elements.length;
5107+
for(; index < length; index++){
5108+
$elements[index].style.display="";
5109+
$elements[index].style.visibility="visible";}"""
5110+
% css_selector
5111+
)
5112+
self.execute_script(script)
50685113

50695114
def remove_element(self, selector, by="css selector"):
50705115
"""Remove the first element on the page that matches the selector."""
50715116
self.__check_scope()
50725117
selector, by = self.__recalculate_selector(selector, by)
5073-
selector = self.convert_to_css_selector(selector, by=by)
5074-
selector = self.__make_css_match_first_element_only(selector)
5075-
remove_script = """jQuery('%s').remove();""" % selector
5076-
self.safe_execute_script(remove_script)
5118+
css_selector = self.convert_to_css_selector(selector, by=by)
5119+
if ":contains(" in css_selector:
5120+
selector = self.__make_css_match_first_element_only(css_selector)
5121+
script = """jQuery('%s').remove();""" % selector
5122+
self.safe_execute_script(script)
5123+
else:
5124+
css_selector = re.escape(css_selector) # Add "\\" to special chars
5125+
css_selector = self.__escape_quotes_if_needed(css_selector)
5126+
script = (
5127+
'const e = document.querySelector("%s");'
5128+
'e.parentElement.removeChild(e);'
5129+
% css_selector
5130+
)
5131+
self.execute_script(script)
50775132

50785133
def remove_elements(self, selector, by="css selector"):
50795134
"""Remove all elements on the page that match the selector."""
50805135
self.__check_scope()
50815136
selector, by = self.__recalculate_selector(selector, by)
5082-
selector = self.convert_to_css_selector(selector, by=by)
5083-
remove_script = """jQuery('%s').remove();""" % selector
5084-
self.safe_execute_script(remove_script)
5137+
css_selector = self.convert_to_css_selector(selector, by=by)
5138+
if ":contains(" in css_selector:
5139+
script = """jQuery('%s').remove();""" % css_selector
5140+
self.safe_execute_script(script)
5141+
else:
5142+
css_selector = re.escape(css_selector) # Add "\\" to special chars
5143+
css_selector = self.__escape_quotes_if_needed(css_selector)
5144+
script = (
5145+
"""var $elements = document.querySelectorAll('%s');
5146+
var index = 0, length = $elements.length;
5147+
for(; index < length; index++){
5148+
$elements[index].remove();}"""
5149+
% css_selector
5150+
)
5151+
self.execute_script(script)
50855152

50865153
def ad_block(self):
50875154
"""Block ads that appear on the current web page."""

0 commit comments

Comments
 (0)