Search code examples
pythonselenium-webdriverui-automation

Update state from the UI


I'm coding a bot in Python that plays tic-tac-toe. The game is a Web app written in React.js and is equipped with an AI of its own that utilizes minimax. The user (which the Python bot simulates) is always X, the AI is always O, and the user always moves first. The AI obviously keeps updated on the state of the board, but the Python bot currently only keeps track of it's own clicked squares and will not select a square that it has already selected, but it does not keep track of the board as such.

How can I update the state of the board in Python through the UI? I'm using Selenium to interact with the Web app through the browser. This is a follow-up to another post: python method not being called.

Edit 1:

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]"
    exSquare = "//div[contains(@class, 'board-row')]//button[contains(text(), 'X')]"
    ohSquare = "//div[contains(@class, 'board-row')]//button[contains(text(), 'O')]"
    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(1,9)
        time.sleep(5)
        winner = ''

        if not clickedSquares:
            LOGGER.debug("I made it into the first if statement")
            element = load_browser.find_element(By.XPATH, squares[random_square])
            element.click()
            clickedSquares.append(random_square)   
        
        for i in range(1,9):
            if clickedSquares[i] == Tags.exSquare:
                clickedSquares.append(i)
            if clickedSquares[i] == Tags.ohSquare:
                clickedSquares.append(i)
        
        for i in clickedSquares:
            LOGGER.debug("I made it into the for loop")
            if i == random_square:
                LOGGER.debug("I made it into the second if statement")
                self.test_playTTT(load_browser)
            else:
                LOGGER.debug("I made it into the first else statement")
                clickedSquares.append(random_square)
        element = load_browser.find_element(By.XPATH, squares[random_square])
        element.click()

This is a segment I added to my code to check for squares already filled by either X or O:

    for i in range(1,9):
        if clickedSquares[i] == Tags.exSquare:
            clickedSquares.append(i)
        if clickedSquares[i] == Tags.ohSquare:
            clickedSquares.append(i)

But I get a "list index out of range" error. I think the problem is that I'm trying to do a string comparison on an XPATH. How can I fix this?


Solution

  • for i in range(x, y)
    

    means we will take a range starting and including x up to but not including y, so I think your range should be (0,9). Also, you're correct about your comparison. If I'm reading your code correctly, it's not clickedSquares[i] you should be using but squares[i].

            for i in range(0,9):
                if squares[i] == Tags.exSquare:
                    clickedSquares.append(i)
                if squares[i] == Tags.ohSquare:
                    clickedSquares.append(i)
    

    This should work because squares is a list of XPATH variables.