Skip to content

Commit aa741cf

Browse files
authored
Merge pull request #158 from seleniumbase/ad-blocking-functionality
Add ad-blocking functionality
2 parents 973a152 + 0e65d7b commit aa741cf

File tree

8 files changed

+116
-9
lines changed

8 files changed

+116
-9
lines changed

help_docs/method_summary.md

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ self.remove_element(selector, by=By.CSS_SELECTOR)
114114

115115
self.remove_elements(selector, by=By.CSS_SELECTOR)
116116

117+
self.ad_block()
118+
117119
self.get_domain_url(url)
118120

119121
self.safe_execute_script(script)

seleniumbase/config/ad_block_list.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
For use with SeleniumBase ad_block functionality.
3+
4+
Usage:
5+
On the command line:
6+
"pytest SOME_TEST.py --ad_block"
7+
8+
From inside a test:
9+
self.ad_block()
10+
11+
If using the command line version, the ad_block functionality gets
12+
activated after "self.wait_for_ready_state_complete()" is called,
13+
which is always run after page loads, unless changed in "settings.py".
14+
Using ad_block will slow down test runs a little. (Use only if necessary.)
15+
16+
Format: A CSS Selector that's ready for JavaScript's querySelectorAll()
17+
"""
18+
19+
AD_BLOCK_LIST = [
20+
'[aria-label="Ad"]',
21+
'[class^="sponsored-content"]',
22+
'[data-ad-details*="Advertisement"]',
23+
'[data-native_ad*="placement"]',
24+
'[data-provider="dianomi"]',
25+
'[data-type="ad"]',
26+
'[data-track-event-label*="-taboola-"]',
27+
'[href*="doubleclick.net/"]',
28+
'[id*="-ad-"]',
29+
'[id*="_ads_"]',
30+
'[id*="AdFrame"]',
31+
'[id^="ad-"]',
32+
'[id^="outbrain_widget"]',
33+
'[id^="taboola-"]',
34+
'[id="dianomiRightRail"]',
35+
'[src*="smartads."]',
36+
'[src*="ad_nexus"]',
37+
'[src*="/ads/"]',
38+
'[data-dcm-click-tracker*="/adclick."]',
39+
'[data-google-query-id^="C"]',
40+
'div.ad-container',
41+
'div.ad_module',
42+
'div.ad-subnav-container',
43+
'div.ad-wrapper',
44+
'div.data-ad-container',
45+
'div.l-ad',
46+
'div.right-ad',
47+
'div.wx-adWrapper',
48+
'img.img_ad',
49+
'link[href*="/adservice."]',
50+
'script[src*="/adservice."]',
51+
'script[src*="/pagead/"]',
52+
'section.dianomi-ad',
53+
]

seleniumbase/config/proxy_list.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"pytest SOME_TEST.py --proxy=proxy1"
99
1010
Format of PROXY_LIST server entries:
11-
* "ip_address:port"
11+
* "ip_address:port" OR
12+
* "server:port"
13+
(Do NOT include the http:// or https:// in your proxy string!)
1214
1315
Example proxies in PROXY_LIST below are not guaranteed to be active or secure.
1416
If you don't already have a proxy server to connect to,

seleniumbase/core/log_helper.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
def log_screenshot(test_logpath, driver):
1111
screenshot_name = settings.SCREENSHOT_NAME
1212
screenshot_path = "%s/%s" % (test_logpath, screenshot_name)
13-
driver.get_screenshot_as_file(screenshot_path)
13+
try:
14+
driver.get_screenshot_as_file(screenshot_path)
15+
except Exception:
16+
print("WARNING: Unable to get screenshot for failure logs!")
1417

1518

1619
def log_test_failure_data(test, test_logpath, driver, browser):

seleniumbase/fixtures/base_case.py

+37-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def test_anything(self):
3636
from bs4 import BeautifulSoup
3737
from pyvirtualdisplay import Display
3838
from seleniumbase.common import decorators
39+
from seleniumbase.config import ad_block_list
3940
from seleniumbase.config import settings
4041
from seleniumbase.core.application_manager import ApplicationManager
4142
from seleniumbase.core.s3_manager import S3LoggingBucket
@@ -75,13 +76,15 @@ def __init__(self, *args, **kwargs):
7576
self.driver = None
7677
self.environment = None
7778
self._last_url_of_delayed_assert = "data:,"
79+
self._last_page_load_url = "data:,"
7880
self._page_check_count = 0
7981
self._page_check_failures = []
8082
self._html_report_extra = []
8183
self._default_driver = None
8284
self._drivers_list = []
8385

8486
def open(self, url):
87+
self._last_page_load_url = None
8588
self.driver.get(url)
8689
if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:
8790
self.wait_for_ready_state_complete()
@@ -411,11 +414,13 @@ def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR,
411414
selector, attribute))
412415

413416
def refresh_page(self):
417+
self._last_page_load_url = None
414418
self.driver.refresh()
419+
self.wait_for_ready_state_complete()
415420

416421
def refresh(self):
417422
""" The shorter version of self.refresh_page() """
418-
self.driver.refresh()
423+
self.refresh_page()
419424

420425
def get_current_url(self):
421426
return self.driver.current_url
@@ -431,10 +436,14 @@ def get_title(self):
431436
return self.driver.title
432437

433438
def go_back(self):
439+
self._last_page_load_url = None
434440
self.driver.back()
441+
self.wait_for_ready_state_complete()
435442

436443
def go_forward(self):
444+
self._last_page_load_url = None
437445
self.driver.forward()
446+
self.wait_for_ready_state_complete()
438447

439448
def get_image_url(self, selector, by=By.CSS_SELECTOR,
440449
timeout=settings.SMALL_TIMEOUT):
@@ -978,6 +987,19 @@ def remove_elements(self, selector, by=By.CSS_SELECTOR):
978987
remove_script = """jQuery('%s').remove()""" % selector
979988
self.safe_execute_script(remove_script)
980989

990+
def ad_block(self):
991+
for css_selector in ad_block_list.AD_BLOCK_LIST:
992+
css_selector = re.escape(css_selector)
993+
script = ("""var $elements = document.querySelectorAll('%s');
994+
var index = 0, length = $elements.length;
995+
for(; index < length; index++){
996+
$elements[index].remove();}"""
997+
% css_selector)
998+
try:
999+
self.execute_script(script)
1000+
except Exception:
1001+
pass # Don't fail test if ad_blocking fails
1002+
9811003
def jq_format(self, code):
9821004
# DEPRECATED - Use re.escape() instead, which does the action you want.
9831005
return page_utils._jq_format(code)
@@ -1077,6 +1099,8 @@ def set_value(self, selector, new_value, by=By.CSS_SELECTOR,
10771099
element = self.wait_for_element_present(
10781100
orginal_selector, by=by, timeout=timeout)
10791101
element.send_keys(Keys.RETURN)
1102+
if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:
1103+
self.wait_for_ready_state_complete()
10801104
self._demo_mode_pause_if_active()
10811105

10821106
def jquery_update_text_value(self, selector, new_value, by=By.CSS_SELECTOR,
@@ -1441,6 +1465,17 @@ def wait_for_ready_state_complete(self, timeout=settings.EXTREME_TIMEOUT):
14411465
is_ready = page_actions.wait_for_ready_state_complete(self.driver,
14421466
timeout)
14431467
self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)
1468+
if self.ad_block_on:
1469+
# If the ad_block feature is enabled, then block ads for new URLs
1470+
current_url = self.get_current_url()
1471+
if not current_url == self._last_page_load_url:
1472+
time.sleep(0.02)
1473+
self.ad_block()
1474+
time.sleep(0.01)
1475+
if self.is_element_present("iframe"):
1476+
time.sleep(0.07) # iframe ads take slightly longer to load
1477+
self.ad_block() # Do ad_block on slower-loading iframes
1478+
self._last_page_load_url = current_url
14441479
return is_ready
14451480

14461481
def wait_for_angularjs(self, timeout=settings.LARGE_TIMEOUT, **kwargs):
@@ -1896,6 +1931,7 @@ def setUp(self):
18961931
self.demo_mode = pytest.config.option.demo_mode
18971932
self.demo_sleep = pytest.config.option.demo_sleep
18981933
self.highlights = pytest.config.option.highlights
1934+
self.ad_block_on = pytest.config.option.ad_block_on
18991935
self.verify_delay = pytest.config.option.verify_delay
19001936
self.timeout_multiplier = pytest.config.option.timeout_multiplier
19011937
self.use_grid = False

seleniumbase/plugins/pytest_plugin.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def pytest_addoption(parser):
8888
dest='headless',
8989
default=False,
9090
help="""Using this makes Webdriver run headlessly,
91-
which is useful inside a Linux Docker.""")
91+
which is required on headless machines.""")
9292
parser.addoption('--is_pytest', action="store_true",
9393
dest='is_pytest',
9494
default=True,
@@ -107,6 +107,11 @@ def pytest_addoption(parser):
107107
default=None,
108108
help="""Setting this overrides the default number of
109109
highlight animation loops to have per call.""")
110+
parser.addoption('--ad_block', action="store_true",
111+
dest='ad_block_on',
112+
default=False,
113+
help="""Using this makes WebDriver block display ads
114+
that are defined in ad_block_list.AD_BLOCK_LIST.""")
110115
parser.addoption('--verify_delay', action='store', dest='verify_delay',
111116
default=None,
112117
help="""Setting this overrides the default wait time

seleniumbase/plugins/selenium_plugin.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ class SeleniumBrowser(Plugin):
1717
self.options.browser -- the browser to use (--browser)
1818
self.options.server -- the server used by the test (--server)
1919
self.options.port -- the port used by the test (--port)
20+
self.options.proxy -- designates the proxy server:port to use. (--proxy)
2021
self.options.headless -- the option to run headlessly (--headless)
2122
self.options.demo_mode -- the option to slow down Selenium (--demo_mode)
2223
self.options.demo_sleep -- Selenium action delay in DemoMode (--demo_sleep)
2324
self.options.highlights -- # of highlight animations shown (--highlights)
25+
self.options.ad_block -- the option to block some display ads (--ad_block)
2426
self.options.verify_delay -- delay before MasterQA checks (--verify_delay)
2527
self.options.timeout_multiplier -- increase defaults (--timeout_multiplier)
2628
"""
@@ -66,7 +68,7 @@ def options(self, parser, env):
6668
dest='headless',
6769
default=False,
6870
help="""Using this makes Webdriver run headlessly,
69-
which is useful inside a Linux Docker.""")
71+
which is required on headless machines.""")
7072
parser.add_option(
7173
'--demo_mode', action="store_true",
7274
dest='demo_mode',
@@ -83,6 +85,12 @@ def options(self, parser, env):
8385
dest='highlights', default=None,
8486
help="""Setting this overrides the default number of
8587
highlight animation loops to have per call.""")
88+
parser.add_option(
89+
'--ad_block', action="store_true",
90+
dest='ad_block_on',
91+
default=False,
92+
help="""Using this makes WebDriver block display ads
93+
that are defined in ad_block_list.AD_BLOCK_LIST.""")
8694
parser.add_option(
8795
'--verify_delay', action='store',
8896
dest='verify_delay', default=None,
@@ -103,9 +111,6 @@ def configure(self, options, conf):
103111
self.headless_active = False # Default setting
104112

105113
def beforeTest(self, test):
106-
""" Running Selenium locally will be handled differently
107-
from how Selenium is run remotely, such as from Jenkins. """
108-
109114
test.test.browser = self.options.browser
110115
test.test.headless = self.options.headless
111116
test.test.servername = self.options.servername
@@ -114,6 +119,7 @@ def beforeTest(self, test):
114119
test.test.demo_mode = self.options.demo_mode
115120
test.test.demo_sleep = self.options.demo_sleep
116121
test.test.highlights = self.options.highlights
122+
test.test.ad_block_on = self.options.ad_block_on
117123
test.test.verify_delay = self.options.verify_delay # MasterQA
118124
test.test.timeout_multiplier = self.options.timeout_multiplier
119125
test.test.use_grid = False

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
setup(
99
name='seleniumbase',
10-
version='1.8.4',
10+
version='1.8.5',
1111
description='Web Automation & Testing Framework - http://seleniumbase.com',
1212
long_description='Web Automation and Testing Framework - seleniumbase.com',
1313
platforms='Mac * Windows * Linux * Docker',

0 commit comments

Comments
 (0)