diff --git a/.github/workflows/test-selenium.yml b/.github/workflows/test-selenium.yml new file mode 100644 index 0000000..9698b23 --- /dev/null +++ b/.github/workflows/test-selenium.yml @@ -0,0 +1,89 @@ +name: Run Selenium Test Suite +on: + push: + branches: [ "main","dev","Selenium","selenium-test-room-change","selenium-behavioral-tests"] + pull_request: + branches: [ "main","dev" ] +jobs: + Test-Selenium: + runs-on: ubuntu-latest + steps: + #Set up environment + - name: Checking out repo + uses: actions/checkout@v3 + - name: Setting up Python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + - name: Installing package list #Not sure what this + run: apt list --installed + #Chrome dependencies + - name: Update + run: sudo apt update -y + - name: Install Chrome Shared Libraries + run: | + sudo apt-get update && sudo apt-get install -y \ + ca-certificates \ + fonts-liberation \ + libasound2 \ + libatk-bridge2.0-0 \ + libatk1.0-0 \ + libc6 \ + libcairo2 \ + libcups2 \ + libdbus-1-3 \ + libexpat1 \ + libfontconfig1 \ + libgbm1 \ + libgcc1 \ + libglib2.0-0 \ + libgtk-3-0 \ + libnspr4 \ + libnss3 \ + libpango-1.0-0 \ + libpangocairo-1.0-0 \ + libstdc++6 \ + libx11-6 \ + libx11-xcb1 \ + libxcb1 \ + libxcomposite1 \ + libxcursor1 \ + libxdamage1 \ + libxext6 \ + libxfixes3 \ + libxi6 \ + libxrandr2 \ + libxrender1 \ + libxss1 \ + libxtst6 \ + lsb-release \ + wget \ + xdg-utils + - name: Removing previous chrome instances on runner #This probably isnt necessary to have + run: sudo apt purge google-chrome-stable + continue-on-error: true + # Actually installing chrome + - name: Install Chrome + run: | + sudo wget https://dl-ssl.google.com/linux/linux_signing_key.pub -O /tmp/google.pub + sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/google-chrome.gpg --import /tmp/google.pub + echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list + sudo apt-get update -y + sudo apt-get install google-chrome-stable -y + + sudo apt-get install -y chromium-browser chromium-chromedriver -y + sudo apt-get update -y + + # Need to fetch reqs if needed + - name: Installing all necessary packages + run: pip install -r tests/selenium/requirements.txt + - name : Start Python's HTTP Server + run: | + echo "$ python3 -m http.server" + python3 -m http.server --bind 127.0.0.1 & + - name: Running behavioral tests + run: behave tests/selenium/behavioral/menu.feature + # Run tests + - name: Running the Python script + run: python3 tests/selenium/seleniumTests.py + \ No newline at end of file diff --git a/tests/selenium/behavioral/menu.feature b/tests/selenium/behavioral/menu.feature new file mode 100644 index 0000000..945b339 --- /dev/null +++ b/tests/selenium/behavioral/menu.feature @@ -0,0 +1,10 @@ +Feature: menu works + +Scenario Outline: Using menu + Given the site is loaded + When we click "" + Then the game state is "" + + Examples: Main Menu + | button_name | goal_state | + | start | PLAYING | diff --git a/tests/selenium/behavioral/steps/behavioral.py b/tests/selenium/behavioral/steps/behavioral.py new file mode 100644 index 0000000..19d1e31 --- /dev/null +++ b/tests/selenium/behavioral/steps/behavioral.py @@ -0,0 +1,44 @@ +from selenium import webdriver +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.by import By +from behave import * +import time +import chromedriver_autoinstaller + +chromedriver_autoinstaller.install() + +def open_browser(link='http://127.0.0.1:8000/index.html'): + time.sleep(1) + chrome_options = webdriver.ChromeOptions() + # Add your options as needed + options = [ + # Define window size here + "--ignore-certificate-errors", + "--headless", + "--no-sandbox" + ] + + for option in options: + chrome_options.add_argument(option) + + browser = webdriver.Chrome(options = chrome_options) + browser.get(link) + time.sleep(3)#Sleep to load page + print("Page loaded...") + + # res=browser.find_element(By.NAME,"controls") + return browser + +@given(u'the site is loaded') +def step_impl(context): + context.browser=open_browser() + +@when(u'we click "{button_name}"') +def step_impl(context, button_name): + context.browser.find_element(By.NAME,button_name).click() + time.sleep(2) + + +@then(u'the game state is "{goal_state}"') +def step_impl(context, goal_state): + assert(context.browser.execute_script(f"return GAMESTATE;")==goal_state) \ No newline at end of file diff --git a/tests/selenium/classExample.py b/tests/selenium/classExample.py new file mode 100644 index 0000000..86ccacc --- /dev/null +++ b/tests/selenium/classExample.py @@ -0,0 +1,36 @@ +from selenium import webdriver +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.by import By +import time + +def test_wikipedia_python_results(): + print("testing wikipedia results to make sure BDFL is mentioned") + browser = webdriver.Chrome() + browser.get("http://wikipedia.org") + query = "Python (programming language)" + search_input = browser.find_element(By.ID,"searchInput") + search_input.clear() + search_input.send_keys(query) + search_input.send_keys(Keys.RETURN) + assert "Guido" in browser.page_source + time.sleep(10) + browser.close() + +def test_wikipedia_CPP_results(): + print("testing wikipedia results to make sure Bjarne is mentioned") + browser = webdriver.Chrome() + browser.get("http://wikipedia.org") + query = "C++" + search_input = browser.find_element(By.ID,"searchInput") + search_input.clear() + search_input.send_keys(query) + search_input.send_keys(Keys.RETURN) + assert "Bjarne Stroustrup" in browser.page_source + time.sleep(3) + browser.close() + + +if __name__ == "__main__": + test_wikipedia_python_results() + test_wikipedia_CPP_results() + print("done.") \ No newline at end of file diff --git a/tests/selenium/requirements.txt b/tests/selenium/requirements.txt new file mode 100644 index 0000000..b4997e3 --- /dev/null +++ b/tests/selenium/requirements.txt @@ -0,0 +1,5 @@ +selenium +chromedriver_autoinstaller +pyvirtualdisplay +webdriver_manager +behave \ No newline at end of file diff --git a/tests/selenium/seleniumTests.py b/tests/selenium/seleniumTests.py new file mode 100644 index 0000000..f4cb716 --- /dev/null +++ b/tests/selenium/seleniumTests.py @@ -0,0 +1,242 @@ +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.chrome.service import Service +from selenium import webdriver +from selenium.webdriver.common.keys import Keys +from selenium.webdriver import Keys, ActionChains +from selenium.webdriver.common.by import By + +# from webdriver_manager.chrome import ChromeDriverManager +import time +import chromedriver_autoinstaller + +chromedriver_autoinstaller.install() # Check if the current version of chromedriver exists + # and if it doesn't exist, download it automatically, + # then add chromedriver to path + +chrome_options = webdriver.ChromeOptions() +# Add your options as needed +options = [ + # Define window size here + "--ignore-certificate-errors", + "--headless", + # "--disable-gpu", + # "--window-size=1920,1200", + # "--ignore-certificate-errors", + # "--disable-extensions", + "--no-sandbox" + # "--disable-dev-shm-usage", + # '--remote-debugging-port=9222' +] + +for option in options: + chrome_options.add_argument(option) + +def open_browser(link='http://127.0.0.1:8000/index.html'): + browser = webdriver.Chrome(options = chrome_options) + browser.get(link) + return browser +def test_execute(): + browser = webdriver.Chrome(options = chrome_options) + browser.get("127.0.0.1:8000/index.html") + time.sleep(1) + browser.find_element(By.NAME,'start').click() + time.sleep(4) + print(browser.execute_script('return testFunc();')) + +def test_rooms(): + browser=open_browser() + time.sleep(3) + browser.find_element(By.NAME,'start').click() + time.sleep(2) + + print("Testing player can move rooms...") + start_room=browser.execute_script('return player.room;') + print("Starting in room: ",start_room) + #Move player to the room to the right, hopefully no obstacles :) + ActionChains(browser)\ + .key_down('d')\ + .perform() + time.sleep(2.5) + ActionChains(browser)\ + .key_down('w')\ + .perform() + time.sleep(1) + ActionChains(browser)\ + .key_up('w')\ + .key_up('d')\ + .perform() + time.sleep(3) + new_room=browser.execute_script('return player.room;') + assert(new_room['x']!=start_room) + print("Rooms changed!") + +def test_movement(): + browser=open_browser() + time.sleep(3) + browser.find_element(By.NAME,'start').click() + time.sleep(4) + + print("Testing player can move upwards...") + y_pos = browser.execute_script('return player.y;') + print("Player Y position: ", y_pos) + ActionChains(browser)\ + .key_down('w')\ + .perform() + time.sleep(.1) + ActionChains(browser)\ + .key_up('w')\ + .perform() + time.sleep(.1) + new_y_pos = browser.execute_script('return player.y;') + print("Player Y position after moving upwards: ", new_y_pos) + assert new_y_pos < y_pos, "Failed to move upwards" + print("Upward movement passed") + + print("Testing player can move downwards...") + y_pos = browser.execute_script('return player.y;') + print("Player Y position: ", y_pos) + ActionChains(browser)\ + .key_down('s')\ + .perform() + time.sleep(.1) + ActionChains(browser)\ + .key_up('s')\ + .perform() + time.sleep(.1) + new_y_pos = browser.execute_script('return player.y;') + print("Player Y position after moving downwards: ", new_y_pos) + assert new_y_pos > y_pos, "Failed to move downwards" + print("Downward movement passed") + + print("Testing player can move to the left...") + x_pos = browser.execute_script('return player.x;') + print("Player X position: ", x_pos) + ActionChains(browser)\ + .key_down('a')\ + .perform() + time.sleep(.1) + ActionChains(browser)\ + .key_up('a')\ + .perform() + time.sleep(.1) + new_x_pos = browser.execute_script('return player.x;') + print("Player X position after moving to the left: ", new_x_pos) + assert new_x_pos < x_pos, "Failed to move to the left" + print("Left movement passed") + + print("Testing player can move to the right...") + x_pos = browser.execute_script('return player.x;') + print("Player X position: ", x_pos) + ActionChains(browser)\ + .key_down('d')\ + .perform() + time.sleep(.1) + ActionChains(browser)\ + .key_up('d')\ + .perform() + time.sleep(.1) + new_x_pos = browser.execute_script('return player.x;') + print("Player X position after moving to the right: ", new_x_pos) + assert new_x_pos > x_pos, "Failed to move to the right" + print("Right movement passed") + +def test_death(): + browser=open_browser() + time.sleep(3) + browser.find_element(By.NAME,'start').click() + time.sleep(4) + + print("Testing player death") + player_health = browser.execute_script('return player.health') + assert player_health > 0, "Player is dead on start" + print("Player has ",player_health," health") + browser.execute_script('player.health -= 200') + time.sleep(4) + alert = browser.switch_to.alert + alert_text = alert.text + assert "died" in alert_text, "Expected alert didn't appear" + alert.dismiss() + gamestate = browser.execute_script('return GAMESTATE') + print(gamestate) + assert gamestate != "PLAYING", "Game state did not change" + print("Player death passed") + +def test_inventory(): + browser=open_browser() + time.sleep(3) + browser.find_element(By.NAME,'start').click() + time.sleep(4) + + print("Testing player inventory") + print("Checking if player is empty on start") + empty_inventory = browser.execute_script('\ + for (let j = 0; j < InventoryController.INVENTORY_HEIGHT; j++) {\ + for (let i = 0; i < InventoryController.INVENTORY_WIDTH; i++) {\ + if (inventory.inventory[j][i] != "") return false;\ + }\ + }\ + return true;\ + ') + assert empty_inventory == True, "Player's inventory is not empty on start" + print("Creating flashlight item on player") + browser.execute_script('\ + flashlight = new Item(player.x,player.y, "FlashLight", 2,1,8,20,flashlightImage);\ + ') + time.sleep(.1) + has_item = browser.execute_script('return inventory.hasItem(flashlight)') + assert has_item == True, "Player did not pick up flashlight" + time.sleep(5) + + print("Creating gun item on player") + browser.execute_script('\ + gun = new Item(player.x,player.y, "Gun", 2,1,33,6,gunImage);\ + ') + time.sleep(.1) + has_item = browser.execute_script('return inventory.hasItem(gun)') + assert has_item == True, "Player did not pick up gun" + time.sleep(5) + + print("Creating bullet item on player") + browser.execute_script('\ + bulletItem = new Item(player.x,player.y, "Bullet", 1,1,4,3,bulletImage);\ + ') + time.sleep(.1) + has_item = browser.execute_script('return inventory.hasItem(bulletItem)') + assert has_item == True, "Player did not pick up bullet" + + print("Creating key item on player") + browser.execute_script('\ + key = new Item(player.x,player.y, "Key", 1,1,10,5,keyImage);\ + ') + time.sleep(.1) + has_item = browser.execute_script('return inventory.hasItem(key)') + assert has_item == True, "Player did not pick up key" + + print("Checking if player's inventory is full") + has_space = browser.execute_script('return inventory.hasSpace(1,1)') + assert has_space["orientation"] == "none", "Player has storage space left" + + print("Checking if items are not visible") + assert browser.execute_script('return flashlight.itemSprite.visible') == False, "Flashlight sprite is still visible" + assert browser.execute_script('return gun.itemSprite.visible') == False, "Gun sprite is still visible" + assert browser.execute_script('return bulletItem.itemSprite.visible') == False, "Bullet sprite is still visible" + assert browser.execute_script('return key.itemSprite.visible') == False, "Key sprite is still visible" + + print("Spawning item on player while inventory is full") + browser.execute_script('\ + key = new Item(player.x,player.y, "Key", 1,1,10,5,keyImage);\ + ') + time.sleep(.1) + has_item = browser.execute_script('return inventory.hasItem(key)') + assert has_item == False, "Player picked up key" + assert browser.execute_script('return key.itemSprite.visible') == True, "Key sprite is not visible" + print("Inventory test passed") + + +if __name__ == "__main__": + test_movement() + test_rooms() + test_death() + test_inventory() + print("done.")