Added test settings & improved test architecture #139
This commit is contained in:
parent
9de66d4fd4
commit
de78cec96c
@ -1,2 +1,14 @@
|
||||
# kd_discord_bot
|
||||
|
||||
## Test Bot
|
||||
|
||||
To test the bot run unittests or call ```cpl test```.
|
||||
Configure test instance by creating the file ./test/ui_tests/.env and set following environment variables:
|
||||
|
||||
```sh
|
||||
KDB_TEST_DB_PASSWORD=
|
||||
KDB_TEST_NAME=
|
||||
KDB_TEST_TOKEN=
|
||||
KDB_TEST_DISCORD_MAIL=
|
||||
KDB_TEST_DISCORD_PASSWORD=
|
||||
```
|
@ -15,14 +15,14 @@
|
||||
"stats": "src/modules/stats/stats.json",
|
||||
"technician": "src/modules/technician/technician.json",
|
||||
"ui-tests": "test/ui_tests/ui-tests.json",
|
||||
"ui-tests-shared": "src/../test/ui_tests_shared/ui-tests-shared.json",
|
||||
"ui-tests-shared": "test/ui_tests_shared/ui-tests-shared.json",
|
||||
"ui-tests-tests": "test/ui_tests_tests/ui-tests-tests.json",
|
||||
"get-version": "tools/get_version/get-version.json",
|
||||
"post-build": "tools/post_build/post-build.json",
|
||||
"set-version": "tools/set_version/set-version.json"
|
||||
},
|
||||
"Scripts": {
|
||||
"test": "cpl run ui-tests",
|
||||
"test": "export $(cat test/ui_tests/.env); export PYTHONPATH=$PWD/src:$PYTHONPATH; cpl run ui-tests",
|
||||
"sv": "cpl set-version $ARGS",
|
||||
"set-version": "cpl run set-version $ARGS; echo '';",
|
||||
"gv": "cpl get-version",
|
||||
|
@ -16,5 +16,10 @@
|
||||
"Languages": [
|
||||
"de"
|
||||
]
|
||||
},
|
||||
"TestSettings": {
|
||||
"LoginUrl": "https://discord.com/login",
|
||||
"MePageUrl": "https://discord.com/channels/@me",
|
||||
"CmdURL": "https://discord.com/channels/910199451145076828/911578636899987526"
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import asyncio
|
||||
from cpl_core.application import ApplicationABC
|
||||
from cpl_core.application import ApplicationBuilder
|
||||
|
||||
from bot.startup_test_extension import StartupTestExtension
|
||||
from startup_test_extension import StartupTestExtension
|
||||
from ui_tests.startup import Startup
|
||||
from ui_tests_shared.declarations import Declarations
|
||||
|
||||
|
@ -21,7 +21,7 @@ class Startup(StartupABC):
|
||||
self._config: Optional[ConfigurationABC] = None
|
||||
|
||||
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC:
|
||||
configuration.add_environment_variables('KDB-TEST_')
|
||||
configuration.add_environment_variables('KDB_TEST_')
|
||||
configuration.add_environment_variables('DISCORD_')
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
|
@ -1,19 +1,10 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Callable, Type, Optional
|
||||
|
||||
from cpl_core.application import StartupExtensionABC
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceCollectionABC
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
|
||||
from bot_core.configuration.bot_logging_settings import BotLoggingSettings
|
||||
from bot_core.configuration.bot_settings import BotSettings
|
||||
from modules.base.configuration.base_settings import BaseSettings
|
||||
from modules.boot_log.configuration.boot_log_settings import BootLogSettings
|
||||
from modules.level.configuration.level_settings import LevelSettings
|
||||
from modules.permission.configuration.permission_settings import PermissionSettings
|
||||
|
||||
|
||||
class StartupTestExtension(StartupExtensionABC):
|
||||
|
||||
@ -23,6 +14,7 @@ class StartupTestExtension(StartupExtensionABC):
|
||||
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironmentABC):
|
||||
# this shit has to be done here because we need settings in subsequent startup extensions
|
||||
environment.set_working_directory(os.path.dirname(os.path.realpath(__file__)))
|
||||
environment.set_working_directory('../../src/bot/')
|
||||
|
||||
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
|
||||
pass
|
@ -0,0 +1,44 @@
|
||||
{
|
||||
"ProjectSettings": {
|
||||
"Name": "ui-tests",
|
||||
"Version": {
|
||||
"Major": "0",
|
||||
"Minor": "0",
|
||||
"Micro": "0"
|
||||
},
|
||||
"Author": "",
|
||||
"AuthorEmail": "",
|
||||
"Description": "",
|
||||
"LongDescription": "",
|
||||
"URL": "",
|
||||
"CopyrightDate": "",
|
||||
"CopyrightName": "",
|
||||
"LicenseName": "",
|
||||
"LicenseDescription": "",
|
||||
"Dependencies": [
|
||||
"cpl-core>=2022.10.0.post9"
|
||||
],
|
||||
"DevDependencies": [
|
||||
"cpl-cli>=2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
"ProjectType": "console",
|
||||
"SourcePath": "",
|
||||
"OutputPath": "../../dist",
|
||||
"Main": "ui_tests_shared.main",
|
||||
"EntryPoint": "ui-tests-shared",
|
||||
"IncludePackageData": false,
|
||||
"Included": [],
|
||||
"Excluded": [
|
||||
"*/__pycache__",
|
||||
"*/logs",
|
||||
"*/tests"
|
||||
],
|
||||
"PackageData": {},
|
||||
"ProjectReferences": []
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from cpl_discord.service import DiscordBotServiceABC
|
||||
from cpl_translation import TranslatePipe
|
||||
from selenium import webdriver
|
||||
from selenium.common import NoSuchElementException, StaleElementReferenceException
|
||||
from selenium.webdriver import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
@ -11,13 +11,15 @@ from selenium.webdriver.support import expected_conditions
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
from ui_tests_shared.test_case_with_app import TestCaseWithApp
|
||||
from ui_tests_shared.ui import UI
|
||||
|
||||
|
||||
class CommandTestCaseWithApp(TestCaseWithApp):
|
||||
_cmd: WebElement
|
||||
|
||||
_bot = None
|
||||
_t = None
|
||||
_bot: Optional[DiscordBotServiceABC] = None
|
||||
_t: Optional[TranslatePipe] = None
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@ -25,22 +27,22 @@ class CommandTestCaseWithApp(TestCaseWithApp):
|
||||
cls._bot = cls._services.get_service(DiscordBotServiceABC)
|
||||
cls._t = cls._services.get_service(TranslatePipe)
|
||||
|
||||
def send_command(self, cmd: str):
|
||||
self._driver.get('https://discord.com/channels/910199451145076828/911578636899987526')
|
||||
@classmethod
|
||||
def send_command(cls, cmd: str):
|
||||
UI.driver.get(cls._test_settings.cmd_url)
|
||||
time.sleep(2)
|
||||
|
||||
cmd_element_ident = (By.XPATH, '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div[2]/main/form/div/div[1]/div/div[3]/div/div[2]/div')
|
||||
cmd_element = self._driver.find_element(*cmd_element_ident)
|
||||
cmd_element = UI.driver.find_element(*cmd_element_ident)
|
||||
cmd_element.send_keys(f'/{cmd}')
|
||||
time.sleep(2)
|
||||
|
||||
ignored_exceptions = (NoSuchElementException, StaleElementReferenceException,)
|
||||
WebDriverWait(self._driver, 20, ignored_exceptions=ignored_exceptions).until(
|
||||
WebDriverWait(UI.driver, 20, ignored_exceptions=ignored_exceptions).until(
|
||||
expected_conditions.presence_of_element_located((
|
||||
By.XPATH,
|
||||
'/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div[2]/main/form/div/div[2]/div/div/div[5]'
|
||||
))
|
||||
).click()
|
||||
time.sleep(2)
|
||||
self._driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div/main/form/div/div[2]/div/div[2]/div/div').send_keys(
|
||||
Keys.ENTER)
|
||||
UI.driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/div[1]/div/div[2]/div/div[1]/div/div/div[3]/div/main/form/div/div[2]/div/div[2]/div/div').send_keys(Keys.ENTER)
|
||||
|
1
kdb-bot/test/ui_tests_shared/configuration/__init__.py
Normal file
1
kdb-bot/test/ui_tests_shared/configuration/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
# imports
|
35
kdb-bot/test/ui_tests_shared/configuration/test_settings.py
Normal file
35
kdb-bot/test/ui_tests_shared/configuration/test_settings.py
Normal file
@ -0,0 +1,35 @@
|
||||
import traceback
|
||||
|
||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||
from cpl_core.console import Console
|
||||
|
||||
|
||||
class TestSettings(ConfigurationModelABC):
|
||||
|
||||
def __init__(self):
|
||||
ConfigurationModelABC.__init__(self)
|
||||
|
||||
self._login_url = ''
|
||||
self._me_page_url = ''
|
||||
self._cmd_url = ''
|
||||
|
||||
@property
|
||||
def login_url(self) -> str:
|
||||
return self._login_url
|
||||
|
||||
@property
|
||||
def me_page_url(self) -> str:
|
||||
return self._me_page_url
|
||||
|
||||
@property
|
||||
def cmd_url(self) -> str:
|
||||
return self._cmd_url
|
||||
|
||||
def from_dict(self, settings: dict):
|
||||
try:
|
||||
self._login_url = settings['LoginUrl']
|
||||
self._me_page_url = settings['MePageUrl']
|
||||
self._cmd_url = settings['CmdURL']
|
||||
except Exception as e:
|
||||
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings')
|
||||
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')
|
@ -1,7 +1,12 @@
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.environment import ApplicationEnvironmentABC
|
||||
|
||||
from ui_tests.test_application import TestApplication
|
||||
from ui_tests_shared.configuration.test_settings import TestSettings
|
||||
|
||||
|
||||
class Declarations:
|
||||
app: Optional[TestApplication] = None
|
||||
app: Optional[TestApplication]
|
||||
env: Optional[ApplicationEnvironmentABC]
|
||||
test_settings: Optional[TestSettings]
|
||||
|
@ -1,29 +1,19 @@
|
||||
import time
|
||||
import unittest
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
from ui_tests.main import get_app
|
||||
from ui_tests_shared.configuration.test_settings import TestSettings
|
||||
from ui_tests_shared.declarations import Declarations
|
||||
from ui_tests_shared.ui import UI
|
||||
|
||||
|
||||
class TestCaseWithApp(unittest.TestCase):
|
||||
options = webdriver.ChromeOptions()
|
||||
options.add_experimental_option('useAutomationExtension', False)
|
||||
options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||
|
||||
_config: Optional[ConfigurationABC] = None
|
||||
_services: Optional[ServiceProviderABC] = None
|
||||
_driver = webdriver.Chrome(options=options)
|
||||
|
||||
_is_logged_in = False
|
||||
_test_settings: Optional[TestSettings] = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@ -32,30 +22,6 @@ class TestCaseWithApp(unittest.TestCase):
|
||||
|
||||
cls._config = Declarations.app.config
|
||||
cls._services = Declarations.app.services
|
||||
cls._login()
|
||||
|
||||
@classmethod
|
||||
def _login(cls):
|
||||
if cls._is_logged_in:
|
||||
return
|
||||
|
||||
# use full xpath: https://stackoverflow.com/questions/71179006/how-can-selenium-python-chrome-find-web-elements-visible-in-dev-tools-but-no
|
||||
|
||||
cls._driver.get('https://discord.com/login')
|
||||
|
||||
WebDriverWait(cls._driver, 20).until(expected_conditions.presence_of_element_located((By.NAME, 'email')))
|
||||
|
||||
mail = cls._driver.find_element(By.NAME, 'email')
|
||||
mail.clear()
|
||||
mail.send_keys("dev.sven.heidemann@sh-edraft.de")
|
||||
mail.send_keys(Keys.RETURN)
|
||||
|
||||
pw = cls._driver.find_element(By.NAME, 'password')
|
||||
pw.clear()
|
||||
pw.send_keys("Heidemann1410")
|
||||
pw.send_keys(Keys.RETURN)
|
||||
time.sleep(1)
|
||||
pw.send_keys(Keys.RETURN)
|
||||
|
||||
WebDriverWait(cls._driver, 20).until(expected_conditions.url_matches('https://discord.com/channels/@me'))
|
||||
time.sleep(4)
|
||||
cls._test_settings: TestSettings = cls._config.get_configuration(TestSettings)
|
||||
UI.set_settings(cls._test_settings)
|
||||
UI.login(cls._config.get_configuration('DISCORD_MAIL'), cls._config.get_configuration('DISCORD_PASSWORD'))
|
||||
|
@ -22,9 +22,7 @@
|
||||
"cpl-cli>=2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {
|
||||
"linux": ""
|
||||
},
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
|
56
kdb-bot/test/ui_tests_shared/ui.py
Normal file
56
kdb-bot/test/ui_tests_shared/ui.py
Normal file
@ -0,0 +1,56 @@
|
||||
import time
|
||||
import unittest
|
||||
from typing import Optional
|
||||
|
||||
from cpl_core.configuration import ConfigurationABC
|
||||
from cpl_core.dependency_injection import ServiceProviderABC
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
from ui_tests.main import get_app
|
||||
from ui_tests_shared.configuration.test_settings import TestSettings
|
||||
from ui_tests_shared.declarations import Declarations
|
||||
|
||||
|
||||
class UI:
|
||||
_test_settings: Optional[TestSettings] = None
|
||||
|
||||
options = webdriver.ChromeOptions()
|
||||
options.add_experimental_option('useAutomationExtension', False)
|
||||
options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||
driver = webdriver.Chrome(options=options)
|
||||
|
||||
_is_logged_in = False
|
||||
|
||||
@classmethod
|
||||
def set_settings(cls, test_settings: TestSettings):
|
||||
cls._test_settings = test_settings
|
||||
|
||||
@classmethod
|
||||
def login(cls, mail: str, password: str):
|
||||
if cls._is_logged_in:
|
||||
return
|
||||
|
||||
# use full xpath: https://stackoverflow.com/questions/71179006/how-can-selenium-python-chrome-find-web-elements-visible-in-dev-tools-but-no
|
||||
|
||||
cls.driver.get(cls._test_settings.login_url)
|
||||
|
||||
WebDriverWait(cls.driver, 20).until(expected_conditions.presence_of_element_located((By.NAME, 'email')))
|
||||
|
||||
mail_element = cls.driver.find_element(By.NAME, 'email')
|
||||
mail_element.clear()
|
||||
mail_element.send_keys(mail)
|
||||
mail_element.send_keys(Keys.RETURN)
|
||||
|
||||
pw_element = cls.driver.find_element(By.NAME, 'password')
|
||||
pw_element.clear()
|
||||
pw_element.send_keys(password)
|
||||
pw_element.send_keys(Keys.RETURN)
|
||||
time.sleep(1)
|
||||
pw_element.send_keys(Keys.RETURN)
|
||||
|
||||
WebDriverWait(cls.driver, 20).until(expected_conditions.url_matches(cls._test_settings.me_page_url))
|
||||
time.sleep(4)
|
@ -1,3 +1,5 @@
|
||||
import unittest
|
||||
|
||||
import discord
|
||||
|
||||
from ui_tests_shared.command_test_case_with_app import CommandTestCaseWithApp
|
||||
@ -6,9 +8,6 @@ from ui_tests_shared.decorators import async_test
|
||||
|
||||
class PingTestCase(CommandTestCaseWithApp):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
@async_test
|
||||
async def test_ping(self):
|
||||
correct_response = self._t.transform('modules.base.pong')
|
||||
|
@ -22,9 +22,7 @@
|
||||
"cpl-cli>=2022.10.0"
|
||||
],
|
||||
"PythonVersion": ">=3.10.4",
|
||||
"PythonPath": {
|
||||
"linux": ""
|
||||
},
|
||||
"PythonPath": {},
|
||||
"Classifiers": []
|
||||
},
|
||||
"BuildSettings": {
|
||||
|
Loading…
Reference in New Issue
Block a user