I'm working on a practice Selenium script to adjust the value of a price slider that has min and max value sliders on the same bar. The code technically works, and I see the max slider value adjust, but not to the exact value I want. Is there a better way I can be doing this?
The code I'm working with at the moment is:
def test_priceScale():
driver.get('https://www.practicesoftwaretesting.com')
slider = driver.find_element(By.CSS_SELECTOR, "span[aria-label = 'ngx-slider-max']")
current_value = float(slider.get_attribute('aria-valuenow'))
max_value = float(slider.get_attribute('aria-valuemax'))
target_value = 10
slider_width = slider.size["width"]
offset = (target_value - current_value) / (max_value * slider_width)
actions = ActionChains(driver)
actions.click_and_hold(slider).move_by_offset(offset, 0).release().perform()
My hope is to find a solution that if I change the value of target_value, I can see the slider adjust to that specific price.
This was challenging...
I first tried to use the size of the slider in px vs the desired value to scale my clicks and that got me close but not exact... and I wanted exact, if I could get it.
Next I tried to see if there was a way to just set the desired value directly using attributes on the bubbles, etc. but I couldn't find a way that worked (and generally this is a bad idea because it's not how users interact with the site and can cause problems).
The last thing I tried was a drag and drop of 1px at a time to inch towards the desired value, checking the displayed text to see if I reached the desired value. This is what I ended up using, with some tweaks.
I kinda went overboard for a typical SO answer... I created a page object for the home page because I thought it would help with code reuse, etc. The test itself is very simple and short... the page object holds the guts of the logic, as it should.
The basic concept is I set a step value to 5 px. I step to the right until I've passed the desired value. Then I step left until I've passed the desired value. If I haven't found the desired value, I decrease the step value by 1 and repeat... stepping right then left. This continues until the desired value is reached or the step value = 0.
I've tried several different values for min and max and it's worked each time. You could increase the initial step value to get there faster, if you want.
Here's the working code:
slider_test.py
from selenium import webdriver
from home_page import HomePage
driver = webdriver.Chrome()
url = "https://www.practicesoftwaretesting.com/"
driver.get(url)
desired_min_value = 51
desired_max_value = 151
homePage = HomePage(driver)
homePage.set_limit(desired_min_value, homePage.Limit.MIN)
homePage.set_limit(desired_max_value, homePage.Limit.MAX)
driver.quit()
home_page.py
import time
from enum import Enum
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver
class HomePage:
def __init__(self, driver: webdriver):
self.driver = driver
self.label_max_locator = (By.CSS_SELECTOR, "span.ngx-slider-model-high")
self.label_min_locator = (By.CSS_SELECTOR, "span.ngx-slider-model-value")
self.slider_max_locator = (By.CSS_SELECTOR, "span.ngx-slider-pointer-max")
self.slider_min_locator = (By.CSS_SELECTOR, "span.ngx-slider-pointer-min")
self.wait = WebDriverWait(driver, 10)
self.Limit = Enum("Limit", [("MIN", 1), ("MAX", 2)]) # an enum to set MIN or MAX
def set_limit(self, desired_value: int, limit):
match limit:
case self.Limit.MIN:
slider_locator = self.slider_min_locator
label_locator = self.label_min_locator
case self.Limit.MAX:
slider_locator = self.slider_max_locator
label_locator = self.label_max_locator
bubble = self.wait.until(EC.visibility_of_element_located(slider_locator))
current_value = self.wait.until(EC.visibility_of_element_located(label_locator))
actions = ActionChains(self.driver)
step = 5
while step > 0:
while int(current_value.text) < desired_value:
actions.drag_and_drop_by_offset(bubble, step, 0).perform()
time.sleep(0.1)
while int(current_value.text) > desired_value:
actions.drag_and_drop_by_offset(bubble, -step, 0).perform()
time.sleep(0.1)
step = step - 1