Home > OS >  AttributeError: 'str' object has no attribute 'click' while trying to loop throu
AttributeError: 'str' object has no attribute 'click' while trying to loop throu

Time:10-03

I'm trying to write a bot using Selenium Python to play an online game of tic-tac-toe. I've scraped the XPATHS of the squares and placed them in variables. The bot is simple. It's just supposed to click random squares. I'll enhance the bot later. Right now, I just want to click elements, and I'm getting stuck by this line of code:

squares[random_square].click()

I get an attribute error in the traceback. I understand strings can't invoke the click() method, and usually, I would have something like this:

pickSquare = WebDriverWait(load_browser, 10).until(EC.element_to_be_clickable(By.XPATH, Tags.someSquare))
pickSquare.click()

But I've put all of my XPATH variables into an array that I need to iterate through, and I'm not sure how to use By and EC on an array as such. Below is the code I have so far.

import pytest
import time
import logging
from random import randint

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

LOGGER = logging.getLogger(__name__)

class Tags():
    square1 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[1]"
    square2 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[2]"
    square3 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[3]"
    square4 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[4]"
    square5 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[5]"
    square6 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[6]"
    square7 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[7]"
    square8 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[8]"
    square9 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[9]"
    resultOh = "//div[contains(@class, 'game-info')]//div[contains(text(), 'Winner: O')]"
    resultEx = "//div[contains(@class, 'game-info')]//div[contains(text(), 'Winner: X')]"
    resultTie = "//div[contains(@class, 'game-info')]//div[contains(text(), 'tie')]"

class TestCase_PlayTTT():
    
    URL = "http://localhost:3000"
    
    @pytest.fixture
    def load_browser(self, browser):
        browser.get(self.URL)
        yield browser

    
    def test_playTTT(self, load_browser):

        squares = [Tags.square1,Tags.square2,Tags.square3,
                   Tags.square4,Tags.square5,Tags.square6,
                   Tags.square7,Tags.square8,Tags.square9]
        
        clickedSquares = []
        random_square = randint(0,8)
        time.sleep(10)

        for i in clickedSquares:
            if i == random_square:
                self.test_playTTT()
            else:
                clickedSquares.append(random_square)
        squares[random_square].click()

        if WebDriverWait(load_browser, 10).until(EC.presence_of_element_located(By.XPATH, Tags.resultOh)):
            winner = 'O'
            LOGGER.info('Winner O')
            assert winner

        elif WebDriverWait(load_browser, 10).until(EC.presence_of_element_located(By.XPATH, Tags.resultEx)):
            winner = 'X'
            LOGGER.info('Winner X')
            assert winner

        else:
            winner = 'Tie'
            LOGGER.info('Tie')
            assert winner

UPDATE 1: Below is the traceback.

self = <TestCases.TestCase_PlayTTT.TestCase_PlayTTT object at 0x000001DA3F4139D0>
load_browser = <selenium.webdriver.firefox.webdriver.WebDriver (session="324ff7dc-195c-4bdf-9ceb-84bf978dfc66")>

    def test_playTTT(self, load_browser):

        squares = [Tags.square1,Tags.square2,Tags.square3,
                   Tags.square4,Tags.square5,Tags.square6,
                   Tags.square7,Tags.square8,Tags.square9]

        clickedSquares = []
        random_square = randint(0,8)
        time.sleep(10)

        for i in clickedSquares:
            if i == random_square:
                self.test_playTTT()
            else:
                clickedSquares.append(random_square)
>       squares[random_square].click()
E       AttributeError: 'str' object has no attribute 'click'

TestCases\TestCase_PlayTTT.py:52: AttributeError

CodePudding user response:

You are missing a pair of parenthesis in expression.
Instead of

WebDriverWait(load_browser, 10).until(EC.presence_of_element_located(By.XPATH, Tags.resultOh))

And

WebDriverWait(load_browser, 10).until(EC.presence_of_element_located(By.XPATH, Tags.resultEx))

Try

WebDriverWait(load_browser, 10).until((EC.presence_of_element_located(By.XPATH, Tags.resultOh)))

And

WebDriverWait(load_browser, 10).until((EC.presence_of_element_located(By.XPATH, Tags.resultEx)))

CodePudding user response:

I think you may need to restructure your code to do something like this to get the right objects to use click() on an element:

# import webdriver
from selenium import webdriver

# create webdriver object
driver = webdriver.Firefox()

# get geeksforgeeks.org
driver.get("http://localhost:3000/")

then have the code that loops waiting on on results and selecting the next thing to try and when you are ready to find and click the element have code like this:

# get element
element = driver.find_element(By.XPATH, squares[random_square])

# click the element
element.click()
  • Related