From c274b02c04fdca5fa061c86b6d2a781e64322387 Mon Sep 17 00:00:00 2001 From: ElenaStepuro Date: Mon, 7 Feb 2022 16:30:20 +0300 Subject: [PATCH 01/78] update docker --- ...docker-compose.yaml => docker-compose.yml} | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) rename infra/{docker-compose.yaml => docker-compose.yml} (69%) diff --git a/infra/docker-compose.yaml b/infra/docker-compose.yml similarity index 69% rename from infra/docker-compose.yaml rename to infra/docker-compose.yml index 002fae1..e13310b 100644 --- a/infra/docker-compose.yaml +++ b/infra/docker-compose.yml @@ -1,5 +1,4 @@ -version: "3.9" - +version: "3.3" services: db: @@ -18,7 +17,6 @@ services: healenium: image: healenium/hlm-backend:3.2.0 - container_name: healenium ports: - "7878:7878" links: @@ -46,8 +44,6 @@ services: container_name: hlm-proxy ports: - "8085:8085" - volumes: - - docker-log-volume:/var/log/dockerlogs environment: - "recovery-tries=1" - "score-cap=.6" @@ -58,24 +54,15 @@ services: networks: - healenium - hlm-selenium-3-standalone-tigervnc: - # image: healenium/hlm-selenium-4-standalone-xpra:0.1.1 #для версии XPRA селениум 4.0.0-rc-1 - #image: healenium/hlm-selenium-4-standalone-xpra:1.0 - image: healenium/hlm-selenium-3-standalone-tigervnc:0.1.0 #для версии vnc селениум 3.141.59 - + hlm-selenium-webview: + image: healenium/hlm-selenium-3-standalone-tigervnc:0.1.1 restart: on-failure - container_name: hlm-selenium-3-standalone-tigervnc + container_name: hlm-selenium-webview ports: - - "4444:4444" - "8086:6080" - # - "8086:10000" - volumes: - - docker-log-volume:/var/log/dockerlogs + - "4444:4444" networks: - healenium -volumes: - docker-log-volume: - networks: healenium: \ No newline at end of file From 652f8f76c326c248bf92899b08fa82d1f15e7393 Mon Sep 17 00:00:00 2001 From: ElenaStepuro Date: Mon, 7 Feb 2022 21:18:51 +0300 Subject: [PATCH 02/78] update readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f249b4e..a90d1b7 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,14 @@ Python 3.9.5 + Pytest project with healenium usage example ```docker-compose up -d``` +To download this file into your project use this command: + +```$ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose.yml -o docker-compose.yml``` + +Create /db/sql folder on the same level in your project. Add init.sql file into ```./db/sql/init.sql``` folder in your project via command: + +```$ curl https://raw.githubusercontent.com/healenium/healenium-client/master/example/init.sql -o init.sql``` + Verify that images ```healenium/hlm-backend:3.2.0```, ```postgres:11-alpine```, ```healenium/hlm-selector-imitator```, ```healenium/hlm-selenium-4-standalone-xpra``` and ```healenium/hlm-proxy:0.2.1``` are up and running ### 2. Project structure From 51c911ae94ebcf02936e479a27a11f6a893040ec Mon Sep 17 00:00:00 2001 From: ElenaStepuro Date: Wed, 9 Feb 2022 17:41:51 +0300 Subject: [PATCH 03/78] update image docker-compose --- infra/docker-compose.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index e13310b..b8e999e 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -42,6 +42,8 @@ services: hlm-proxy: image: healenium/hlm-proxy:0.2.1 container_name: hlm-proxy + volumes: + - docker-log-volume:/var/log/dockerlogs ports: - "8085:8085" environment: @@ -61,8 +63,13 @@ services: ports: - "8086:6080" - "4444:4444" + volumes: + - docker-log-volume:/var/log/dockerlogs networks: - healenium +volumes: + docker-log-volume: + networks: healenium: \ No newline at end of file From 85a5132da8d9b76f5cb412640f8839641d090ebf Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Thu, 31 Mar 2022 12:13:36 +0300 Subject: [PATCH 04/78] Update versions --- infra/docker-compose.yml | 14 +++++------ tests/test_base.py | 6 ----- tests/test_callback.py | 22 ++++++++-------- tests/test_markup.py | 54 +++++++++++++++++++++------------------- 4 files changed, 46 insertions(+), 50 deletions(-) diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index b8e999e..9ad9bfe 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.3" +version: "3.9" services: db: @@ -16,7 +16,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.0 + image: healenium/hlm-backend:3.2.1 ports: - "7878:7878" links: @@ -40,7 +40,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:0.2.1 + image: healenium/hlm-proxy:0.2.4 container_name: hlm-proxy volumes: - docker-log-volume:/var/log/dockerlogs @@ -50,14 +50,14 @@ services: - "recovery-tries=1" - "score-cap=.6" - "heal-enabled=true" - - "serverHost=localhost" - - "serverPort=7878" - - "imitatePort=8000" +# - SELENIUM_HOST=hlm-selenium-webview +# - HEALENIUM_HOST=healenium +# - IMITATE_HOST=selector-imitator networks: - healenium hlm-selenium-webview: - image: healenium/hlm-selenium-3-standalone-tigervnc:0.1.1 + image: healenium/hlm-selenium-3-standalone-tigervnc:firefox98.0.2_chrome99.0.4844.84_edge99.0.1150.5599 restart: on-failure container_name: hlm-selenium-webview ports: diff --git a/tests/test_base.py b/tests/test_base.py index 3fefabe..6f4a844 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -13,11 +13,5 @@ def setup_method(self): desired_capabilities=webdriver.DesiredCapabilities.CHROME, options=options) - # options = webdriver.FirefoxOptions() - # browser = webdriver.Remote( - # command_executor="http://localhost:4444/wd/hub/", - # desired_capabilities=webdriver.DesiredCapabilities.FIREFOX, - # options=options) - def teardown_method(self, method): self.driver.quit() diff --git a/tests/test_callback.py b/tests/test_callback.py index bc36fcf..ccab84f 100644 --- a/tests/test_callback.py +++ b/tests/test_callback.py @@ -4,17 +4,17 @@ class Test_Callback(Test_Base): - def test_element_from_shadow_root(self, setup_method): - callback_page=Callback_Page(self.driver) - - callback_page.open_browser() - callback_page.click_add_square_button() - result = callback_page.verify_shadow_element() - assert result == True - - callback_page.click_update_square_button() - result = callback_page.verify_shadow_element() #should be healed - assert result == True + # def test_element_from_shadow_root(self, setup_method): + # callback_page=Callback_Page(self.driver) + # + # callback_page.open_browser() + # callback_page.click_add_square_button() + # result = callback_page.verify_shadow_element() + # assert result == True + # + # callback_page.click_update_square_button() + # result = callback_page.verify_shadow_element() #should be healed + # assert result == True def test_css_locators(self, setup_method): callback_page=Callback_Page(self.driver) diff --git a/tests/test_markup.py b/tests/test_markup.py index 21e4025..5d6e449 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -1,3 +1,5 @@ +from selenium import webdriver + from src.main.pages.markup_page import * from test_base import Test_Base @@ -16,32 +18,32 @@ def test_button_click_specific_find_element(self, setup_method): main_page.click_test_button() #should be healed main_page.confirm_alert() - def test_select_checkboxes(self, setup_method): - main_page = Markup_Page(self.driver) - main_page.open_browser().generate_markup() - - while main_page.displayed_text() != True: - main_page.generate_markup() - - for i in [0,1,2,3,4,5]: - main_page.select_first_checkbox() #should be healed - - result = main_page.verify_first_checkbox() #should be healed - assert result == True - - def test_ButtonClickWithId(self, setup_method): - main_page = Markup_Page(self.driver) - - main_page.open_browser().click_test_button() - main_page.confirm_alert() - - while main_page.test_button_id_enabled()!=True: - main_page.generate_markup() - - for i in [0,1,2]: - main_page.click_test_generated_button() #should be healed - main_page.confirm_alert() - main_page.generate_markup() + # def test_select_checkboxes(self, setup_method): + # main_page = Markup_Page(self.driver) + # main_page.open_browser().generate_markup() + # + # while main_page.displayed_text() != True: + # main_page.generate_markup() + # + # for i in [0,1,2,3,4,5]: + # main_page.select_first_checkbox() #should be healed + # + # result = main_page.verify_first_checkbox() #should be healed + # assert result == True + + # def test_ButtonClickWithId(self, setup_method): + # main_page = Markup_Page(self.driver) + # + # main_page.open_browser().click_test_button() + # main_page.confirm_alert() + # + # while main_page.test_button_id_enabled()!=True: + # main_page.generate_markup() + # + # for i in [0,1,2]: + # main_page.click_test_generated_button() #should be healed + # main_page.confirm_alert() + # main_page.generate_markup() # Markup_Page_By: different locator types with By in property def test_button_click_find_by(self, setup_method): From 81a6e033ad0a65a0ead6ea7c2224e79179733028 Mon Sep 17 00:00:00 2001 From: Elena Date: Fri, 1 Apr 2022 16:59:12 +0300 Subject: [PATCH 05/78] Update tests --- .idea/healenium-example-python.iml | 2 +- .idea/misc.xml | 2 +- README.md | 2 +- infra/docker-compose.yml | 14 +++---- src/main/pages/base_page.py | 3 +- src/main/pages/callback_page.py | 15 +++----- src/main/pages/markup_page.py | 20 +++++----- src/main/pages/testenv_page.py | 26 +++++++++++++ tests/test_base.py | 4 +- tests/test_callback.py | 26 +++++-------- tests/test_markup.py | 60 +++++++++++++----------------- 11 files changed, 90 insertions(+), 84 deletions(-) create mode 100644 src/main/pages/testenv_page.py diff --git a/.idea/healenium-example-python.iml b/.idea/healenium-example-python.iml index 170d602..5b05384 100644 --- a/.idea/healenium-example-python.iml +++ b/.idea/healenium-example-python.iml @@ -2,7 +2,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 2d83d70..dc9ea49 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index a90d1b7..3fcb6b5 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Create /db/sql folder on the same level in your project. Add init.sql file into ```$ curl https://raw.githubusercontent.com/healenium/healenium-client/master/example/init.sql -o init.sql``` -Verify that images ```healenium/hlm-backend:3.2.0```, ```postgres:11-alpine```, ```healenium/hlm-selector-imitator```, ```healenium/hlm-selenium-4-standalone-xpra``` and ```healenium/hlm-proxy:0.2.1``` are up and running +Verify that images ```healenium/hlm-backend:3.2.1```, ```postgres:11-alpine```, ```healenium/hlm-selector-imitator:1.1```, ```healenium/hlm-proxy``` are up and running ### 2. Project structure ``` diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index b8e999e..9ad9bfe 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.3" +version: "3.9" services: db: @@ -16,7 +16,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.0 + image: healenium/hlm-backend:3.2.1 ports: - "7878:7878" links: @@ -40,7 +40,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:0.2.1 + image: healenium/hlm-proxy:0.2.4 container_name: hlm-proxy volumes: - docker-log-volume:/var/log/dockerlogs @@ -50,14 +50,14 @@ services: - "recovery-tries=1" - "score-cap=.6" - "heal-enabled=true" - - "serverHost=localhost" - - "serverPort=7878" - - "imitatePort=8000" +# - SELENIUM_HOST=hlm-selenium-webview +# - HEALENIUM_HOST=healenium +# - IMITATE_HOST=selector-imitator networks: - healenium hlm-selenium-webview: - image: healenium/hlm-selenium-3-standalone-tigervnc:0.1.1 + image: healenium/hlm-selenium-3-standalone-tigervnc:firefox98.0.2_chrome99.0.4844.84_edge99.0.1150.5599 restart: on-failure container_name: hlm-selenium-webview ports: diff --git a/src/main/pages/base_page.py b/src/main/pages/base_page.py index 316bde4..009c14d 100644 --- a/src/main/pages/base_page.py +++ b/src/main/pages/base_page.py @@ -1,6 +1,7 @@ -class Base_Page(object): +class BasePage(object): mainPageUrl = 'https://sha-test-app.herokuapp.com/' callbackTestPageUrl = 'https://mdn.github.io/web-components-examples/life-cycle-callbacks/' + testEnvPageUrl = 'https://elenastepuro.github.io/test_env/index.html' def __init__(self, driver): self.driver = driver diff --git a/src/main/pages/callback_page.py b/src/main/pages/callback_page.py index 94f32a0..af889e5 100644 --- a/src/main/pages/callback_page.py +++ b/src/main/pages/callback_page.py @@ -1,23 +1,18 @@ from selenium.webdriver.common.by import By from src.main.locators.callback_locators import Locators -from src.main.pages.base_page import Base_Page +from src.main.pages.base_page import BasePage -class Callback_Page(Base_Page): +class CallbackPage(BasePage): def open_browser(self): - self.driver.get(Base_Page.callbackTestPageUrl); + self.driver.get(BasePage.callbackTestPageUrl) return self def click_add_square_button(self): - self.add_square_button = self.driver.find_element(By.XPATH, Locators.add_square_button) - self.add_square_button.click() - - def verify_shadow_element(self): - shadowRoot = self.driver.find_element(By.XPATH, Locators.test_button); - button = self.driver.execute_script("return arguments[0].shadowRoot", shadowRoot) - return button.is_enabled() + add_square_button = self.driver.find_element(By.XPATH, Locators.add_square_button) + add_square_button.click() def verify_square_element(self): square = self.driver.find_element(By.CSS_SELECTOR, Locators.test_button_css) diff --git a/src/main/pages/markup_page.py b/src/main/pages/markup_page.py index 1476b42..3e6daab 100644 --- a/src/main/pages/markup_page.py +++ b/src/main/pages/markup_page.py @@ -3,13 +3,13 @@ from selenium.webdriver.support.ui import WebDriverWait from src.main.locators.markup_locators import * -from src.main.pages.base_page import Base_Page +from src.main.pages.base_page import BasePage -class Markup_Page(Base_Page): +class MarkupPage(BasePage): def open_browser(self): - self.driver.get(Base_Page.mainPageUrl); + self.driver.get(BasePage.mainPageUrl) return self def generate_markup(self): @@ -26,13 +26,13 @@ def click_test_generated_button(self): def test_button_id_enabled(self): try: return self.driver.find_element_by_id(Locators.test_generated_button).is_enabled() - except(NoSuchElementException): + except NoSuchElementException: return False def displayed_text(self): try: return self.driver.find_element_by_xpath(Locators.text_first_select).is_enabled() - except(NoSuchElementException): + except NoSuchElementException: return False def select_first_checkbox(self): @@ -42,10 +42,10 @@ def verify_first_checkbox(self): return self.driver.find_element_by_xpath(Locators.checkbox_account).is_enabled() -class Markup_Page_By(Base_Page): +class MarkupPageBy(BasePage): def open_browser(self): - self.driver.get(Base_Page.mainPageUrl); + self.driver.get(BasePage.mainPageUrl) return self def generate_markup(self): @@ -68,14 +68,14 @@ def check_that_button_invisible(self): WebDriverWait(self.driver, 5).until( expected_conditions.invisibility_of_element(*Locators_By.button_invisible)) return True - except (Exception): + except Exception: return False -class Markup_Page_Request(Base_Page): +class MarkupPageRequest(BasePage): def open_browser(self): - self.driver.get(Base_Page.mainPageUrl) + self.driver.get(BasePage.mainPageUrl) return self def generate_markup(self): diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py new file mode 100644 index 0000000..bed92c6 --- /dev/null +++ b/src/main/pages/testenv_page.py @@ -0,0 +1,26 @@ +from src.main.pages.base_page import BasePage +from src.main.locators.markup_locators import * + + +class TestEnvPage(BasePage): + def open_browser(self): + self.driver.get(BasePage.testEnvPageUrl) + return self + + def select_checkboxes(self): + checkboxes = self.driver.find_elements(By.XPATH, "//*[contains(@class,'test-form')]//*[@class='input1']") + for ch in checkboxes: + ch.click() + return self + + def click_form_submit_btn(self): + self.driver.find_element_by_id('Submit_checkbox').click() + return self + + def click_btn_id(self): + self.driver.find_element_by_id('change_id').click() + return self + + def click_submit_btn(self): + self.driver.find_element_by_id('Submit').click() + return self diff --git a/tests/test_base.py b/tests/test_base.py index 3fefabe..93ff6e5 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -2,16 +2,18 @@ from selenium import webdriver -class Test_Base(): +class TestBase: @pytest.fixture() def setup_method(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") self.driver = webdriver.Remote( command_executor="http://localhost:8085/", desired_capabilities=webdriver.DesiredCapabilities.CHROME, options=options) + return self.driver # options = webdriver.FirefoxOptions() # browser = webdriver.Remote( diff --git a/tests/test_callback.py b/tests/test_callback.py index bc36fcf..5a01402 100644 --- a/tests/test_callback.py +++ b/tests/test_callback.py @@ -1,29 +1,21 @@ -from src.main.pages.callback_page import Callback_Page -from test_base import Test_Base +from src.main.pages.callback_page import CallbackPage -class Test_Callback(Test_Base): +from test_base import TestBase - def test_element_from_shadow_root(self, setup_method): - callback_page=Callback_Page(self.driver) - callback_page.open_browser() - callback_page.click_add_square_button() - result = callback_page.verify_shadow_element() - assert result == True - - callback_page.click_update_square_button() - result = callback_page.verify_shadow_element() #should be healed - assert result == True +class TestCallback(TestBase): def test_css_locators(self, setup_method): - callback_page=Callback_Page(self.driver) + self.driver = setup_method + callback_page = CallbackPage(self.driver) callback_page.open_browser() callback_page.click_add_square_button() result = callback_page.verify_square_element() - assert result==True + assert result == True callback_page.click_update_square_button() - result = callback_page.verify_square_element() #should be healed - assert result == True \ No newline at end of file + result = callback_page.verify_square_element() # should be healed + assert result == True + driver.quit() diff --git a/tests/test_markup.py b/tests/test_markup.py index 21e4025..7592699 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -1,72 +1,62 @@ from src.main.pages.markup_page import * -from test_base import Test_Base +from src.main.pages.testenv_page import TestEnvPage +from test_base import TestBase -class Test_Markup(Test_Base): +class TestMarkup(TestBase): -# Markup_Page: different locator types with string property + # Markup_Page: different locator types with string property def test_button_click_specific_find_element(self, setup_method): - main_page = Markup_Page(self.driver) + main_page = MarkupPage(self.driver) main_page.open_browser() main_page.click_test_button() main_page.confirm_alert() main_page.generate_markup() - main_page.click_test_button() #should be healed + main_page.click_test_button() # should be healed main_page.confirm_alert() def test_select_checkboxes(self, setup_method): - main_page = Markup_Page(self.driver) - main_page.open_browser().generate_markup() + test_page = TestEnvPage(self.driver) - while main_page.displayed_text() != True: - main_page.generate_markup() + test_page.open_browser() + test_page.select_checkboxes() + test_page.click_form_submit_btn() + test_page.select_checkboxes() # should be healed - for i in [0,1,2,3,4,5]: - main_page.select_first_checkbox() #should be healed + def test_button_click_with_id(self, setup_method): + test_page = TestEnvPage(self.driver) - result = main_page.verify_first_checkbox() #should be healed - assert result == True + test_page.open_browser() + test_page.click_btn_id() + test_page.click_submit_btn() + test_page.click_btn_id() # should be healed - def test_ButtonClickWithId(self, setup_method): - main_page = Markup_Page(self.driver) - - main_page.open_browser().click_test_button() - main_page.confirm_alert() - - while main_page.test_button_id_enabled()!=True: - main_page.generate_markup() - - for i in [0,1,2]: - main_page.click_test_generated_button() #should be healed - main_page.confirm_alert() - main_page.generate_markup() - -# Markup_Page_By: different locator types with By in property + # Markup_Page_By: different locator types with By in property def test_button_click_find_by(self, setup_method): - markup_page_by=Markup_Page_By(self.driver) + markup_page_by = MarkupPageBy(self.driver) markup_page_by.open_browser() markup_page_by.click_button_for_invisible() - invisible = markup_page_by.check_that_button_invisible() + markup_page_by.check_that_button_invisible() markup_page_by.open_browser() markup_page_by.click_test_button() markup_page_by.confirm_alert() markup_page_by.generate_markup() - markup_page_by.click_test_button() #should be healed + markup_page_by.click_test_button() # should be healed markup_page_by.confirm_alert() -# Markup_Page_Request: different locator types with By in request + # Markup_Page_Request: different locator types with By in request def test_button_click_find_request(self, setup_method): - markup_page_request=Markup_Page_Request(self.driver) + markup_page_request = MarkupPageRequest(self.driver) markup_page_request.open_browser() markup_page_request.click_test_button() markup_page_request.confirm_alert() markup_page_request.generate_markup() - markup_page_request.click_test_button() #should be healed - markup_page_request.confirm_alert() \ No newline at end of file + markup_page_request.click_test_button() # should be healed + markup_page_request.confirm_alert() From 4dedb697bf73cd6b7e437f3354722afd5e2105f9 Mon Sep 17 00:00:00 2001 From: Elena Date: Mon, 11 Apr 2022 18:16:02 +0300 Subject: [PATCH 06/78] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d9a06fe..662d3fe 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -2,6 +2,8 @@ name: Bug report description: Create a report to help us improve title: "[BUG]: " labels: [ bug ] +assignees: ElenaStepuro + body: - type: textarea id: description From fdd61833163ad14fb12a1b2587f9a2cb0c12b8c8 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 12 Apr 2022 16:42:23 +0300 Subject: [PATCH 07/78] Change alert accept --- src/main/pages/base_page.py | 7 +++++-- tests/test_callback.py | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/pages/base_page.py b/src/main/pages/base_page.py index 009c14d..eea3cba 100644 --- a/src/main/pages/base_page.py +++ b/src/main/pages/base_page.py @@ -1,3 +1,6 @@ +from selenium.webdriver.common.alert import Alert + + class BasePage(object): mainPageUrl = 'https://sha-test-app.herokuapp.com/' callbackTestPageUrl = 'https://mdn.github.io/web-components-examples/life-cycle-callbacks/' @@ -7,5 +10,5 @@ def __init__(self, driver): self.driver = driver def confirm_alert(self): - allert = self.driver.switch_to_alert() - allert.accept() + Alert(self.driver).accept() + diff --git a/tests/test_callback.py b/tests/test_callback.py index 5a01402..288e4d1 100644 --- a/tests/test_callback.py +++ b/tests/test_callback.py @@ -18,4 +18,3 @@ def test_css_locators(self, setup_method): callback_page.click_update_square_button() result = callback_page.verify_square_element() # should be healed assert result == True - driver.quit() From 6a66a0d2d59327caa94f54cfe765772f07b0ee34 Mon Sep 17 00:00:00 2001 From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com> Date: Fri, 15 Apr 2022 10:36:21 +0300 Subject: [PATCH 08/78] Update docker-compose.yml --- infra/docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 9ad9bfe..3fa9a57 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -16,7 +16,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.1 + image: healenium/hlm-backend:3.2.2 ports: - "7878:7878" links: @@ -31,7 +31,7 @@ services: - healenium selector-imitator: - image: healenium/hlm-selector-imitator:1 + image: healenium/hlm-selector-imitator:1.1 container_name: selector-imitator restart: on-failure ports: @@ -40,7 +40,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:0.2.4 + image: healenium/hlm-proxy:0.2.5 container_name: hlm-proxy volumes: - docker-log-volume:/var/log/dockerlogs @@ -57,7 +57,7 @@ services: - healenium hlm-selenium-webview: - image: healenium/hlm-selenium-3-standalone-tigervnc:firefox98.0.2_chrome99.0.4844.84_edge99.0.1150.5599 + image: healenium/hlm-selenium-4-standalone-tigervnc:firefox98.0.1_chrome99.0.4844.82_edge99.0.1150.46 restart: on-failure container_name: hlm-selenium-webview ports: @@ -72,4 +72,4 @@ volumes: docker-log-volume: networks: - healenium: \ No newline at end of file + healenium: From 373fe3ccce8d0c89ba51e0d3b88bcc0c4cbbf9d5 Mon Sep 17 00:00:00 2001 From: Elena Date: Fri, 15 Apr 2022 16:20:09 +0300 Subject: [PATCH 09/78] Add test with elements under parent --- src/main/pages/testenv_page.py | 6 ++++++ tests/test_markup.py | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py index bed92c6..52c9a03 100644 --- a/src/main/pages/testenv_page.py +++ b/src/main/pages/testenv_page.py @@ -24,3 +24,9 @@ def click_btn_id(self): def click_submit_btn(self): self.driver.find_element_by_id('Submit').click() return self + + def select_checkboxes_under_parent(self): + checkboxes = self.driver.find_element(By.XPATH, "//*[contains(@class,'test-form')]").find_elements(By.XPATH, ".//*[@class='input1']") + for ch in checkboxes: + ch.click() + return self diff --git a/tests/test_markup.py b/tests/test_markup.py index fc2e66c..a424168 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -27,6 +27,14 @@ def test_select_checkboxes(self, setup_method): test_page.click_form_submit_btn() test_page.select_checkboxes() # should be healed + def test_select_checkboxes_under_parent(self, setup_method): + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.select_checkboxes_under_parent() + test_page.click_form_submit_btn() + test_page.select_checkboxes_under_parent() + def test_button_click_with_id(self, setup_method): test_page = TestEnvPage(self.driver) From 5ae16561dd92730a1dab3365d933dfb3d5a705fe Mon Sep 17 00:00:00 2001 From: Elena Date: Mon, 25 Apr 2022 18:09:22 +0300 Subject: [PATCH 10/78] Refactor framework --- {infra/db => db}/sql/init.sql | 0 .../docker-compose.yml => docker-compose.yml | 0 src/main/constants/locator_type.py | 12 ++ .../callback_locators.cpython-38.pyc | Bin 619 -> 0 bytes .../callback_locators.cpython-39.pyc | Bin 619 -> 0 bytes .../markup_locators.cpython-39.pyc | Bin 1309 -> 0 bytes src/main/locators/callback_locators.py | 9 -- src/main/locators/markup_locators.py | 25 ----- src/main/pages/callback_page.py | 17 ++- src/main/pages/markup_page.py | 86 +------------- src/main/pages/testenv_page.py | 22 ++-- src/main/search/context.py | 37 ++++++ .../search/locators/class_name_strategy.py | 7 ++ src/main/search/locators/css_strategy.py | 15 +++ src/main/search/locators/id_strategy.py | 15 +++ .../search/locators/link_text_strategy.py | 15 +++ src/main/search/locators/name_strategy.py | 15 +++ .../locators/partial_link_text_strategy.py | 15 +++ src/main/search/locators/tag_strategy.py | 15 +++ src/main/search/locators/xpath_strategy.py | 15 +++ src/main/search/strategy.py | 7 ++ tests/test_base.py | 1 + tests/test_callback.py | 20 ---- tests/test_css.py | 83 ++++++++++++++ tests/test_general.py | 14 +++ tests/test_markup.py | 72 ------------ tests/test_parent_child.py | 42 +++++++ tests/test_semantic.py | 63 +++++++++++ tests/test_wait.py | 21 ++++ tests/test_xpath.py | 105 ++++++++++++++++++ 30 files changed, 528 insertions(+), 220 deletions(-) rename {infra/db => db}/sql/init.sql (100%) rename infra/docker-compose.yml => docker-compose.yml (100%) create mode 100644 src/main/constants/locator_type.py delete mode 100644 src/main/locators/__pycache__/callback_locators.cpython-38.pyc delete mode 100644 src/main/locators/__pycache__/callback_locators.cpython-39.pyc delete mode 100644 src/main/locators/__pycache__/markup_locators.cpython-39.pyc delete mode 100644 src/main/locators/callback_locators.py delete mode 100644 src/main/locators/markup_locators.py create mode 100644 src/main/search/context.py create mode 100644 src/main/search/locators/class_name_strategy.py create mode 100644 src/main/search/locators/css_strategy.py create mode 100644 src/main/search/locators/id_strategy.py create mode 100644 src/main/search/locators/link_text_strategy.py create mode 100644 src/main/search/locators/name_strategy.py create mode 100644 src/main/search/locators/partial_link_text_strategy.py create mode 100644 src/main/search/locators/tag_strategy.py create mode 100644 src/main/search/locators/xpath_strategy.py create mode 100644 src/main/search/strategy.py delete mode 100644 tests/test_callback.py create mode 100644 tests/test_css.py create mode 100644 tests/test_general.py delete mode 100644 tests/test_markup.py create mode 100644 tests/test_parent_child.py create mode 100644 tests/test_semantic.py create mode 100644 tests/test_wait.py create mode 100644 tests/test_xpath.py diff --git a/infra/db/sql/init.sql b/db/sql/init.sql similarity index 100% rename from infra/db/sql/init.sql rename to db/sql/init.sql diff --git a/infra/docker-compose.yml b/docker-compose.yml similarity index 100% rename from infra/docker-compose.yml rename to docker-compose.yml diff --git a/src/main/constants/locator_type.py b/src/main/constants/locator_type.py new file mode 100644 index 0000000..3c5beec --- /dev/null +++ b/src/main/constants/locator_type.py @@ -0,0 +1,12 @@ +import enum + + +class LocatorType(enum.Enum): + xpath = 'xpath', + css = 'css', + id = 'id', + link_text = 'link text', + name = 'name', + partial_link_text = 'partial link text', + tag = 'tag', + class_name = 'class name' diff --git a/src/main/locators/__pycache__/callback_locators.cpython-38.pyc b/src/main/locators/__pycache__/callback_locators.cpython-38.pyc deleted file mode 100644 index 7ba6e49de1a1367cfcd7620b7aca2b5583d4195d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 619 zcmah`y-ve05O#jrhL#E;bz~?N1Cc1*5k-|cz<@e{5K@}Ta&1d3IB|#_M7zSv@Jd;E z1U4ovNh3lBIO|S#-~IaTuH9}TpaVQ6Lj$3Y%&3wL#t2w-KwKh*nDGY8(W~Vd%)CXe zMZxonpiYs~1%)GsL!QYD&tj%$Gs~+n+pDvh*I;$XZ=}w(q(sYDtq%u-d7`xpr&NZT zEJJlXqJk)O((Mt(dhTp}^hZb%F{1f@Q85qXa~VYwrDf1pPYH>6wJUUtdERknN{6{V zo48KuU>uTwW1KcI4kSxNrte}56~xA~53}N;Brg7wc8b}y(PEY@w57SyW#V4Nz*MPs z6lWL9#n=7#%)jD9@Nk&~{tb_$TEZe%{-Ol?{DlOO;QeT&7c%r!O#J{B>x<3KeM*Fw z6Z(it(kd$`7ZT<101;C0AvD(>r-jHY6@D|Adn+3{e)h9#bk z!ypd`NoXtx$(w}4if6LGhlD@{S=}7Nti+Zi_C0yW&bA-5v+PA%MrgB1Jlb&x)A|FY z#gCQd`;lEmb3vs@7FoPS{pHHcRk95bEh>&qSro%!BU$Y{VpPf*WzS?2xm>Q&;tO3+ nUGxfE-+=?RjE_S%udCUUV5ao9^H|h^lsve$ySXWFG=;QMD-Ik=#Gytw1;hzal(Zl~DiII;%09 zk}UBeGFu^Zh>Mte0Z*s^>2B$ek3nxKUDp&z0t7W4NlWToYBJVZ`8UE(`b9 zr75RjmXf*cm%>(I9+J(zgfEUp6vs4;iexEy;|U*S>Czz2%9xymM+uJ7qj)|j(8Qe3 zLT`^JOiW%AlWzcXG($BsLkF%(&tEVqu3Bn)lN&oDPIZ9t=fD;_L-k{Ws6)*+L9&Pj zk}YjINRE^YkZQz)9kR-m#SWpp2@?sYFw+XPLjZz9_4#(SCy!fK7O3S8rt8Cuc~N>6 zxtK>@H9)vN>v9t!s5vT5o`*Lqtb}J^^+eETa9a;DU!HWeZ@k;Ta3N zGY6`93DhTgT0pB3EmteGU{acAI4&p?cQEl!0XuzL<7{07h+0_ str: + pass diff --git a/src/main/search/locators/css_strategy.py b/src/main/search/locators/css_strategy.py new file mode 100644 index 0000000..a38670c --- /dev/null +++ b/src/main/search/locators/css_strategy.py @@ -0,0 +1,15 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By + +from src.main.search.strategy import Strategy + + +class CssStrategy(Strategy): + driver: webdriver + + def __init__(self, driver1): + self.driver = driver1 + + def do_action(self, selector) -> str: + element = self.driver.find_element(By.CSS_SELECTOR, selector) + return element.is_displayed() diff --git a/src/main/search/locators/id_strategy.py b/src/main/search/locators/id_strategy.py new file mode 100644 index 0000000..b9c0c20 --- /dev/null +++ b/src/main/search/locators/id_strategy.py @@ -0,0 +1,15 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By + +from src.main.search.strategy import Strategy + + +class IdStrategy(Strategy): + driver: webdriver + + def __init__(self, driver1): + self.driver = driver1 + + def do_action(self, selector) -> str: + element = self.driver.find_element(By.ID, selector) + return element.is_displayed() diff --git a/src/main/search/locators/link_text_strategy.py b/src/main/search/locators/link_text_strategy.py new file mode 100644 index 0000000..7fa9cf0 --- /dev/null +++ b/src/main/search/locators/link_text_strategy.py @@ -0,0 +1,15 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By + +from src.main.search.strategy import Strategy + + +class LinkTextStrategy(Strategy): + driver: webdriver + + def __init__(self, driver1): + self.driver = driver1 + + def do_action(self, selector) -> str: + element = self.driver.find_element(By.LINK_TEXT, selector) + return element.is_displayed() diff --git a/src/main/search/locators/name_strategy.py b/src/main/search/locators/name_strategy.py new file mode 100644 index 0000000..a2452c6 --- /dev/null +++ b/src/main/search/locators/name_strategy.py @@ -0,0 +1,15 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By + +from src.main.search.strategy import Strategy + + +class NameStrategy(Strategy): + driver: webdriver + + def __init__(self, driver1): + self.driver = driver1 + + def do_action(self, selector) -> str: + element = self.driver.find_element(By.NAME, selector) + return element.is_displayed() diff --git a/src/main/search/locators/partial_link_text_strategy.py b/src/main/search/locators/partial_link_text_strategy.py new file mode 100644 index 0000000..5da8397 --- /dev/null +++ b/src/main/search/locators/partial_link_text_strategy.py @@ -0,0 +1,15 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By + +from src.main.search.strategy import Strategy + + +class PartialLinkTextStrategy(Strategy): + driver: webdriver + + def __init__(self, driver1): + self.driver = driver1 + + def do_action(self, selector) -> str: + element = self.driver.find_element(By.PARTIAL_LINK_TEXT, selector) + return element.is_displayed() diff --git a/src/main/search/locators/tag_strategy.py b/src/main/search/locators/tag_strategy.py new file mode 100644 index 0000000..afcae87 --- /dev/null +++ b/src/main/search/locators/tag_strategy.py @@ -0,0 +1,15 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By + +from src.main.search.strategy import Strategy + + +class TagStrategy(Strategy): + driver: webdriver + + def __init__(self, driver1): + self.driver = driver1 + + def do_action(self, selector) -> str: + element = self.driver.find_element(By.TAG_NAME, selector) + return element.is_displayed() diff --git a/src/main/search/locators/xpath_strategy.py b/src/main/search/locators/xpath_strategy.py new file mode 100644 index 0000000..e500d9f --- /dev/null +++ b/src/main/search/locators/xpath_strategy.py @@ -0,0 +1,15 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By + +from src.main.search.strategy import Strategy + + +class XpathStrategy(Strategy): + driver: webdriver + + def __init__(self, driver1): + self.driver = driver1 + + def do_action(self, selector) -> str: + element = self.driver.find_element(By.XPATH, selector) + return element.is_displayed() diff --git a/src/main/search/strategy.py b/src/main/search/strategy.py new file mode 100644 index 0000000..3458fa3 --- /dev/null +++ b/src/main/search/strategy.py @@ -0,0 +1,7 @@ +from abc import ABC + + +class Strategy(ABC): + + def do_action(self, selector) -> str: + pass diff --git a/tests/test_base.py b/tests/test_base.py index 3d15dc6..9b7db97 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -13,6 +13,7 @@ def setup_method(self): command_executor="http://localhost:8085/", desired_capabilities=webdriver.DesiredCapabilities.CHROME, options=options) + return self.driver def teardown_method(self, method): diff --git a/tests/test_callback.py b/tests/test_callback.py deleted file mode 100644 index 288e4d1..0000000 --- a/tests/test_callback.py +++ /dev/null @@ -1,20 +0,0 @@ - -from src.main.pages.callback_page import CallbackPage - -from test_base import TestBase - - -class TestCallback(TestBase): - - def test_css_locators(self, setup_method): - self.driver = setup_method - callback_page = CallbackPage(self.driver) - - callback_page.open_browser() - callback_page.click_add_square_button() - result = callback_page.verify_square_element() - assert result == True - - callback_page.click_update_square_button() - result = callback_page.verify_square_element() # should be healed - assert result == True diff --git a/tests/test_css.py b/tests/test_css.py new file mode 100644 index 0000000..aa2d9ae --- /dev/null +++ b/tests/test_css.py @@ -0,0 +1,83 @@ +from src.main.constants.locator_type import LocatorType +from src.main.pages.callback_page import CallbackPage +from src.main.pages.testenv_page import TestEnvPage +from tests.test_base import TestBase + + +class TestCss(TestBase): + + def test_css_attribute(self, setup_method): + self.driver = setup_method + callback_page = CallbackPage(self.driver) + + callback_page.open_browser() + callback_page.click_add_square_button() + result = callback_page.verify_square_element() + assert result == True + + callback_page.click_update_square_button() + result = callback_page.verify_square_element() # should be healed + assert result == True + + def test_css_id(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "#change_id") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "#change_id") + + def test_css_id_special_character(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "input#change\\:name") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "input#change\\:name") + + def test_css_element(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "test_tag") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "test_tag") + + def test_css_disabled(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "input:disabled") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "input:disabled") + + def test_css_enabled(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "textarea:enabled") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "textarea:enabled") + + def test_css_checked(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "input:checked") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "input:checked") + + def test_css_class_name(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, ".test_class") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, ".test_class") diff --git a/tests/test_general.py b/tests/test_general.py new file mode 100644 index 0000000..657cf4a --- /dev/null +++ b/tests/test_general.py @@ -0,0 +1,14 @@ +from src.main.pages.testenv_page import TestEnvPage +from tests.test_base import TestBase + + +class TestGeneral(TestBase): + + def test_select_checkboxes(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.select_checkboxes() + test_page.click_form_submit_btn() + test_page.select_checkboxes() # should be healed diff --git a/tests/test_markup.py b/tests/test_markup.py deleted file mode 100644 index a424168..0000000 --- a/tests/test_markup.py +++ /dev/null @@ -1,72 +0,0 @@ -from selenium import webdriver - -from src.main.pages.markup_page import * -from src.main.pages.testenv_page import TestEnvPage -from test_base import TestBase - - -class TestMarkup(TestBase): - - # Markup_Page: different locator types with string property - def test_button_click_specific_find_element(self, setup_method): - main_page = MarkupPage(self.driver) - - main_page.open_browser() - main_page.click_test_button() - main_page.confirm_alert() - - main_page.generate_markup() - main_page.click_test_button() # should be healed - main_page.confirm_alert() - - def test_select_checkboxes(self, setup_method): - test_page = TestEnvPage(self.driver) - - test_page.open_browser() - test_page.select_checkboxes() - test_page.click_form_submit_btn() - test_page.select_checkboxes() # should be healed - - def test_select_checkboxes_under_parent(self, setup_method): - test_page = TestEnvPage(self.driver) - - test_page.open_browser() - test_page.select_checkboxes_under_parent() - test_page.click_form_submit_btn() - test_page.select_checkboxes_under_parent() - - def test_button_click_with_id(self, setup_method): - test_page = TestEnvPage(self.driver) - - test_page.open_browser() - test_page.click_btn_id() - test_page.click_submit_btn() - test_page.click_btn_id() # should be healed - - # Markup_Page_By: different locator types with By in property - def test_button_click_find_by(self, setup_method): - markup_page_by = MarkupPageBy(self.driver) - - markup_page_by.open_browser() - markup_page_by.click_button_for_invisible() - markup_page_by.check_that_button_invisible() - - markup_page_by.open_browser() - markup_page_by.click_test_button() - markup_page_by.confirm_alert() - - markup_page_by.generate_markup() - markup_page_by.click_test_button() # should be healed - markup_page_by.confirm_alert() - - # Markup_Page_Request: different locator types with By in request - def test_button_click_find_request(self, setup_method): - markup_page_request = MarkupPageRequest(self.driver) - - markup_page_request.open_browser() - markup_page_request.click_test_button() - markup_page_request.confirm_alert() - - markup_page_request.generate_markup() - markup_page_request.click_test_button() # should be healed - markup_page_request.confirm_alert() diff --git a/tests/test_parent_child.py b/tests/test_parent_child.py new file mode 100644 index 0000000..65df271 --- /dev/null +++ b/tests/test_parent_child.py @@ -0,0 +1,42 @@ +from src.main.constants.locator_type import LocatorType +from src.main.pages.testenv_page import TestEnvPage +from tests.test_base import TestBase + + +class TestParentChild(TestBase): + + def test_select_checkboxes_under_parent(self, setup_method): + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.select_checkboxes_under_parent() + test_page.click_form_submit_btn() + test_page.select_checkboxes_under_parent() + + def test_parent_xpath(self, setup_method): + # "parent::" + self.driver = setup_method + testenv_page = TestEnvPage(self.driver) + + testenv_page.open_browser() + testenv_page.find_test_element(LocatorType.xpath, "") + testenv_page.click_submit_btn() + testenv_page.find_test_element(LocatorType.xpath, "") + + def test_css_first_child(self, setup_method): + self.driver = setup_method + testenv_page = TestEnvPage(self.driver) + + testenv_page.open_browser() + testenv_page.find_test_element(LocatorType.css, "test_tag:first-child") + testenv_page.click_submit_btn() + testenv_page.find_test_element(LocatorType.css, "test_tag:first-child") + + def test_css_last_child(self, setup_method): + self.driver = setup_method + testenv_page = TestEnvPage(self.driver) + + testenv_page.open_browser() + testenv_page.find_test_element(LocatorType.css, "child_tag:last-child") + testenv_page.click_submit_btn() + testenv_page.find_test_element(LocatorType.css, "child_tag:last-child") diff --git a/tests/test_semantic.py b/tests/test_semantic.py new file mode 100644 index 0000000..20097ff --- /dev/null +++ b/tests/test_semantic.py @@ -0,0 +1,63 @@ +from src.main.constants.locator_type import LocatorType +from src.main.pages.markup_page import MarkupPage +from src.main.pages.testenv_page import TestEnvPage +from tests.test_base import TestBase + + +class TestSemantic(TestBase): + + def test_semantic_class_name(self, setup_method): + main_page = MarkupPage(self.driver) + + main_page.open_browser() + main_page.click_test_button() + main_page.confirm_alert() + + main_page.generate_markup() + main_page.click_test_button() # should be healed + main_page.confirm_alert() + + def test_semantic_id(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.id, "change_id") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.id, "change_id") # should be healed + + def test_semantic_link_text(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.link_text, "Change: LinkText, PartialLinkText") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.link_text, "Change: LinkText, PartialLinkText") # should be healed + + def test_semantic_partial_link_text(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.partial_link_text, "PartialLinkText") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.partial_link_text, "PartialLinkText") # should be healed + + def test_semantic_name(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.name, "change_name") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.name, "change_name") # should be healed + + def test_semantic_element(self, setup_method): + self.driver = setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.tag, "test_tag") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.tag, "test_tag") # should be healed diff --git a/tests/test_wait.py b/tests/test_wait.py new file mode 100644 index 0000000..b4a92a9 --- /dev/null +++ b/tests/test_wait.py @@ -0,0 +1,21 @@ +from src.main.pages.markup_page import MarkupPage +from tests.test_base import TestBase + + +class TestWait(TestBase): + + def test_conditional_wait(self,setup_method): + self.driver=setup_method + markup_page=MarkupPage(self.driver) + + markup_page.open_browser() + markup_page.click_test_button() + markup_page.confirm_alert() + + markup_page.generate_markup() + markup_page.click_test_button() #should be healed + markup_page.confirm_alert() + + markup_page.generate_markup() + markup_page.click_test_button_wait(5) #should be healed + markup_page.confirm_alert() diff --git a/tests/test_xpath.py b/tests/test_xpath.py new file mode 100644 index 0000000..5b3a224 --- /dev/null +++ b/tests/test_xpath.py @@ -0,0 +1,105 @@ +from src.main.constants.locator_type import LocatorType +from src.main.pages.testenv_page import TestEnvPage +from tests.test_base import TestBase + + +class TestXpath(TestBase): + + def test_xpath_special_character(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change:name']") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change:name']") + + def test_xpath_following(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/following::test_tag") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/following::test_tag") + + def test_xpath_contains(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//input[contains(@class, 'test')]") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//input[contains(@class, 'test')]") + + def test_xpath_not_contains(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//input[not(contains(@class, 'input1'))]") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//input[not(contains(@class, 'input1'))]") + + def test_xpath_following_sibling(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]/following-sibling::*") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]/following-sibling::*") + + def test_xpath_ancestor(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "(//*[starts-with(@class, 'test')]/ancestor::div[@class='healenium-form validate-form']//input)[1]") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "(//*[starts-with(@class, 'test')]/ancestor::div[@class='healenium-form validate-form']//input)[1]") + + def test_xpath_or(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' or @id='omg']") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' or @id='omg']") + + def test_xpath_and(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' and @type='text']") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' and @type='text']") + + def test_xpath_starts_with(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]") + + def test_xpath_precending(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/preceding::*[@id='change_id']") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/preceding::*[@id='change_id']") + + def test_xpath_descendant(self,setup_method): + self.driver=setup_method + test_page = TestEnvPage(self.driver) + + test_page.open_browser() + test_page.find_test_element(LocatorType.xpath, "//*[@id='descendant_change']/descendant::input") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.xpath, "//*[@id='descendant_change']/descendant::input") From eb173f910b9a23d8a6fe5875d07306e12a54be60 Mon Sep 17 00:00:00 2001 From: Elena Date: Mon, 25 Apr 2022 18:43:17 +0300 Subject: [PATCH 11/78] CI-test --- tests/test_general.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_general.py b/tests/test_general.py index 657cf4a..b5a307e 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -1,3 +1,5 @@ +from selenium import webdriver + from src.main.pages.testenv_page import TestEnvPage from tests.test_base import TestBase @@ -5,7 +7,13 @@ class TestGeneral(TestBase): def test_select_checkboxes(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() From 7dbd6ed91ce3df0588823d16bf7a76a44506ebc5 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 11:48:31 +0300 Subject: [PATCH 12/78] CI-test css --- tests/test_css.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/test_css.py b/tests/test_css.py index aa2d9ae..d26ece9 100644 --- a/tests/test_css.py +++ b/tests/test_css.py @@ -1,10 +1,24 @@ +import pytest +from selenium import webdriver + from src.main.constants.locator_type import LocatorType from src.main.pages.callback_page import CallbackPage from src.main.pages.testenv_page import TestEnvPage from tests.test_base import TestBase -class TestCss(TestBase): +class TestCss: + + @pytest.fixture() + def setup_method(self): + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) + return self.driver def test_css_attribute(self, setup_method): self.driver = setup_method @@ -81,3 +95,6 @@ def test_css_class_name(self, setup_method): test_page.find_test_element(LocatorType.css, ".test_class") test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, ".test_class") + + def teardown_method(self): + self.driver.quit() \ No newline at end of file From 06b9dae7c5d79063b290a9f94594308eb37ad6fa Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 12:16:59 +0300 Subject: [PATCH 13/78] CI-css --- tests/test_css.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_css.py b/tests/test_css.py index d26ece9..0d7ed07 100644 --- a/tests/test_css.py +++ b/tests/test_css.py @@ -15,7 +15,7 @@ def setup_method(self): options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", + command_executor="http://10.6.223.91:8085/", desired_capabilities=webdriver.DesiredCapabilities.CHROME, options=options) return self.driver From 16ce9c22c61563411f251bba9251c3757faac634 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 12:40:16 +0300 Subject: [PATCH 14/78] Reformat for CI --- tests/test_css.py | 82 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/tests/test_css.py b/tests/test_css.py index 0d7ed07..f14a8f3 100644 --- a/tests/test_css.py +++ b/tests/test_css.py @@ -9,19 +9,14 @@ class TestCss: - @pytest.fixture() - def setup_method(self): + def test_css_attribute(self, setup_method): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") self.driver = webdriver.Remote( - command_executor="http://10.6.223.91:8085/", + command_executor="http://localhost:8085/", desired_capabilities=webdriver.DesiredCapabilities.CHROME, options=options) - return self.driver - - def test_css_attribute(self, setup_method): - self.driver = setup_method callback_page = CallbackPage(self.driver) callback_page.open_browser() @@ -33,8 +28,16 @@ def test_css_attribute(self, setup_method): result = callback_page.verify_square_element() # should be healed assert result == True + self.driver.quit() + def test_css_id(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() @@ -42,8 +45,16 @@ def test_css_id(self, setup_method): test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "#change_id") + self.driver.quit() + def test_css_id_special_character(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() @@ -51,8 +62,16 @@ def test_css_id_special_character(self, setup_method): test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "input#change\\:name") + self.driver.quit() + def test_css_element(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() @@ -60,8 +79,16 @@ def test_css_element(self, setup_method): test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "test_tag") + self.driver.quit() + def test_css_disabled(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() @@ -69,8 +96,16 @@ def test_css_disabled(self, setup_method): test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "input:disabled") + self.driver.quit() + def test_css_enabled(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() @@ -78,8 +113,16 @@ def test_css_enabled(self, setup_method): test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "textarea:enabled") + self.driver.quit() + def test_css_checked(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() @@ -87,8 +130,16 @@ def test_css_checked(self, setup_method): test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "input:checked") + self.driver.quit() + def test_css_class_name(self, setup_method): - self.driver = setup_method + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) test_page = TestEnvPage(self.driver) test_page.open_browser() @@ -96,5 +147,4 @@ def test_css_class_name(self, setup_method): test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, ".test_class") - def teardown_method(self): - self.driver.quit() \ No newline at end of file + self.driver.quit() From 58c3f5f92ddfa52382f604e2a405668ffebde993 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 12:42:30 +0300 Subject: [PATCH 15/78] Reformat CI css --- tests/test_css.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_css.py b/tests/test_css.py index f14a8f3..21cb668 100644 --- a/tests/test_css.py +++ b/tests/test_css.py @@ -9,7 +9,7 @@ class TestCss: - def test_css_attribute(self, setup_method): + def test_css_attribute(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") @@ -30,7 +30,7 @@ def test_css_attribute(self, setup_method): self.driver.quit() - def test_css_id(self, setup_method): + def test_css_id(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") @@ -47,7 +47,7 @@ def test_css_id(self, setup_method): self.driver.quit() - def test_css_id_special_character(self, setup_method): + def test_css_id_special_character(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") @@ -64,7 +64,7 @@ def test_css_id_special_character(self, setup_method): self.driver.quit() - def test_css_element(self, setup_method): + def test_css_element(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") @@ -81,7 +81,7 @@ def test_css_element(self, setup_method): self.driver.quit() - def test_css_disabled(self, setup_method): + def test_css_disabled(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") @@ -98,7 +98,7 @@ def test_css_disabled(self, setup_method): self.driver.quit() - def test_css_enabled(self, setup_method): + def test_css_enabled(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") @@ -115,7 +115,7 @@ def test_css_enabled(self, setup_method): self.driver.quit() - def test_css_checked(self, setup_method): + def test_css_checked(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") @@ -132,7 +132,7 @@ def test_css_checked(self, setup_method): self.driver.quit() - def test_css_class_name(self, setup_method): + def test_css_class_name(self): options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') options.add_argument("--disable-dev-shm-usage") From 10c091b4a226f09e323588f9f1fb0e9e6c3dec42 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 15:37:55 +0300 Subject: [PATCH 16/78] CI-test refactor --- tests/test_css.py | 46 ++++++++-------------------------------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/tests/test_css.py b/tests/test_css.py index 21cb668..9dea552 100644 --- a/tests/test_css.py +++ b/tests/test_css.py @@ -1,10 +1,8 @@ -import pytest from selenium import webdriver from src.main.constants.locator_type import LocatorType from src.main.pages.callback_page import CallbackPage from src.main.pages.testenv_page import TestEnvPage -from tests.test_base import TestBase class TestCss: @@ -31,72 +29,44 @@ def test_css_attribute(self): self.driver.quit() def test_css_id(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.css, "#change_id") test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "#change_id") - self.driver.quit() + test_page.close() def test_css_id_special_character(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.css, "input#change\\:name") test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "input#change\\:name") - self.driver.quit() + test_page.close() def test_css_element(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.css, "test_tag") test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "test_tag") - self.driver.quit() + test_page.close() def test_css_disabled(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.css, "input:disabled") test_page.click_submit_btn() test_page.find_test_element(LocatorType.css, "input:disabled") - self.driver.quit() + test_page.close() def test_css_enabled(self): options = webdriver.ChromeOptions() From 8815062eacfee830e59bc6a3522939dc761787d4 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 15:39:24 +0300 Subject: [PATCH 17/78] Extract webdriver --- tests/test_css.py | 153 ++++++++++++++++++++++------------------------ 1 file changed, 73 insertions(+), 80 deletions(-) diff --git a/tests/test_css.py b/tests/test_css.py index 9dea552..e779888 100644 --- a/tests/test_css.py +++ b/tests/test_css.py @@ -38,83 +38,76 @@ def test_css_id(self): test_page.close() - def test_css_id_special_character(self): - test_page = TestEnvPage() - - test_page.open_browser() - test_page.find_test_element(LocatorType.css, "input#change\\:name") - test_page.click_submit_btn() - test_page.find_test_element(LocatorType.css, "input#change\\:name") - - test_page.close() - - def test_css_element(self): - test_page = TestEnvPage() - - test_page.open_browser() - test_page.find_test_element(LocatorType.css, "test_tag") - test_page.click_submit_btn() - test_page.find_test_element(LocatorType.css, "test_tag") - - test_page.close() - - def test_css_disabled(self): - test_page = TestEnvPage() - - test_page.open_browser() - test_page.find_test_element(LocatorType.css, "input:disabled") - test_page.click_submit_btn() - test_page.find_test_element(LocatorType.css, "input:disabled") - - test_page.close() - - def test_css_enabled(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) - - test_page.open_browser() - test_page.find_test_element(LocatorType.css, "textarea:enabled") - test_page.click_submit_btn() - test_page.find_test_element(LocatorType.css, "textarea:enabled") - - self.driver.quit() - - def test_css_checked(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) - - test_page.open_browser() - test_page.find_test_element(LocatorType.css, "input:checked") - test_page.click_submit_btn() - test_page.find_test_element(LocatorType.css, "input:checked") - - self.driver.quit() - - def test_css_class_name(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) - - test_page.open_browser() - test_page.find_test_element(LocatorType.css, ".test_class") - test_page.click_submit_btn() - test_page.find_test_element(LocatorType.css, ".test_class") - - self.driver.quit() + # def test_css_id_special_character(self): + # test_page = TestEnvPage() + # + # test_page.open_browser() + # test_page.find_test_element(LocatorType.css, "input#change\\:name") + # test_page.click_submit_btn() + # test_page.find_test_element(LocatorType.css, "input#change\\:name") + # + # test_page.close() + # + # def test_css_element(self): + # test_page = TestEnvPage() + # + # test_page.open_browser() + # test_page.find_test_element(LocatorType.css, "test_tag") + # test_page.click_submit_btn() + # test_page.find_test_element(LocatorType.css, "test_tag") + # + # test_page.close() + # + # def test_css_disabled(self): + # test_page = TestEnvPage() + # + # test_page.open_browser() + # test_page.find_test_element(LocatorType.css, "input:disabled") + # test_page.click_submit_btn() + # test_page.find_test_element(LocatorType.css, "input:disabled") + # + # test_page.close() + # + # def test_css_enabled(self): + # test_page = TestEnvPage() + # + # test_page.open_browser() + # test_page.find_test_element(LocatorType.css, "textarea:enabled") + # test_page.click_submit_btn() + # test_page.find_test_element(LocatorType.css, "textarea:enabled") + # + # test_page.close() + # + # def test_css_checked(self): + # options = webdriver.ChromeOptions() + # options.add_argument('--no-sandbox') + # options.add_argument("--disable-dev-shm-usage") + # self.driver = webdriver.Remote( + # command_executor="http://localhost:8085/", + # desired_capabilities=webdriver.DesiredCapabilities.CHROME, + # options=options) + # test_page = TestEnvPage(self.driver) + # + # test_page.open_browser() + # test_page.find_test_element(LocatorType.css, "input:checked") + # test_page.click_submit_btn() + # test_page.find_test_element(LocatorType.css, "input:checked") + # + # self.driver.quit() + # + # def test_css_class_name(self): + # options = webdriver.ChromeOptions() + # options.add_argument('--no-sandbox') + # options.add_argument("--disable-dev-shm-usage") + # self.driver = webdriver.Remote( + # command_executor="http://localhost:8085/", + # desired_capabilities=webdriver.DesiredCapabilities.CHROME, + # options=options) + # test_page = TestEnvPage(self.driver) + # + # test_page.open_browser() + # test_page.find_test_element(LocatorType.css, ".test_class") + # test_page.click_submit_btn() + # test_page.find_test_element(LocatorType.css, ".test_class") + # + # self.driver.quit() From 606a509fb1dc3588088f1a7ae0ab8005dfdef243 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 15:42:10 +0300 Subject: [PATCH 18/78] Refactor test env page --- src/main/pages/testenv_page.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py index a064cb9..0d94132 100644 --- a/src/main/pages/testenv_page.py +++ b/src/main/pages/testenv_page.py @@ -1,3 +1,6 @@ +import logging + +from selenium import webdriver from selenium.webdriver.common.by import By from src.main.pages.base_page import BasePage @@ -8,6 +11,18 @@ class TestEnvPage(BasePage): submit_btn = 'Submit' submit_form_btn = 'Submit_checkbox' + driver: webdriver + + def __init__(self): + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) + # self.driver=webdriver.Chrome() + def open_browser(self): self.driver.get(BasePage.testEnvPageUrl) return self @@ -34,5 +49,9 @@ def select_checkboxes_under_parent(self): return self def find_test_element(self, locator_type, selector): + logging.info("Find element By ") result = Context().set_strategy(self.driver, locator_type).execute_strategy(selector) assert result == True + + def close(self): + self.driver.quit() From a17e6fc3f04bdd78e7406e805a095cd162e129c5 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 15:58:31 +0300 Subject: [PATCH 19/78] Update test webdrivers --- src/main/pages/callback_page.py | 15 ++++ src/main/pages/markup_page.py | 16 ++++ src/main/pages/testenv_page.py | 1 - tests/test_base.py | 20 ----- tests/test_css.py | 145 ++++++++++++++------------------ tests/test_general.py | 18 ++-- tests/test_parent_child.py | 43 ++++++---- tests/test_semantic.py | 44 +++++----- tests/test_wait.py | 16 ++-- tests/test_xpath.py | 80 ++++++++++-------- 10 files changed, 200 insertions(+), 198 deletions(-) delete mode 100644 tests/test_base.py diff --git a/src/main/pages/callback_page.py b/src/main/pages/callback_page.py index b658a44..b02a498 100644 --- a/src/main/pages/callback_page.py +++ b/src/main/pages/callback_page.py @@ -1,3 +1,4 @@ +from selenium import webdriver from selenium.webdriver.common.by import By from src.main.pages.base_page import BasePage @@ -11,6 +12,17 @@ class CallbackPage(BasePage): test_button = '//custom-square[contains(@c, "red")]' test_button_css = '[c="red"]' + driver: webdriver + + def __init__(self): + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) + def open_browser(self): self.driver.get(BasePage.callbackTestPageUrl) return self @@ -28,3 +40,6 @@ def click_update_square_button(self): def click_remove_square_button(self): self.driver.find_element(By.XPATH, self.remove_square_button).click() + + def close(self): + self.driver.quit() diff --git a/src/main/pages/markup_page.py b/src/main/pages/markup_page.py index 584ca6c..50015f3 100644 --- a/src/main/pages/markup_page.py +++ b/src/main/pages/markup_page.py @@ -1,3 +1,5 @@ +from selenium import webdriver + from src.main.pages.base_page import BasePage @@ -8,6 +10,17 @@ class MarkupPage(BasePage): # by class name test_button = 'default-btn' + driver: webdriver + + def __init__(self): + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options) + def open_browser(self): self.driver.get(BasePage.mainPageUrl) return self @@ -19,3 +32,6 @@ def generate_markup(self): def click_test_button(self): self.driver.find_element_by_class_name(self.test_button).click() + + def close(self): + self.driver.quit() diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py index 0d94132..0ec5f8a 100644 --- a/src/main/pages/testenv_page.py +++ b/src/main/pages/testenv_page.py @@ -21,7 +21,6 @@ def __init__(self): command_executor="http://localhost:8085/", desired_capabilities=webdriver.DesiredCapabilities.CHROME, options=options) - # self.driver=webdriver.Chrome() def open_browser(self): self.driver.get(BasePage.testEnvPageUrl) diff --git a/tests/test_base.py b/tests/test_base.py deleted file mode 100644 index 9b7db97..0000000 --- a/tests/test_base.py +++ /dev/null @@ -1,20 +0,0 @@ -import pytest -from selenium import webdriver - - -class TestBase: - - @pytest.fixture() - def setup_method(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - - return self.driver - - def teardown_method(self, method): - self.driver.quit() diff --git a/tests/test_css.py b/tests/test_css.py index e779888..3772205 100644 --- a/tests/test_css.py +++ b/tests/test_css.py @@ -1,5 +1,3 @@ -from selenium import webdriver - from src.main.constants.locator_type import LocatorType from src.main.pages.callback_page import CallbackPage from src.main.pages.testenv_page import TestEnvPage @@ -8,14 +6,7 @@ class TestCss: def test_css_attribute(self): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - callback_page = CallbackPage(self.driver) + callback_page = CallbackPage() callback_page.open_browser() callback_page.click_add_square_button() @@ -26,7 +17,7 @@ def test_css_attribute(self): result = callback_page.verify_square_element() # should be healed assert result == True - self.driver.quit() + callback_page.close() def test_css_id(self): test_page = TestEnvPage() @@ -38,76 +29,62 @@ def test_css_id(self): test_page.close() - # def test_css_id_special_character(self): - # test_page = TestEnvPage() - # - # test_page.open_browser() - # test_page.find_test_element(LocatorType.css, "input#change\\:name") - # test_page.click_submit_btn() - # test_page.find_test_element(LocatorType.css, "input#change\\:name") - # - # test_page.close() - # - # def test_css_element(self): - # test_page = TestEnvPage() - # - # test_page.open_browser() - # test_page.find_test_element(LocatorType.css, "test_tag") - # test_page.click_submit_btn() - # test_page.find_test_element(LocatorType.css, "test_tag") - # - # test_page.close() - # - # def test_css_disabled(self): - # test_page = TestEnvPage() - # - # test_page.open_browser() - # test_page.find_test_element(LocatorType.css, "input:disabled") - # test_page.click_submit_btn() - # test_page.find_test_element(LocatorType.css, "input:disabled") - # - # test_page.close() - # - # def test_css_enabled(self): - # test_page = TestEnvPage() - # - # test_page.open_browser() - # test_page.find_test_element(LocatorType.css, "textarea:enabled") - # test_page.click_submit_btn() - # test_page.find_test_element(LocatorType.css, "textarea:enabled") - # - # test_page.close() - # - # def test_css_checked(self): - # options = webdriver.ChromeOptions() - # options.add_argument('--no-sandbox') - # options.add_argument("--disable-dev-shm-usage") - # self.driver = webdriver.Remote( - # command_executor="http://localhost:8085/", - # desired_capabilities=webdriver.DesiredCapabilities.CHROME, - # options=options) - # test_page = TestEnvPage(self.driver) - # - # test_page.open_browser() - # test_page.find_test_element(LocatorType.css, "input:checked") - # test_page.click_submit_btn() - # test_page.find_test_element(LocatorType.css, "input:checked") - # - # self.driver.quit() - # - # def test_css_class_name(self): - # options = webdriver.ChromeOptions() - # options.add_argument('--no-sandbox') - # options.add_argument("--disable-dev-shm-usage") - # self.driver = webdriver.Remote( - # command_executor="http://localhost:8085/", - # desired_capabilities=webdriver.DesiredCapabilities.CHROME, - # options=options) - # test_page = TestEnvPage(self.driver) - # - # test_page.open_browser() - # test_page.find_test_element(LocatorType.css, ".test_class") - # test_page.click_submit_btn() - # test_page.find_test_element(LocatorType.css, ".test_class") - # - # self.driver.quit() + def test_css_id_special_character(self): + test_page = TestEnvPage() + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "input#change\\:name") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "input#change\\:name") + + test_page.close() + + def test_css_element(self): + test_page = TestEnvPage() + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "test_tag") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "test_tag") + + test_page.close() + + def test_css_disabled(self): + test_page = TestEnvPage() + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "input:disabled") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "input:disabled") + + test_page.close() + + def test_css_enabled(self): + test_page = TestEnvPage() + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "textarea:enabled") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "textarea:enabled") + + test_page.close() + + def test_css_checked(self): + test_page = TestEnvPage() + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, "input:checked") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, "input:checked") + + test_page.close() + + def test_css_class_name(self): + test_page = TestEnvPage() + + test_page.open_browser() + test_page.find_test_element(LocatorType.css, ".test_class") + test_page.click_submit_btn() + test_page.find_test_element(LocatorType.css, ".test_class") + + test_page.close() diff --git a/tests/test_general.py b/tests/test_general.py index b5a307e..37be91f 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -1,22 +1,14 @@ -from selenium import webdriver - from src.main.pages.testenv_page import TestEnvPage -from tests.test_base import TestBase -class TestGeneral(TestBase): +class TestGeneral: - def test_select_checkboxes(self, setup_method): - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) - test_page = TestEnvPage(self.driver) + def test_select_checkboxes(self): + test_page = TestEnvPage() test_page.open_browser() test_page.select_checkboxes() test_page.click_form_submit_btn() test_page.select_checkboxes() # should be healed + + test_page.close() diff --git a/tests/test_parent_child.py b/tests/test_parent_child.py index 65df271..0e7ec9a 100644 --- a/tests/test_parent_child.py +++ b/tests/test_parent_child.py @@ -1,42 +1,49 @@ +from selenium import webdriver + from src.main.constants.locator_type import LocatorType from src.main.pages.testenv_page import TestEnvPage -from tests.test_base import TestBase -class TestParentChild(TestBase): +class TestParentChild: - def test_select_checkboxes_under_parent(self, setup_method): - test_page = TestEnvPage(self.driver) + def test_select_checkboxes_under_parent(self): + test_page = TestEnvPage() test_page.open_browser() test_page.select_checkboxes_under_parent() test_page.click_form_submit_btn() test_page.select_checkboxes_under_parent() - def test_parent_xpath(self, setup_method): - # "parent::" - self.driver = setup_method - testenv_page = TestEnvPage(self.driver) + test_page.close() - testenv_page.open_browser() - testenv_page.find_test_element(LocatorType.xpath, "") - testenv_page.click_submit_btn() - testenv_page.find_test_element(LocatorType.xpath, "") + # def test_parent_xpath(self, setup_method): + # # "parent::" + # self.driver = setup_method + # testenv_page = TestEnvPage(self.driver) + # + # testenv_page.open_browser() + # testenv_page.find_test_element(LocatorType.xpath, "") + # testenv_page.click_submit_btn() + # testenv_page.find_test_element(LocatorType.xpath, "") + # + # self.driver.quit() - def test_css_first_child(self, setup_method): - self.driver = setup_method - testenv_page = TestEnvPage(self.driver) + def test_css_first_child(self): + testenv_page = TestEnvPage() testenv_page.open_browser() testenv_page.find_test_element(LocatorType.css, "test_tag:first-child") testenv_page.click_submit_btn() testenv_page.find_test_element(LocatorType.css, "test_tag:first-child") - def test_css_last_child(self, setup_method): - self.driver = setup_method - testenv_page = TestEnvPage(self.driver) + testenv_page.close() + + def test_css_last_child(self): + testenv_page = TestEnvPage() testenv_page.open_browser() testenv_page.find_test_element(LocatorType.css, "child_tag:last-child") testenv_page.click_submit_btn() testenv_page.find_test_element(LocatorType.css, "child_tag:last-child") + + testenv_page.close() diff --git a/tests/test_semantic.py b/tests/test_semantic.py index 20097ff..418cb5b 100644 --- a/tests/test_semantic.py +++ b/tests/test_semantic.py @@ -1,13 +1,12 @@ from src.main.constants.locator_type import LocatorType from src.main.pages.markup_page import MarkupPage from src.main.pages.testenv_page import TestEnvPage -from tests.test_base import TestBase -class TestSemantic(TestBase): +class TestSemantic: - def test_semantic_class_name(self, setup_method): - main_page = MarkupPage(self.driver) + def test_semantic_class_name(self): + main_page = MarkupPage() main_page.open_browser() main_page.click_test_button() @@ -17,47 +16,54 @@ def test_semantic_class_name(self, setup_method): main_page.click_test_button() # should be healed main_page.confirm_alert() - def test_semantic_id(self, setup_method): - self.driver = setup_method - test_page = TestEnvPage(self.driver) + main_page.close() + + def test_semantic_id(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.id, "change_id") test_page.click_submit_btn() test_page.find_test_element(LocatorType.id, "change_id") # should be healed - def test_semantic_link_text(self, setup_method): - self.driver = setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_semantic_link_text(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.link_text, "Change: LinkText, PartialLinkText") test_page.click_submit_btn() test_page.find_test_element(LocatorType.link_text, "Change: LinkText, PartialLinkText") # should be healed - def test_semantic_partial_link_text(self, setup_method): - self.driver = setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_semantic_partial_link_text(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.partial_link_text, "PartialLinkText") test_page.click_submit_btn() test_page.find_test_element(LocatorType.partial_link_text, "PartialLinkText") # should be healed - def test_semantic_name(self, setup_method): - self.driver = setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_semantic_name(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.name, "change_name") test_page.click_submit_btn() test_page.find_test_element(LocatorType.name, "change_name") # should be healed - def test_semantic_element(self, setup_method): - self.driver = setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_semantic_element(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.tag, "test_tag") test_page.click_submit_btn() test_page.find_test_element(LocatorType.tag, "test_tag") # should be healed + + test_page.close() diff --git a/tests/test_wait.py b/tests/test_wait.py index b4a92a9..1f4321d 100644 --- a/tests/test_wait.py +++ b/tests/test_wait.py @@ -1,12 +1,10 @@ from src.main.pages.markup_page import MarkupPage -from tests.test_base import TestBase -class TestWait(TestBase): +class TestWait: - def test_conditional_wait(self,setup_method): - self.driver=setup_method - markup_page=MarkupPage(self.driver) + def test_conditional_wait(self): + markup_page=MarkupPage() markup_page.open_browser() markup_page.click_test_button() @@ -16,6 +14,8 @@ def test_conditional_wait(self,setup_method): markup_page.click_test_button() #should be healed markup_page.confirm_alert() - markup_page.generate_markup() - markup_page.click_test_button_wait(5) #should be healed - markup_page.confirm_alert() + # markup_page.generate_markup() + # markup_page.click_test_button_wait(5) #should be healed + # markup_page.confirm_alert() + + markup_page.close() diff --git a/tests/test_xpath.py b/tests/test_xpath.py index 5b3a224..95e6dee 100644 --- a/tests/test_xpath.py +++ b/tests/test_xpath.py @@ -1,105 +1,115 @@ from src.main.constants.locator_type import LocatorType from src.main.pages.testenv_page import TestEnvPage -from tests.test_base import TestBase -class TestXpath(TestBase): +class TestXpath: - def test_xpath_special_character(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + def test_xpath_special_character(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[@id='change:name']") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[@id='change:name']") - def test_xpath_following(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_following(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/following::test_tag") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/following::test_tag") - def test_xpath_contains(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_contains(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//input[contains(@class, 'test')]") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//input[contains(@class, 'test')]") - def test_xpath_not_contains(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_not_contains(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//input[not(contains(@class, 'input1'))]") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//input[not(contains(@class, 'input1'))]") - def test_xpath_following_sibling(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_following_sibling(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]/following-sibling::*") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]/following-sibling::*") - def test_xpath_ancestor(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_ancestor(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "(//*[starts-with(@class, 'test')]/ancestor::div[@class='healenium-form validate-form']//input)[1]") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "(//*[starts-with(@class, 'test')]/ancestor::div[@class='healenium-form validate-form']//input)[1]") - def test_xpath_or(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_or(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' or @id='omg']") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' or @id='omg']") - def test_xpath_and(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_and(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' and @type='text']") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_id' and @type='text']") - def test_xpath_starts_with(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_starts_with(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[starts-with(@class, 'test')]") - def test_xpath_precending(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_precending(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/preceding::*[@id='change_id']") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[@id='change_className']/preceding::*[@id='change_id']") - def test_xpath_descendant(self,setup_method): - self.driver=setup_method - test_page = TestEnvPage(self.driver) + test_page.close() + + def test_xpath_descendant(self): + test_page = TestEnvPage() test_page.open_browser() test_page.find_test_element(LocatorType.xpath, "//*[@id='descendant_change']/descendant::input") test_page.click_submit_btn() test_page.find_test_element(LocatorType.xpath, "//*[@id='descendant_change']/descendant::input") + + test_page.close() From b343442e508cfc28e0bc69476d5fbb92536a8584 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 17:28:05 +0300 Subject: [PATCH 20/78] Add tests with wait and parent xpath --- src/main/pages/markup_page.py | 8 ++++++++ tests/test_parent_child.py | 22 +++++++++------------- tests/test_wait.py | 6 +++--- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/pages/markup_page.py b/src/main/pages/markup_page.py index 50015f3..f4c8a1c 100644 --- a/src/main/pages/markup_page.py +++ b/src/main/pages/markup_page.py @@ -1,4 +1,7 @@ from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.support import expected_conditions as ec from src.main.pages.base_page import BasePage @@ -35,3 +38,8 @@ def click_test_button(self): def close(self): self.driver.quit() + + def click_test_button_wait(self, seconds): + test_btn = WebDriverWait(self.driver, seconds).until(ec.visibility_of_element_located((By.XPATH, "//*[contains(@class, 'default-btn')]"))) + test_btn.click() + diff --git a/tests/test_parent_child.py b/tests/test_parent_child.py index 0e7ec9a..e233101 100644 --- a/tests/test_parent_child.py +++ b/tests/test_parent_child.py @@ -1,5 +1,3 @@ -from selenium import webdriver - from src.main.constants.locator_type import LocatorType from src.main.pages.testenv_page import TestEnvPage @@ -16,17 +14,15 @@ def test_select_checkboxes_under_parent(self): test_page.close() - # def test_parent_xpath(self, setup_method): - # # "parent::" - # self.driver = setup_method - # testenv_page = TestEnvPage(self.driver) - # - # testenv_page.open_browser() - # testenv_page.find_test_element(LocatorType.xpath, "") - # testenv_page.click_submit_btn() - # testenv_page.find_test_element(LocatorType.xpath, "") - # - # self.driver.quit() + def test_parent_xpath(self): + testenv_page = TestEnvPage() + + testenv_page.open_browser() + testenv_page.find_test_element(LocatorType.xpath, "(//*[@class='input1']//parent::*[contains(@class, 'input1')])[8]") + testenv_page.click_submit_btn() + testenv_page.find_test_element(LocatorType.xpath, "(//*[@class='input1']//parent::*[contains(@class, 'input1')])[8]") + + testenv_page.close() def test_css_first_child(self): testenv_page = TestEnvPage() diff --git a/tests/test_wait.py b/tests/test_wait.py index 1f4321d..7a6dd44 100644 --- a/tests/test_wait.py +++ b/tests/test_wait.py @@ -14,8 +14,8 @@ def test_conditional_wait(self): markup_page.click_test_button() #should be healed markup_page.confirm_alert() - # markup_page.generate_markup() - # markup_page.click_test_button_wait(5) #should be healed - # markup_page.confirm_alert() + markup_page.generate_markup() + markup_page.click_test_button_wait(5) #should be healed + markup_page.confirm_alert() markup_page.close() From 884603ff844b90d200f0ab211674cfc725a37d01 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 17:46:57 +0300 Subject: [PATCH 21/78] Comment wait test --- tests/test_wait.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_wait.py b/tests/test_wait.py index 7a6dd44..1f4321d 100644 --- a/tests/test_wait.py +++ b/tests/test_wait.py @@ -14,8 +14,8 @@ def test_conditional_wait(self): markup_page.click_test_button() #should be healed markup_page.confirm_alert() - markup_page.generate_markup() - markup_page.click_test_button_wait(5) #should be healed - markup_page.confirm_alert() + # markup_page.generate_markup() + # markup_page.click_test_button_wait(5) #should be healed + # markup_page.confirm_alert() markup_page.close() From 1a9958752b5a08e0741903218674155f76daa3f2 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 18:38:26 +0300 Subject: [PATCH 22/78] Update readme --- README.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3fcb6b5..e43c4cd 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ Python 3.9.5 + Pytest project with healenium usage example ## How to start -### 1.Start Healenium backend from 'infra' folder - -```cd infra``` +### 1.Start Healenium backend from 'root' folder ```docker-compose up -d``` @@ -23,21 +21,30 @@ Verify that images ```healenium/hlm-backend:3.2.1```, ```postgres:11-alpine```, |__healenium-example-python (root) |__src |__main - |__locators + |__constants |__pages + |__search |__tests |__selenium tests - |__infra - |__docker-compose.yml + |__docker-compose.yml ``` ### 3.Run test -To run tests in terminal with pytest you need to go to ```cd healenium_selenium\tests``` project folder +To run tests in terminal with pytest you need to go to execute next comands: + +``python3 -m venv env`` + +``source ./env/bin/activate`` + +``python -m pip install -U pytest`` + +``python -m pip install -U selenium`` + +``python -m pytest ./tests/`` -> If you want to execute tests from test_callback.py file, please use the command: ```pytest test_callback.py``` -> And appropriate command for test_markup.py file: ```pytest test_markup.py``` ->> In case you want to run all tests in project use ```pytest``` command +> If you want to execute tests from test_callback.py file, please use the command: ```python -m pytest ./tests/test_css.py``` +>> In case you want to run all tests in project use ```python -m pytest ./tests/``` command ### 4. Monitoring tests running You can monitor tests running. To do this go to ```http://:8086``` From 1a24edfc40a0b85d1de93de082ab0f723adc41ea Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 26 Apr 2022 18:39:35 +0300 Subject: [PATCH 23/78] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e43c4cd..d4d935c 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ To run tests in terminal with pytest you need to go to execute next comands: ``python -m pytest ./tests/`` -> If you want to execute tests from test_callback.py file, please use the command: ```python -m pytest ./tests/test_css.py``` +> If you want to execute tests from specified file, please use the command: ```python -m pytest ./tests/test_css.py``` >> In case you want to run all tests in project use ```python -m pytest ./tests/``` command ### 4. Monitoring tests running From 868fb0965952bc83055b9aedea3da38e62c5930f Mon Sep 17 00:00:00 2001 From: Elena Date: Mon, 6 Jun 2022 14:35:42 +0300 Subject: [PATCH 24/78] Move infra structure --- {db => infra/db}/sql/init.sql | 0 docker-compose.yml => infra/docker-compose.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {db => infra/db}/sql/init.sql (100%) rename docker-compose.yml => infra/docker-compose.yml (100%) diff --git a/db/sql/init.sql b/infra/db/sql/init.sql similarity index 100% rename from db/sql/init.sql rename to infra/db/sql/init.sql diff --git a/docker-compose.yml b/infra/docker-compose.yml similarity index 100% rename from docker-compose.yml rename to infra/docker-compose.yml From 4c6a3e5dd7773d4af332dae6939ab42fcda47a1d Mon Sep 17 00:00:00 2001 From: Anatoli Zaitsau Date: Wed, 22 Jun 2022 14:57:07 +0200 Subject: [PATCH 25/78] update docker-compose file to work with selenoid --- README.md | 4 ++-- infra/docker-compose.yml | 38 ++++++++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d4d935c..b4b314f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Create /db/sql folder on the same level in your project. Add init.sql file into ```$ curl https://raw.githubusercontent.com/healenium/healenium-client/master/example/init.sql -o init.sql``` -Verify that images ```healenium/hlm-backend:3.2.1```, ```postgres:11-alpine```, ```healenium/hlm-selector-imitator:1.1```, ```healenium/hlm-proxy``` are up and running +Verify that images ```healenium/hlm-backend:3.2.2```, ```postgres:14.2-bullseye```, ```healenium/hlm-selector-imitator:1.1```, ```healenium/hlm-proxy``` are up and running ### 2. Project structure ``` @@ -47,4 +47,4 @@ To run tests in terminal with pytest you need to go to execute next comands: >> In case you want to run all tests in project use ```python -m pytest ./tests/``` command ### 4. Monitoring tests running -You can monitor tests running. To do this go to ```http://:8086``` +You can monitor tests running. To do this go to ```http://localhost:8080``` diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 3fa9a57..c9110ce 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -2,12 +2,13 @@ version: "3.9" services: db: - image: postgres:11-alpine + image: postgres:14.2-bullseye + container_name: db + restart: always ports: - "5432:5432" volumes: - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql - restart: always environment: - POSTGRES_DB=healenium - POSTGRES_USER=healenium_user @@ -17,6 +18,8 @@ services: healenium: image: healenium/hlm-backend:3.2.2 + container_name: healenium + restart: on-failure ports: - "7878:7878" links: @@ -40,36 +43,47 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:0.2.5 + image: healenium/hlm-proxy:0.2.5.2 container_name: hlm-proxy - volumes: - - docker-log-volume:/var/log/dockerlogs + restart: on-failure ports: - "8085:8085" environment: - "recovery-tries=1" - "score-cap=.6" - "heal-enabled=true" + - SELENIUM_PATH=/wd/hub # - SELENIUM_HOST=hlm-selenium-webview # - HEALENIUM_HOST=healenium # - IMITATE_HOST=selector-imitator networks: - healenium - hlm-selenium-webview: - image: healenium/hlm-selenium-4-standalone-tigervnc:firefox98.0.1_chrome99.0.4844.82_edge99.0.1150.46 - restart: on-failure + selenoid: + image: healenium/hlm-selenoid:0.1.0 container_name: hlm-selenium-webview + restart: on-failure ports: - - "8086:6080" - "4444:4444" + environment: + - "PROXY_CONTAINER_NAME=hlm-proxy" volumes: - - docker-log-volume:/var/log/dockerlogs + - .:/etc/selenoid/:ro + - /var/run/docker.sock:/var/run/docker.sock + command: ["-container-network", "healenium"] networks: - healenium -volumes: - docker-log-volume: + selenoid-ui: + image: aerokube/selenoid-ui:1.10.4 + container_name: hlm-selenium-webview-ui + restart: on-failure + ports: + - "8080:8080" + command: ["--selenoid-uri", "http://hlm-selenium-webview:4444/"] + networks: + - healenium networks: healenium: + name: healenium \ No newline at end of file From 83e262957a86e9bf9fdc0d9c69c5f468481de1dc Mon Sep 17 00:00:00 2001 From: Anatoli Zaitsau Date: Wed, 22 Jun 2022 16:24:33 +0200 Subject: [PATCH 26/78] add docker-compose file for selenium v3 --- infra/docker-compose-selenium-v3.yaml | 114 ++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 infra/docker-compose-selenium-v3.yaml diff --git a/infra/docker-compose-selenium-v3.yaml b/infra/docker-compose-selenium-v3.yaml new file mode 100644 index 0000000..d862077 --- /dev/null +++ b/infra/docker-compose-selenium-v3.yaml @@ -0,0 +1,114 @@ +version: "3.9" + +services: + + db: + image: postgres:14.2-bullseye + container_name: db + restart: always + ports: + - "5432:5432" + volumes: + - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql + environment: + - POSTGRES_DB=healenium + - POSTGRES_USER=healenium_user + - POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + networks: + - healenium + + healenium: + image: healenium/hlm-backend:3.2.2 + container_name: healenium + restart: on-failure + ports: + - "7878:7878" + links: + - db + environment: + - SPRING_CONTAINER_NAME=healenium + - SPRING_POSTGRES_DB=healenium + - SPRING_POSTGRES_USER=healenium_user + - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + - SPRING_POSTGRES_URL=jdbc:postgresql://db:5432/healenium?currentSchema=healenium + networks: + - healenium + + selector-imitator: + image: healenium/hlm-selector-imitator:1.1 + container_name: selector-imitator + restart: on-failure + ports: + - "8000:8000" + networks: + - healenium + + hlm-proxy: + image: healenium/hlm-proxy:0.2.5.2 + container_name: hlm-proxy + restart: on-failure + ports: + - "8085:8085" + environment: + - "recovery-tries=1" + - "score-cap=.6" + - "heal-enabled=true" + # - SELENIUM_PATH=/wd/hub + - SELENIUM_HOST=selenium-hub + # - HEALENIUM_HOST=healenium + # - IMITATE_HOST=selector-imitator + networks: + - healenium + + chrome: + image: selenium/node-chrome:103.0 + container_name: node-chrome + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + edge: + image: selenium/node-edge:102.0 + container_name: node-edge + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + firefox: + image: selenium/node-firefox:101.0 + container_name: node-firefox + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + selenium-hub: + image: selenium/hub:4.2.2 + container_name: selenium-hub + ports: + - "4442:4442" + - "4443:4443" + - "4444:4444" + networks: + - healenium + +networks: + healenium: + name: healenium \ No newline at end of file From a3a65b663625af567b8b18d36fb79c8ba64e981c Mon Sep 17 00:00:00 2001 From: AnatoliZaitsau Date: Wed, 22 Jun 2022 16:28:15 +0200 Subject: [PATCH 27/78] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b4b314f..001ec92 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ Python 3.9.5 + Pytest project with healenium usage example ### 1.Start Healenium backend from 'root' folder ```docker-compose up -d``` +or +```docker compose -f docker-compose-selenium-v3.yaml up -d``` To download this file into your project use this command: From 05b06cd7b4fc0f7ee5193ed5d5eada3cc19e3c44 Mon Sep 17 00:00:00 2001 From: AnatoliZaitsau Date: Wed, 22 Jun 2022 16:32:20 +0200 Subject: [PATCH 28/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 001ec92..7af7d63 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Python 3.9.5 + Pytest project with healenium usage example ```docker-compose up -d``` or -```docker compose -f docker-compose-selenium-v3.yaml up -d``` +```docker-compose -f docker-compose-selenium-v3.yaml up -d``` To download this file into your project use this command: From d72ce07c536aecb76600ed4f07a1dfdc48cb9f16 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Thu, 23 Jun 2022 14:41:34 +0300 Subject: [PATCH 29/78] 0.2.5-test 289 proxy feature --- docker-compose.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3fa9a57..2dbfdcb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,12 +47,12 @@ services: ports: - "8085:8085" environment: - - "recovery-tries=1" - - "score-cap=.6" - - "heal-enabled=true" -# - SELENIUM_HOST=hlm-selenium-webview -# - HEALENIUM_HOST=healenium -# - IMITATE_HOST=selector-imitator + - RECOVERY_TRIES=1 + - SCORE_CAP=.6 + - HEAL_ENABLED=true + - SELENIUM_HOST=hlm-selenium-webview + - HEALENIUM_HOST=healenium + - IMITATE_HOST=selector-imitator networks: - healenium From 4580dce97e2c9d86873f541677acf4adfaffee00 Mon Sep 17 00:00:00 2001 From: Elena Date: Fri, 24 Jun 2022 16:18:26 +0300 Subject: [PATCH 30/78] Changed docker-compose and add browsers json --- browsers.json | 22 ++++++++++++++++++++++ docker-compose.yml | 47 ++++++++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 browsers.json diff --git a/browsers.json b/browsers.json new file mode 100644 index 0000000..99188f9 --- /dev/null +++ b/browsers.json @@ -0,0 +1,22 @@ +{ + "firefox": { + "default": "89.0", + "versions": { + "89.0": { + "image": "selenoid/vnc:firefox_89.0", + "port": "4444", + "path": "/wd/hub" + } + } + }, + "chrome": { + "default": "100.0", + "versions": { + "100.0": { + "image": "selenoid/vnc:chrome_100.0", + "port": "4444", + "path": "/" + } + } + } +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2dbfdcb..bd65af5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,15 @@ version: "3.9" + services: db: - image: postgres:11-alpine + image: postgres:14.2-bullseye + container_name: db + restart: always ports: - "5432:5432" volumes: - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql - restart: always environment: - POSTGRES_DB=healenium - POSTGRES_USER=healenium_user @@ -17,6 +19,8 @@ services: healenium: image: healenium/hlm-backend:3.2.2 + container_name: healenium + restart: on-failure ports: - "7878:7878" links: @@ -40,36 +44,43 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:0.2.5 + image: healenium/hlm-proxy:0.2.5.1 container_name: hlm-proxy - volumes: - - docker-log-volume:/var/log/dockerlogs + restart: on-failure ports: - "8085:8085" environment: - - RECOVERY_TRIES=1 - - SCORE_CAP=.6 - - HEAL_ENABLED=true - - SELENIUM_HOST=hlm-selenium-webview - - HEALENIUM_HOST=healenium - - IMITATE_HOST=selector-imitator + - "recovery-tries=1" + - "score-cap=.6" + - "heal-enabled=true" networks: - healenium - hlm-selenium-webview: - image: healenium/hlm-selenium-4-standalone-tigervnc:firefox98.0.1_chrome99.0.4844.82_edge99.0.1150.46 - restart: on-failure + selenoid: + image: healenium/hlm-selenoid:0.1.0 container_name: hlm-selenium-webview + restart: on-failure ports: - - "8086:6080" - "4444:4444" + environment: + - "PROXY_CONTAINER_NAME=hlm-proxy" volumes: - - docker-log-volume:/var/log/dockerlogs + - .:/etc/selenoid/:ro + - /var/run/docker.sock:/var/run/docker.sock + command: ["-container-network", "healenium"] networks: - healenium -volumes: - docker-log-volume: + selenoid-ui: + image: aerokube/selenoid-ui:1.10.4 + container_name: hlm-selenium-webview-ui + restart: on-failure + ports: + - "8080:8080" + command: ["--selenoid-uri", "http://hlm-selenium-webview:4444/"] + networks: + - healenium networks: healenium: + name: healenium \ No newline at end of file From 60062dcc791a69dbf0930cfa3680717f01e13709 Mon Sep 17 00:00:00 2001 From: Anatoli Zaitsau Date: Tue, 28 Jun 2022 17:02:33 +0200 Subject: [PATCH 31/78] healenium 1.0.0, update readme and docker-compose --- README.md | 70 ++++++++++++++----- infra/docker-compose-selenium-v3.yaml | 32 ++++----- ...docker-compose.yml => docker-compose.yaml} | 39 +++++------ 3 files changed, 86 insertions(+), 55 deletions(-) rename infra/{docker-compose.yml => docker-compose.yaml} (66%) diff --git a/README.md b/README.md index 7af7d63..83e795b 100644 --- a/README.md +++ b/README.md @@ -3,37 +3,66 @@ Python 3.9.5 + Pytest project with healenium usage example ## How to start ### 1.Start Healenium backend from 'root' folder - -```docker-compose up -d``` -or -```docker-compose -f docker-compose-selenium-v3.yaml up -d``` - -To download this file into your project use this command: - -```$ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose.yml -o docker-compose.yml``` - -Create /db/sql folder on the same level in your project. Add init.sql file into ```./db/sql/init.sql``` folder in your project via command: - -```$ curl https://raw.githubusercontent.com/healenium/healenium-client/master/example/init.sql -o init.sql``` - -Verify that images ```healenium/hlm-backend:3.2.2```, ```postgres:14.2-bullseye```, ```healenium/hlm-selector-imitator:1.1```, ```healenium/hlm-proxy``` are up and running +```cd infra```

+To work with Healenium and Selenoid plus Selenoid Ui, use:
+
+    docker-compose up -d
+
+    To download docker-compose.yaml file into your project use this command:
+
+    $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose.yaml -o docker-compose.yaml
+
+To work with Healenium and standard Selenium hub with nodes, use:
+
+    docker-compose -f docker-compose-selenium-v3.yaml up -d
+
+    To download docker-compose.yaml file into your project use this command:
+
+    $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose-selenium-v3.yaml -o docker-compose-selenium-v3.yaml
+
+ +Create /db/sql folder on the same level in your project.
+
+    Add init.sql file into ./db/sql/init.sql folder in your project via command:
+
+    $ curl https://raw.githubusercontent.com/healenium/healenium/master/db/sql/init.sql -o init.sql
+
+Verify the next images are UP and Running +
+    * postgres:14.2-bullseye
+    * healenium/hlm-backend:3.2.2
+    * healenium/hlm-selector-imitator:1.1
+    * healenium/hlm-proxy:1.0.0
+    * healenium/hlm-selenoid:0.1.0
+    * aerokube/selenoid-ui:1.10.5
+
### 2. Project structure ``` |__healenium-example-python (root) + |__infra + |__db + |__sql + |__init.sql + |__docker-compose.yaml + |__docker-compose-selenium-v3.yaml |__src |__main |__constants |__pages |__search |__tests - |__selenium tests - |__docker-compose.yml + |__test_css.py + |__test_general.py + |__test_parent_child.py + |__test_semantic.py + |__test_wait.py + |__test_xpath.py -``` +``` ### 3.Run test -To run tests in terminal with pytest you need to go to execute next comands: +To run tests in terminal with pytest you need to go to execute next commands: ``python3 -m venv env`` @@ -49,4 +78,7 @@ To run tests in terminal with pytest you need to go to execute next comands: >> In case you want to run all tests in project use ```python -m pytest ./tests/``` command ### 4. Monitoring tests running -You can monitor tests running. To do this go to ```http://localhost:8080``` +You can monitor tests running if you using Healenium with Selenoid plus Selenoid Ui, use:
+
+    go to http://localhost:8080
+
\ No newline at end of file diff --git a/infra/docker-compose-selenium-v3.yaml b/infra/docker-compose-selenium-v3.yaml index d862077..42c2da2 100644 --- a/infra/docker-compose-selenium-v3.yaml +++ b/infra/docker-compose-selenium-v3.yaml @@ -2,9 +2,9 @@ version: "3.9" services: - db: + postgres-db: image: postgres:14.2-bullseye - container_name: db + container_name: postgres-db restart: always ports: - "5432:5432" @@ -18,19 +18,19 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.2 + image: healenium/hlm-backend:3.2.3 container_name: healenium restart: on-failure ports: - "7878:7878" links: - - db + - postgres-db environment: - - SPRING_CONTAINER_NAME=healenium - SPRING_POSTGRES_DB=healenium + - SPRING_POSTGRES_SCHEMA=healenium - SPRING_POSTGRES_USER=healenium_user - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - - SPRING_POSTGRES_URL=jdbc:postgresql://db:5432/healenium?currentSchema=healenium + - SPRING_POSTGRES_DB_HOST=postgres-db networks: - healenium @@ -44,19 +44,19 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:0.2.5.2 + image: healenium/hlm-proxy:1.0.0 container_name: hlm-proxy restart: on-failure ports: - "8085:8085" environment: - - "recovery-tries=1" - - "score-cap=.6" - - "heal-enabled=true" - # - SELENIUM_PATH=/wd/hub - - SELENIUM_HOST=selenium-hub - # - HEALENIUM_HOST=healenium - # - IMITATE_HOST=selector-imitator + - RECOVERY_TRIES=1 + - SCORE_CAP=.6 + - HEAL_ENABLED=true + - SELENIUM_HOST=hlm-selenoid + - HEALENIUM_HOST=healenium + - IMITATE_HOST=selector-imitator + - SELENIUM_URL=/wd/hub networks: - healenium @@ -74,7 +74,7 @@ services: - healenium edge: - image: selenium/node-edge:102.0 + image: selenium/node-edge:103.0 container_name: node-edge shm_size: 2gb depends_on: @@ -100,7 +100,7 @@ services: - healenium selenium-hub: - image: selenium/hub:4.2.2 + image: selenium/hub:4.3.0 container_name: selenium-hub ports: - "4442:4442" diff --git a/infra/docker-compose.yml b/infra/docker-compose.yaml similarity index 66% rename from infra/docker-compose.yml rename to infra/docker-compose.yaml index c9110ce..a243ec5 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yaml @@ -1,9 +1,9 @@ version: "3.9" services: - db: + postgres-db: image: postgres:14.2-bullseye - container_name: db + container_name: postgres-db restart: always ports: - "5432:5432" @@ -17,22 +17,21 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.2 + image: healenium/hlm-backend:3.2.3 container_name: healenium restart: on-failure ports: - "7878:7878" links: - - db + - postgres-db environment: - - SPRING_CONTAINER_NAME=healenium - SPRING_POSTGRES_DB=healenium + - SPRING_POSTGRES_SCHEMA=healenium - SPRING_POSTGRES_USER=healenium_user - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - - SPRING_POSTGRES_URL=jdbc:postgresql://db:5432/healenium?currentSchema=healenium + - SPRING_POSTGRES_DB_HOST=postgres-db networks: - healenium - selector-imitator: image: healenium/hlm-selector-imitator:1.1 container_name: selector-imitator @@ -43,30 +42,30 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:0.2.5.2 + image: healenium/hlm-proxy:1.0.0 container_name: hlm-proxy restart: on-failure ports: - "8085:8085" environment: - - "recovery-tries=1" - - "score-cap=.6" - - "heal-enabled=true" - - SELENIUM_PATH=/wd/hub -# - SELENIUM_HOST=hlm-selenium-webview -# - HEALENIUM_HOST=healenium -# - IMITATE_HOST=selector-imitator + - RECOVERY_TRIES=1 + - SCORE_CAP=.6 + - HEAL_ENABLED=true + - SELENIUM_HOST=hlm-selenoid + - HEALENIUM_HOST=healenium + - IMITATE_HOST=selector-imitator + - SELENIUM_URL=/wd/hub networks: - healenium selenoid: image: healenium/hlm-selenoid:0.1.0 - container_name: hlm-selenium-webview + container_name: hlm-selenoid restart: on-failure ports: - "4444:4444" environment: - - "PROXY_CONTAINER_NAME=hlm-proxy" + - PROXY_CONTAINER_NAME=hlm-proxy volumes: - .:/etc/selenoid/:ro - /var/run/docker.sock:/var/run/docker.sock @@ -75,12 +74,12 @@ services: - healenium selenoid-ui: - image: aerokube/selenoid-ui:1.10.4 - container_name: hlm-selenium-webview-ui + image: aerokube/selenoid-ui:1.10.5 + container_name: selenoid-ui restart: on-failure ports: - "8080:8080" - command: ["--selenoid-uri", "http://hlm-selenium-webview:4444/"] + command: [ "--selenoid-uri", "http://hlm-selenoid:4444/" ] networks: - healenium From de2e29baa4939ad05c4d043a27786a6441301dbb Mon Sep 17 00:00:00 2001 From: AnatoliZaitsau Date: Tue, 28 Jun 2022 17:13:40 +0200 Subject: [PATCH 32/78] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 83e795b..789bcf2 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Create /db/sql folder on the same level in your project.
Verify the next images are UP and Running
     * postgres:14.2-bullseye
-    * healenium/hlm-backend:3.2.2
+    * healenium/hlm-backend:3.2.3
     * healenium/hlm-selector-imitator:1.1
     * healenium/hlm-proxy:1.0.0
     * healenium/hlm-selenoid:0.1.0
@@ -81,4 +81,4 @@ To run tests in terminal with pytest you need to go to execute next commands:
 You can monitor tests running if you using Healenium with Selenoid plus Selenoid Ui, use:
     go to http://localhost:8080
-
\ No newline at end of file +
From 015207256c7a9d78c9de58a08f739fbcad4d691d Mon Sep 17 00:00:00 2001 From: AnatoliZaitsau Date: Tue, 28 Jun 2022 17:18:23 +0200 Subject: [PATCH 33/78] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 789bcf2..324fa71 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,10 @@ Verify the next images are UP and Running |__docker-compose.yaml |__docker-compose-selenium-v3.yaml |__src - |__main - |__constants - |__pages - |__search + |__main + |__constants + |__pages + |__search |__tests |__test_css.py |__test_general.py From f8a9948b7b0a040b00c538b915094c20e2f09b31 Mon Sep 17 00:00:00 2001 From: AnatoliZaitsau Date: Wed, 29 Jun 2022 10:19:17 +0200 Subject: [PATCH 34/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 324fa71..7997d87 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ To work with Healenium and standard Selenium hub with nodes, use:
     docker-compose -f docker-compose-selenium-v3.yaml up -d
 
-    To download docker-compose.yaml file into your project use this command:
+    To download docker-compose-selenium-v3.yaml file into your project use this command:
 
     $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose-selenium-v3.yaml -o docker-compose-selenium-v3.yaml
 
From 23fe1c862c37dfd50ab2ec025817874255cf8c38 Mon Sep 17 00:00:00 2001 From: AnatoliZaitsau Date: Wed, 29 Jun 2022 10:34:37 +0200 Subject: [PATCH 35/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7997d87..75c26d7 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Create /db/sql folder on the same level in your project.
$ curl https://raw.githubusercontent.com/healenium/healenium/master/db/sql/init.sql -o init.sql -Verify the next images are UP and Running +Verify the next images are Up and Running
     * postgres:14.2-bullseye
     * healenium/hlm-backend:3.2.3

From 22da01182e4371f5416fe36811e087c337d75ce5 Mon Sep 17 00:00:00 2001
From: AnatoliZaitsau 
Date: Mon, 4 Jul 2022 12:31:38 +0200
Subject: [PATCH 36/78] Create browsers.json

---
 infra/browsers.json | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 infra/browsers.json

diff --git a/infra/browsers.json b/infra/browsers.json
new file mode 100644
index 0000000..05fc533
--- /dev/null
+++ b/infra/browsers.json
@@ -0,0 +1,30 @@
+{
+  "chrome": {
+    "default": "102.0",
+    "versions": {
+      "102.0": {
+        "image": "selenoid/vnc:chrome_102.0",
+        "port": "4444"
+      },
+      "101.0": {
+        "image": "selenoid/vnc:chrome_101.0",
+        "port": "4444"
+      }
+    }
+  },
+  "firefox": {
+    "default": "101.0",
+    "versions": {
+      "101.0": {
+        "image": "selenoid/vnc:firefox_101.0",
+        "port": "4444",
+        "path":"/wd/hub"
+      },
+      "100.0": {
+        "image": "selenoid/vnc:firefox_100.0",
+        "port": "4444",
+        "path": "/wd/hub"
+      }
+    }
+  }
+}

From c73a9fbfa077ecee7dfc5cd6e504cd5b5e0402ed Mon Sep 17 00:00:00 2001
From: AnatoliZaitsau 
Date: Mon, 4 Jul 2022 12:39:00 +0200
Subject: [PATCH 37/78] Update README.md

---
 README.md | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/README.md b/README.md
index 75c26d7..1eb715e 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,17 @@ To work with Healenium and Selenoid plus Selenoid Ui, use:
To download docker-compose.yaml file into your project use this command: $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose.yaml -o docker-compose.yaml + + Additionally downlod browsers.json file into you project use this command: + + curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/browsers.json -o browsers.json + + Manually pull docker images with specific versions from browsers.json: + + docker pull selenoid/vnc:chrome_102.0 + docker pull selenoid/vnc:chrome_101.0 + docker pull selenoid/vnc:firefox_101.0 + docker pull selenoid/vnc:chrome_100.0
To work with Healenium and standard Selenium hub with nodes, use:

From f9d1b3415b09f78864691ea76d610cc07253dc91 Mon Sep 17 00:00:00 2001
From: AnatoliZaitsau 
Date: Mon, 4 Jul 2022 12:39:32 +0200
Subject: [PATCH 38/78] Update README.md

---
 README.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 1eb715e..0e61f11 100644
--- a/README.md
+++ b/README.md
@@ -18,10 +18,10 @@ To work with Healenium and Selenoid plus Selenoid Ui, use:
Manually pull docker images with specific versions from browsers.json: - docker pull selenoid/vnc:chrome_102.0 - docker pull selenoid/vnc:chrome_101.0 - docker pull selenoid/vnc:firefox_101.0 - docker pull selenoid/vnc:chrome_100.0 + docker pull selenoid/vnc:chrome_102.0 + docker pull selenoid/vnc:chrome_101.0 + docker pull selenoid/vnc:firefox_101.0 + docker pull selenoid/vnc:chrome_100.0
To work with Healenium and standard Selenium hub with nodes, use:

From 1c676bb26055675c07b433cbbdb8f4503c1581a4 Mon Sep 17 00:00:00 2001
From: AnatoliZaitsau 
Date: Mon, 4 Jul 2022 12:39:48 +0200
Subject: [PATCH 39/78] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 0e61f11..706b4e4 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ To work with Healenium and Selenoid plus Selenoid Ui, use:
docker pull selenoid/vnc:chrome_102.0 docker pull selenoid/vnc:chrome_101.0 docker pull selenoid/vnc:firefox_101.0 - docker pull selenoid/vnc:chrome_100.0 + docker pull selenoid/vnc:firefox_100.0
To work with Healenium and standard Selenium hub with nodes, use:

From 7190bca700750372bb6ce35effe39108ca621134 Mon Sep 17 00:00:00 2001
From: Anatoli Zaitsau 
Date: Mon, 4 Jul 2022 16:03:25 +0200
Subject: [PATCH 40/78] update readme file

---
 README.md | 113 +++++++++++++++++++++++++++++++++---------------------
 1 file changed, 69 insertions(+), 44 deletions(-)

diff --git a/README.md b/README.md
index 706b4e4..05652ad 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,29 @@
-# healenium
+# Python Example with Healenium
+[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
Python 3.9.5 + Pytest project with healenium usage example ## How to start ### 1.Start Healenium backend from 'root' folder ```cd infra```

-To work with Healenium and Selenoid plus Selenoid Ui, use:
+ +Create /db/sql folder on the same level in your project.
-    docker-compose up -d
+    Add init.sql file into ./db/sql/init.sql folder in your project via command:
 
+    $ curl https://raw.githubusercontent.com/healenium/healenium/master/db/sql/init.sql -o init.sql
+
+    Example project structure:
+
+        your_project_name (root)
+            |__infra
+                |__db
+                    |__sql
+                        |__init.sql
+
+ +To work with Healenium and Selenoid plus Selenoid Ui, use:
+ +
     To download docker-compose.yaml file into your project use this command:
 
     $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose.yaml -o docker-compose.yaml
@@ -22,56 +38,65 @@ To work with Healenium and Selenoid plus Selenoid Ui, use:
docker pull selenoid/vnc:chrome_101.0 docker pull selenoid/vnc:firefox_101.0 docker pull selenoid/vnc:firefox_100.0 + + Example project structure: + + your_project_name (root) + |__infra + |__db + |__sql + |__init.sql + |__browsers.json + |__docker-compose.yaml + + Command to run docker-compose.yaml + + docker-compose up -d + + ATTENTION + Verify the next images are Up and Running + * postgres:14.2-bullseye + * healenium/hlm-backend:3.2.3 + * healenium/hlm-selector-imitator:1.1 + * healenium/hlm-proxy:1.0.0 + * healenium/hlm-selenoid:0.1.0 + * aerokube/selenoid-ui:1.10.5
+ To work with Healenium and standard Selenium hub with nodes, use:
-
-    docker-compose -f docker-compose-selenium-v3.yaml up -d
 
+
     To download docker-compose-selenium-v3.yaml file into your project use this command:
 
     $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose-selenium-v3.yaml -o docker-compose-selenium-v3.yaml
-
- -Create /db/sql folder on the same level in your project.
-
-    Add init.sql file into ./db/sql/init.sql folder in your project via command:
 
-    $ curl https://raw.githubusercontent.com/healenium/healenium/master/db/sql/init.sql -o init.sql
-
-Verify the next images are Up and Running -
-    * postgres:14.2-bullseye
-    * healenium/hlm-backend:3.2.3
-    * healenium/hlm-selector-imitator:1.1
-    * healenium/hlm-proxy:1.0.0
-    * healenium/hlm-selenoid:0.1.0
-    * aerokube/selenoid-ui:1.10.5
+    Example project structure:
+
+        your_project_name (root)
+            |__infra
+                |__db
+                    |__sql
+                        |__init.sql
+                |__docker-compose-selenium-v3.yaml
+
+    Command to run docker-compose-selenium-v3.yaml
+
+        docker-compose -f docker-compose-selenium-v3.yaml up -d
+
+    ATTENTION
+    Verify the next images are Up and Running
+        * postgres:14.2-bullseye
+        * healenium/hlm-backend:3.2.3
+        * healenium/hlm-selector-imitator:1.1
+        * healenium/hlm-proxy:1.0.0
+        * selenium/hub:4.3.0
+        * selenium/node-chrome:103.0
+        * selenium/node-edge:103.0
+        * selenium/node-firefox:101.0
 
-### 2. Project structure -``` -|__healenium-example-python (root) - |__infra - |__db - |__sql - |__init.sql - |__docker-compose.yaml - |__docker-compose-selenium-v3.yaml - |__src - |__main - |__constants - |__pages - |__search - |__tests - |__test_css.py - |__test_general.py - |__test_parent_child.py - |__test_semantic.py - |__test_wait.py - |__test_xpath.py - -``` - +### 2.Create RemoteWebDriver for Healenium-Proxy + ### 3.Run test To run tests in terminal with pytest you need to go to execute next commands: From 0908f7c4f4a860dd7da41bad4de24c8ba66621f1 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 20 Jul 2022 14:20:11 +0300 Subject: [PATCH 41/78] Rename docker-compose-selenium-grid.yaml and add gitignore --- .gitignore | 160 ++++++++++++++++++ .idea/.gitignore | 3 - .idea/healenium-example-python.iml | 11 -- .../inspectionProfiles/profiles_settings.xml | 6 - .idea/misc.xml | 4 - .idea/modules.xml | 8 - .idea/vcs.xml | 6 - README.md | 10 +- ...yaml => docker-compose-selenium-grid.yaml} | 0 9 files changed, 165 insertions(+), 43 deletions(-) create mode 100644 .gitignore delete mode 100644 .idea/.gitignore delete mode 100644 .idea/healenium-example-python.iml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml rename infra/{docker-compose-selenium-v3.yaml => docker-compose-selenium-grid.yaml} (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0b6f3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/healenium-example-python.iml b/.idea/healenium-example-python.iml deleted file mode 100644 index 5b05384..0000000 --- a/.idea/healenium-example-python.iml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index dc9ea49..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index e30bcea..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 05652ad..61db5ff 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,9 @@ To work with Healenium and Selenoid plus Selenoid Ui, use:
To work with Healenium and standard Selenium hub with nodes, use:
-    To download docker-compose-selenium-v3.yaml file into your project use this command:
+    To download docker-compose-selenium-grid.yaml file into your project use this command:
 
-    $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose-selenium-v3.yaml -o docker-compose-selenium-v3.yaml
+    $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose-selenium-grid.yaml -o docker-compose-selenium-grid.yaml
 
     Example project structure:
 
@@ -77,11 +77,11 @@ To work with Healenium and standard Selenium hub with nodes, use:
|__db |__sql |__init.sql - |__docker-compose-selenium-v3.yaml + |__docker-compose-selenium-grid.yaml - Command to run docker-compose-selenium-v3.yaml + Command to run docker-compose-selenium-grid.yaml - docker-compose -f docker-compose-selenium-v3.yaml up -d + docker-compose -f docker-compose-selenium-grid.yaml up -d ATTENTION Verify the next images are Up and Running diff --git a/infra/docker-compose-selenium-v3.yaml b/infra/docker-compose-selenium-grid.yaml similarity index 100% rename from infra/docker-compose-selenium-v3.yaml rename to infra/docker-compose-selenium-grid.yaml From 350b5f4d28ec05aa6a86439bcd6282f78b7b532e Mon Sep 17 00:00:00 2001 From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com> Date: Wed, 20 Jul 2022 14:21:33 +0300 Subject: [PATCH 42/78] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 4f4bf70f867ac47bbfec4524718ba72b020cee53 Mon Sep 17 00:00:00 2001 From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com> Date: Wed, 27 Jul 2022 15:28:56 +0300 Subject: [PATCH 43/78] Update docker-compose-selenium-grid.yaml --- infra/docker-compose-selenium-grid.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/docker-compose-selenium-grid.yaml b/infra/docker-compose-selenium-grid.yaml index 42c2da2..4201a43 100644 --- a/infra/docker-compose-selenium-grid.yaml +++ b/infra/docker-compose-selenium-grid.yaml @@ -3,7 +3,7 @@ version: "3.9" services: postgres-db: - image: postgres:14.2-bullseye + image: postgres:11-alpine container_name: postgres-db restart: always ports: @@ -111,4 +111,4 @@ services: networks: healenium: - name: healenium \ No newline at end of file + name: healenium From 656b4c3503bbb32b92f9fca96fd35e1fa608c6bf Mon Sep 17 00:00:00 2001 From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com> Date: Wed, 27 Jul 2022 15:29:20 +0300 Subject: [PATCH 44/78] Update docker-compose.yaml --- infra/docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/docker-compose.yaml b/infra/docker-compose.yaml index a243ec5..a869514 100644 --- a/infra/docker-compose.yaml +++ b/infra/docker-compose.yaml @@ -2,7 +2,7 @@ version: "3.9" services: postgres-db: - image: postgres:14.2-bullseye + image: postgres:11-alpine container_name: postgres-db restart: always ports: @@ -85,4 +85,4 @@ services: networks: healenium: - name: healenium \ No newline at end of file + name: healenium From 455e3ed296a3d361107ffd79a27a9b32b07cc298 Mon Sep 17 00:00:00 2001 From: Elena Date: Wed, 3 Aug 2022 16:39:36 +0300 Subject: [PATCH 45/78] Docker-compose main --- .idea/vcs.xml | 1 + browsers.json | 22 ------------ db/sql/init.sql | 2 -- docker-compose.yml | 86 ---------------------------------------------- healenium | 1 + 5 files changed, 2 insertions(+), 110 deletions(-) delete mode 100644 browsers.json delete mode 100644 db/sql/init.sql delete mode 100644 docker-compose.yml create mode 160000 healenium diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..37b9020 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/browsers.json b/browsers.json deleted file mode 100644 index 99188f9..0000000 --- a/browsers.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "firefox": { - "default": "89.0", - "versions": { - "89.0": { - "image": "selenoid/vnc:firefox_89.0", - "port": "4444", - "path": "/wd/hub" - } - } - }, - "chrome": { - "default": "100.0", - "versions": { - "100.0": { - "image": "selenoid/vnc:chrome_100.0", - "port": "4444", - "path": "/" - } - } - } -} \ No newline at end of file diff --git a/db/sql/init.sql b/db/sql/init.sql deleted file mode 100644 index 2b5b529..0000000 --- a/db/sql/init.sql +++ /dev/null @@ -1,2 +0,0 @@ -CREATE SCHEMA healenium AUTHORIZATION healenium_user; -GRANT USAGE ON SCHEMA healenium TO healenium_user; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index bd65af5..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,86 +0,0 @@ -version: "3.9" - -services: - - db: - image: postgres:14.2-bullseye - container_name: db - restart: always - ports: - - "5432:5432" - volumes: - - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql - environment: - - POSTGRES_DB=healenium - - POSTGRES_USER=healenium_user - - POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - networks: - - healenium - - healenium: - image: healenium/hlm-backend:3.2.2 - container_name: healenium - restart: on-failure - ports: - - "7878:7878" - links: - - db - environment: - - SPRING_CONTAINER_NAME=healenium - - SPRING_POSTGRES_DB=healenium - - SPRING_POSTGRES_USER=healenium_user - - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - - SPRING_POSTGRES_URL=jdbc:postgresql://db:5432/healenium?currentSchema=healenium - networks: - - healenium - - selector-imitator: - image: healenium/hlm-selector-imitator:1.1 - container_name: selector-imitator - restart: on-failure - ports: - - "8000:8000" - networks: - - healenium - - hlm-proxy: - image: healenium/hlm-proxy:0.2.5.1 - container_name: hlm-proxy - restart: on-failure - ports: - - "8085:8085" - environment: - - "recovery-tries=1" - - "score-cap=.6" - - "heal-enabled=true" - networks: - - healenium - - selenoid: - image: healenium/hlm-selenoid:0.1.0 - container_name: hlm-selenium-webview - restart: on-failure - ports: - - "4444:4444" - environment: - - "PROXY_CONTAINER_NAME=hlm-proxy" - volumes: - - .:/etc/selenoid/:ro - - /var/run/docker.sock:/var/run/docker.sock - command: ["-container-network", "healenium"] - networks: - - healenium - - selenoid-ui: - image: aerokube/selenoid-ui:1.10.4 - container_name: hlm-selenium-webview-ui - restart: on-failure - ports: - - "8080:8080" - command: ["--selenoid-uri", "http://hlm-selenium-webview:4444/"] - networks: - - healenium - -networks: - healenium: - name: healenium \ No newline at end of file diff --git a/healenium b/healenium new file mode 160000 index 0000000..69fee95 --- /dev/null +++ b/healenium @@ -0,0 +1 @@ +Subproject commit 69fee957c2d56c7187a6d9f221d9b00761eb7953 From 5559f0d207f56c35dc819bb72cae1a29c86212d8 Mon Sep 17 00:00:00 2001 From: Elena Date: Wed, 3 Aug 2022 16:50:35 +0300 Subject: [PATCH 46/78] Remove --- healenium | 1 - 1 file changed, 1 deletion(-) delete mode 160000 healenium diff --git a/healenium b/healenium deleted file mode 160000 index 69fee95..0000000 --- a/healenium +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 69fee957c2d56c7187a6d9f221d9b00761eb7953 From 3b53e227b6b06aecd34b733a5aa80a9e68cf72bb Mon Sep 17 00:00:00 2001 From: Elena Date: Wed, 3 Aug 2022 17:11:37 +0300 Subject: [PATCH 47/78] Add healenium main --- healenium/README.md | 128 ++++++++++++++++++++ healenium/browsers.json | 30 +++++ healenium/db/sql/init.sql | 2 + healenium/docker-compose-selenium-grid.yaml | 114 +++++++++++++++++ healenium/docker-compose.yaml | 90 ++++++++++++++ 5 files changed, 364 insertions(+) create mode 100644 healenium/README.md create mode 100644 healenium/browsers.json create mode 100644 healenium/db/sql/init.sql create mode 100644 healenium/docker-compose-selenium-grid.yaml create mode 100644 healenium/docker-compose.yaml diff --git a/healenium/README.md b/healenium/README.md new file mode 100644 index 0000000..e6f8235 --- /dev/null +++ b/healenium/README.md @@ -0,0 +1,128 @@ +# Healenium + +[![Docker Pulls](https://img.shields.io/docker/pulls/healenium/hlm-backend.svg?maxAge=25920)](https://hub.docker.com/u/healenium) +[![License](https://img.shields.io/badge/license-Apache-brightgreen.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +### Table of Contents + +[Overall information](#overall-information) + +[Healenium installation](#healenium-installation) +* [Healenium with Selenoid](#run-healenium-with-selenoid) +* [Healenium with Selenium-Grid](#run-healenium-with-selenium-grid) + +[Language Examples](#language-examples) +* [Java](#java) +* [Python](#python) +* [C#](#c#) +* [JavaScript](#javascript) + +### Overall information +Self-healing framework based on Selenium and able to use all Selenium supported languages like Java/Python/JS/C# +Healenium acts as proxy between client and selenium server. + +`Docker-compose` includes the following services: +- `postgres-db` (PostgreSQL database to store etalon selector / healing / report) +- `hlm-proxy` (Proxy client request to Selenium server) +- `hlm-bacand` (CRUD service) +- `selector imitator` (Convert healed locator to convenient format) +- `selenoid`/`selenium-grid` (Selenium server) + +### Healenium installation + +Clone Healenium repository: +```sh +git clone https://github.com/healenium/healenium.git +``` + +#### Run Healenium with Selenoid + +> Note: `browsers.json` consists of target browsers and appropriate versions. +> Before run healenium you have to manually pull selenoid browser docker images with version specified in browsers.json + +Example pull selenoid chrome image: +```sh +docker pull selenoid/vnc:chrome_102.0 +``` +Full list of browser images you can find [here](https://hub.docker.com/u/selenoid) + +Run healenium with Selenoid: +```sh +docker-compose up -d +``` + +#### Run Healenium with Selenium-Grid: +```sh +docker-compose -f docker-compose-selenium-grid.yaml up -d +``` + +### Language examples + +``` + /** + * "http://127.0.0.1:8085/" OR "http://localhost:8085/" if you are using locally running proxy server + * + * if you want to use a remote proxy server, + * specify the ip address of this server - "http://remote_ip_address:8085/" + */ +``` + +###### Java: +```java + String nodeURL = "http://localhost:8085/"; + + ChromeOptions options = new ChromeOptions(); + options.addArguments("--no-sandbox"); + options.addArguments("--disable-dev-shm-usage"); + + WebDriver driver = new RemoteWebDriver(new URL(nodeURL), options); +``` + +###### Python +```py + nodeURL = "http://localhost:8085/" + + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + + current_webdriver = webdriver.Remote( + command_executor=nodeURL, + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options, + ) +``` + +###### C# +```csharp + String nodeURL = "http://localhost:8085/"; + + ChromeOptions optionsChrome = new ChromeOptions(); + optionsChrome.AddArguments("--no-sandbox"); + + RemoteWebDriver driverChrome = new RemoteWebDriver(new Uri(nodeURL), optionsChrome); +``` + +###### JavaScript +```javascript + const NODE_URL = "http://localhost:8085/"; + + let args = [ + "--no-sandbox" + ]; + + let chromeCapabilities = selenium.Capabilities.chrome() + .set('chromeOptions', { args }); + + let builder = new selenium.Builder() + .forBrowser('chrome') + .withCapabilities(chromeCapabilities); + + let driver = await builder.usingServer(NODE_URL).build(); +``` + + +## Community / Support + +* [Telegram chat](https://t.me/healenium) +* [GitHub Issues](https://github.com/healenium/healenium/issues) +* [YouTube Channel](https://www.youtube.com/channel/UCsZJ0ri-Hp7IA1A6Fgi4Hvg) diff --git a/healenium/browsers.json b/healenium/browsers.json new file mode 100644 index 0000000..2029555 --- /dev/null +++ b/healenium/browsers.json @@ -0,0 +1,30 @@ +{ + "chrome": { + "default": "102.0", + "versions": { + "102.0": { + "image": "selenoid/vnc:chrome_102.0", + "port": "4444" + }, + "101.0": { + "image": "selenoid/vnc:chrome_101.0", + "port": "4444" + } + } + }, + "firefox": { + "default": "101.0", + "versions": { + "101.0": { + "image": "selenoid/vnc:firefox_101.0", + "port": "4444", + "path": "/wd/hub" + }, + "100.0": { + "image": "selenoid/vnc:firefox_100.0", + "port": "4444", + "path": "/wd/hub" + } + } + } +} diff --git a/healenium/db/sql/init.sql b/healenium/db/sql/init.sql new file mode 100644 index 0000000..2b5b529 --- /dev/null +++ b/healenium/db/sql/init.sql @@ -0,0 +1,2 @@ +CREATE SCHEMA healenium AUTHORIZATION healenium_user; +GRANT USAGE ON SCHEMA healenium TO healenium_user; \ No newline at end of file diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenium-grid.yaml new file mode 100644 index 0000000..c95fc57 --- /dev/null +++ b/healenium/docker-compose-selenium-grid.yaml @@ -0,0 +1,114 @@ +version: "3.9" + +services: + + postgres-db: + image: postgres:11-alpine + container_name: postgres-db + restart: always + ports: + - "5432:5432" + volumes: + - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql + environment: + - POSTGRES_DB=healenium + - POSTGRES_USER=healenium_user + - POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + networks: + - healenium + + healenium: + image: healenium/hlm-backend:3.2.3 + container_name: healenium + restart: on-failure + ports: + - "7878:7878" + links: + - postgres-db + environment: + - SPRING_POSTGRES_DB=healenium + - SPRING_POSTGRES_SCHEMA=healenium + - SPRING_POSTGRES_USER=healenium_user + - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + - SPRING_POSTGRES_DB_HOST=postgres-db + networks: + - healenium + + selector-imitator: + image: healenium/hlm-selector-imitator:1.1 + container_name: selector-imitator + restart: on-failure + ports: + - "8000:8000" + networks: + - healenium + + hlm-proxy: + image: healenium/hlm-proxy:1.0.0 + container_name: hlm-proxy + restart: on-failure + ports: + - "8085:8085" + environment: + - RECOVERY_TRIES=1 + - SCORE_CAP=.6 + - HEAL_ENABLED=true + - SELENIUM_HOST=selenium-hub + - HEALENIUM_HOST=healenium + - IMITATE_HOST=selector-imitator + - SELENIUM_URL=/ + networks: + - healenium + + chrome: + image: selenium/node-chrome:103.0 + container_name: node-chrome + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + edge: + image: selenium/node-edge:103.0 + container_name: node-edge + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + firefox: + image: selenium/node-firefox:101.0 + container_name: node-firefox + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + selenium-hub: + image: selenium/hub:4.3.0 + container_name: selenium-hub + ports: + - "4442:4442" + - "4443:4443" + - "4444:4444" + networks: + - healenium + +networks: + healenium: + name: healenium diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml new file mode 100644 index 0000000..9f3d129 --- /dev/null +++ b/healenium/docker-compose.yaml @@ -0,0 +1,90 @@ +version: "3.9" + +services: + + postgres-db: + image: postgres:11-alpine + container_name: postgres-db + restart: always + ports: + - "5432:5432" + volumes: + - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql + environment: + - POSTGRES_DB=healenium + - POSTGRES_USER=healenium_user + - POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + networks: + - healenium + + healenium: + image: healenium/hlm-backend:3.2.3 + container_name: healenium + restart: on-failure + ports: + - "7878:7878" + links: + - postgres-db + environment: + - SPRING_POSTGRES_DB=healenium + - SPRING_POSTGRES_SCHEMA=healenium + - SPRING_POSTGRES_USER=healenium_user + - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + - SPRING_POSTGRES_DB_HOST=postgres-db + networks: + - healenium + + selector-imitator: + image: healenium/hlm-selector-imitator:1.1 + container_name: selector-imitator + restart: on-failure + ports: + - "8000:8000" + networks: + - healenium + + hlm-proxy: + image: healenium/hlm-proxy:1.0.0 + container_name: hlm-proxy + restart: on-failure + ports: + - "8085:8085" + environment: + - RECOVERY_TRIES=1 + - SCORE_CAP=.6 + - HEAL_ENABLED=true + - SELENIUM_HOST=hlm-selenoid + - HEALENIUM_HOST=healenium + - IMITATE_HOST=selector-imitator + - SELENIUM_URL=/wd/hub + networks: + - healenium + + selenoid: + image: healenium/hlm-selenoid:0.1.0 + container_name: hlm-selenoid + restart: on-failure + ports: + - "4444:4444" + environment: + - PROXY_CONTAINER_NAME=hlm-proxy + volumes: + - .:/etc/selenoid/:ro + - /var/run/docker.sock:/var/run/docker.sock + command: ["-container-network", "healenium"] + networks: + - healenium + + selenoid-ui: + image: aerokube/selenoid-ui:1.10.5 + container_name: selenoid-ui + restart: on-failure + ports: + - "8080:8080" + command: [ "--selenoid-uri", "http://hlm-selenoid:4444/" ] + networks: + - healenium + +networks: + healenium: + name: healenium From 13cadcc9514221fc9f53de78582970923ad3c634 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 3 Aug 2022 18:28:19 +0300 Subject: [PATCH 48/78] Update readme --- README.md | 119 +++++++++++++++++------------------------------------- 1 file changed, 38 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 61db5ff..d198739 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,60 @@ # Python Example with Healenium -[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
-Python 3.9.5 + Pytest project with healenium usage example - -## How to start -### 1.Start Healenium backend from 'root' folder -```cd infra```

- -Create /db/sql folder on the same level in your project.
-
-    Add init.sql file into ./db/sql/init.sql folder in your project via command:
-
-    $ curl https://raw.githubusercontent.com/healenium/healenium/master/db/sql/init.sql -o init.sql
-
-    Example project structure:
-
-        your_project_name (root)
-            |__infra
-                |__db
-                    |__sql
-                        |__init.sql
-
- -To work with Healenium and Selenoid plus Selenoid Ui, use:
- -
-    To download docker-compose.yaml file into your project use this command:
 
-    $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose.yaml -o docker-compose.yaml
+[![Docker Pulls](https://img.shields.io/docker/pulls/healenium/hlm-backend.svg?maxAge=25920)](https://hub.docker.com/u/healenium)
+[![License](https://img.shields.io/badge/license-Apache-brightgreen.svg)](https://www.apache.org/licenses/LICENSE-2.0)
 
-    Additionally downlod browsers.json file into you project use this command:
-
-    curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/browsers.json -o browsers.json
+Python 3.9.5 + Pytest project with healenium usage example 
 
-    Manually pull docker images with specific versions from browsers.json:
+[1. Start Healenium components](#1-start-healenium-components)
+* [Healenium with Selenoid](#run-healenium-with-selenoid)
+* [Healenium with Selenium-Grid](#run-healenium-with-selenium-grid)
 
-    docker pull selenoid/vnc:chrome_102.0
-    docker pull selenoid/vnc:chrome_101.0
-    docker pull selenoid/vnc:firefox_101.0
-    docker pull selenoid/vnc:firefox_100.0
+[2. Run test](#2-run-test)
 
-    Example project structure:
+[3. Monitoring tests running](#3-monitoring-tests-running)
 
-        your_project_name (root)
-            |__infra
-                |__db
-                    |__sql
-                        |__init.sql
-                |__browsers.json
-                |__docker-compose.yaml
+## How to start
 
-    Command to run docker-compose.yaml
+### 1. Start Healenium components
 
-        docker-compose up -d
+Go into healenium folder
 
-    ATTENTION
-    Verify the next images are Up and Running
-        * postgres:14.2-bullseye
-        * healenium/hlm-backend:3.2.3
-        * healenium/hlm-selector-imitator:1.1
-        * healenium/hlm-proxy:1.0.0
-        * healenium/hlm-selenoid:0.1.0
-        * aerokube/selenoid-ui:1.10.5
-
+```cd healenium``` -To work with Healenium and standard Selenium hub with nodes, use:
+#### Run Healenium with Selenoid: -
-    To download docker-compose-selenium-grid.yaml file into your project use this command:
+> Note: `browsers.json` consists of target browsers and appropriate versions.
+> Before run healenium you have to manually pull selenoid browser docker images with version specified in browsers.json
 
-    $ curl https://raw.githubusercontent.com/healenium/healenium-example-python/master/infra/docker-compose-selenium-grid.yaml -o docker-compose-selenium-grid.yaml
+Example pull selenoid chrome image:
+```sh
+docker pull selenoid/vnc:chrome_102.0
+```
+Full list of browser images you can find [here](https://hub.docker.com/u/selenoid)
 
-    Example project structure:
+Run healenium with Selenoid:
+```sh
+docker-compose up -d
+```
 
-        your_project_name (root)
-            |__infra
-                |__db
-                    |__sql
-                        |__init.sql
-                |__docker-compose-selenium-grid.yaml
+#### Run Healenium with Selenium-Grid:
+```sh
+docker-compose -f docker-compose-selenium-grid.yaml up -d
+```
 
-    Command to run docker-compose-selenium-grid.yaml
 
-        docker-compose -f docker-compose-selenium-grid.yaml up -d
+To work with Healenium and Selenoid plus Selenoid Ui, use:
- ATTENTION - Verify the next images are Up and Running - * postgres:14.2-bullseye - * healenium/hlm-backend:3.2.3 - * healenium/hlm-selector-imitator:1.1 - * healenium/hlm-proxy:1.0.0 - * selenium/hub:4.3.0 - * selenium/node-chrome:103.0 - * selenium/node-edge:103.0 - * selenium/node-firefox:101.0 -
+ATTENTION -### 2.Create RemoteWebDriver for Healenium-Proxy +Verify the next images are Up and Running +- `postgres-db` (PostgreSQL database to store etalon selector / healing / report) +- `hlm-proxy` (Proxy client request to Selenium server) +- `hlm-bacand` (CRUD service) +- `selector imitator` (Convert healed locator to convenient format) +- `selenoid`/`selenium-grid` (Selenium server) -### 3.Run test +### 2. Run test To run tests in terminal with pytest you need to go to execute next commands: ``python3 -m venv env`` @@ -113,7 +70,7 @@ To run tests in terminal with pytest you need to go to execute next commands: > If you want to execute tests from specified file, please use the command: ```python -m pytest ./tests/test_css.py``` >> In case you want to run all tests in project use ```python -m pytest ./tests/``` command -### 4. Monitoring tests running +### 3. Monitoring tests running You can monitor tests running if you using Healenium with Selenoid plus Selenoid Ui, use:
     go to http://localhost:8080

From 664ef770f598f533b1d9a3c99d99e07763e03407 Mon Sep 17 00:00:00 2001
From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com>
Date: Wed, 3 Aug 2022 18:29:51 +0300
Subject: [PATCH 49/78] Update README.md

---
 README.md | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/README.md b/README.md
index d198739..7ecbab8 100644
--- a/README.md
+++ b/README.md
@@ -42,9 +42,6 @@ docker-compose up -d
 docker-compose -f docker-compose-selenium-grid.yaml up -d
 ```
 
-
-To work with Healenium and Selenoid plus Selenoid Ui, use:
- ATTENTION Verify the next images are Up and Running From 98f6148f5dbe393852c2b0e669d1d3b4ea2dfd5b Mon Sep 17 00:00:00 2001 From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:31:03 +0300 Subject: [PATCH 50/78] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ecbab8..1356f41 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,5 @@ To run tests in terminal with pytest you need to go to execute next commands: >> In case you want to run all tests in project use ```python -m pytest ./tests/``` command ### 3. Monitoring tests running -You can monitor tests running if you using Healenium with Selenoid plus Selenoid Ui, use:
-
-    go to http://localhost:8080
-
+You can monitor tests running if you using Healenium with Selenoid plus Selenoid Ui, go to:
+```http://localhost:8080``` From 8603d0561065de4148e925c7d15c182e430bbbc8 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 3 Aug 2022 18:33:32 +0300 Subject: [PATCH 51/78] Update readme --- README.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1356f41..acd4dce 100644 --- a/README.md +++ b/README.md @@ -54,19 +54,31 @@ Verify the next images are Up and Running ### 2. Run test To run tests in terminal with pytest you need to go to execute next commands: -``python3 -m venv env`` +```sh +python3 -m venv env +``` -``source ./env/bin/activate`` +```sh +source ./env/bin/activate +``` -``python -m pip install -U pytest`` +```sh +python -m pip install -U pytest +``` -``python -m pip install -U selenium`` +```sh +python -m pip install -U selenium +``` -``python -m pytest ./tests/`` +```sh +python -m pytest ./tests/ +``` > If you want to execute tests from specified file, please use the command: ```python -m pytest ./tests/test_css.py``` >> In case you want to run all tests in project use ```python -m pytest ./tests/``` command ### 3. Monitoring tests running You can monitor tests running if you using Healenium with Selenoid plus Selenoid Ui, go to:
-```http://localhost:8080``` +```sh +http://localhost:8080 +``` From b167a0ddf7862fe96880d7f9b0c70d185859e065 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 3 Aug 2022 18:36:08 +0300 Subject: [PATCH 52/78] Update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index acd4dce..63d4582 100644 --- a/README.md +++ b/README.md @@ -82,3 +82,9 @@ You can monitor tests running if you using Healenium with Selenoid plus Selenoid ```sh http://localhost:8080 ``` + +## Community / Support + +* [Telegram chat](https://t.me/healenium) +* [YouTube Channel](https://www.youtube.com/channel/UCsZJ0ri-Hp7IA1A6Fgi4Hvg) + From d8d6739e7e0d4220e69cbd3a2ff363fd3ddbdd45 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 3 Aug 2022 18:37:29 +0300 Subject: [PATCH 53/78] Update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 63d4582..2185156 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,9 @@ Python 3.9.5 + Pytest project with healenium usage example Go into healenium folder -```cd healenium``` +```sh +cd healenium +``` #### Run Healenium with Selenoid: From fb2547094cac5c4ae7a8d1ecca0c56e8c56db904 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Thu, 4 Aug 2022 10:32:58 +0300 Subject: [PATCH 54/78] add .env file --- healenium/docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 9f3d129..2dc30f0 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.3 + image: healenium/hlm-backend:${BACK_VERSION} container_name: healenium restart: on-failure ports: @@ -44,7 +44,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.0.0 + image: healenium/hlm-proxy:${PROXY_VERSION} container_name: hlm-proxy restart: on-failure ports: From bcad7eaa05cf05a202ba6ab48efa252303f1e31e Mon Sep 17 00:00:00 2001 From: Elena Date: Thu, 4 Aug 2022 16:07:52 +0300 Subject: [PATCH 55/78] Change find element 4.3.0 --- src/main/pages/markup_page.py | 4 ++-- src/main/pages/testenv_page.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/pages/markup_page.py b/src/main/pages/markup_page.py index f4c8a1c..a58bc6e 100644 --- a/src/main/pages/markup_page.py +++ b/src/main/pages/markup_page.py @@ -29,12 +29,12 @@ def open_browser(self): return self def generate_markup(self): - generate_markup = self.driver.find_element_by_id(self.generate_markup_btn_id) + generate_markup = self.driver.find_element(By.ID, self.generate_markup_btn_id) generate_markup.click() return self def click_test_button(self): - self.driver.find_element_by_class_name(self.test_button).click() + self.driver.find_element(By.CLASS_NAME, self.test_button).click() def close(self): self.driver.quit() diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py index 0ec5f8a..a8deebc 100644 --- a/src/main/pages/testenv_page.py +++ b/src/main/pages/testenv_page.py @@ -33,11 +33,11 @@ def select_checkboxes(self): return self def click_form_submit_btn(self): - self.driver.find_element_by_id(self.submit_form_btn).click() + self.driver.find_element(By.ID, self.submit_form_btn).click() return self def click_submit_btn(self): - self.driver.find_element_by_id(self.submit_btn).click() + self.driver.find_element(By.ID, self.submit_btn).click() return self def select_checkboxes_under_parent(self): From d9e13be821fd60b2c9ffe1005edc09662a380b01 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Thu, 4 Aug 2022 18:24:27 +0300 Subject: [PATCH 56/78] Revert "Change find element 4.3.0" This reverts commit bcad7eaa05cf05a202ba6ab48efa252303f1e31e. --- src/main/pages/markup_page.py | 4 ++-- src/main/pages/testenv_page.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/pages/markup_page.py b/src/main/pages/markup_page.py index a58bc6e..f4c8a1c 100644 --- a/src/main/pages/markup_page.py +++ b/src/main/pages/markup_page.py @@ -29,12 +29,12 @@ def open_browser(self): return self def generate_markup(self): - generate_markup = self.driver.find_element(By.ID, self.generate_markup_btn_id) + generate_markup = self.driver.find_element_by_id(self.generate_markup_btn_id) generate_markup.click() return self def click_test_button(self): - self.driver.find_element(By.CLASS_NAME, self.test_button).click() + self.driver.find_element_by_class_name(self.test_button).click() def close(self): self.driver.quit() diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py index a8deebc..0ec5f8a 100644 --- a/src/main/pages/testenv_page.py +++ b/src/main/pages/testenv_page.py @@ -33,11 +33,11 @@ def select_checkboxes(self): return self def click_form_submit_btn(self): - self.driver.find_element(By.ID, self.submit_form_btn).click() + self.driver.find_element_by_id(self.submit_form_btn).click() return self def click_submit_btn(self): - self.driver.find_element(By.ID, self.submit_btn).click() + self.driver.find_element_by_id(self.submit_btn).click() return self def select_checkboxes_under_parent(self): From 2439f607d91f701ecbc466924893e66ae88e0136 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Thu, 4 Aug 2022 18:24:32 +0300 Subject: [PATCH 57/78] Revert "add .env file" This reverts commit fb2547094cac5c4ae7a8d1ecca0c56e8c56db904. --- healenium/docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 2dc30f0..9f3d129 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:${BACK_VERSION} + image: healenium/hlm-backend:3.2.3 container_name: healenium restart: on-failure ports: @@ -44,7 +44,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:${PROXY_VERSION} + image: healenium/hlm-proxy:1.0.0 container_name: hlm-proxy restart: on-failure ports: From 7cb9290cce439727264f34863e3019ada93774da Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Fri, 5 Aug 2022 10:59:00 +0300 Subject: [PATCH 58/78] Update readme --- README.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2185156..4263aca 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ Python 3.9.5 + Pytest project with healenium usage example * [Healenium with Selenoid](#run-healenium-with-selenoid) * [Healenium with Selenium-Grid](#run-healenium-with-selenium-grid) -[2. Run test](#2-run-test) +[2. Configuration RemoteWebDriver for Healenium](#2-configuration-remotewebdriver-for-healenium) -[3. Monitoring tests running](#3-monitoring-tests-running) +[3. Run test](#3-run-test) + +[4. Monitoring tests running](#4-monitoring-tests-running) ## How to start @@ -53,7 +55,24 @@ Verify the next images are Up and Running - `selector imitator` (Convert healed locator to convenient format) - `selenoid`/`selenium-grid` (Selenium server) -### 2. Run test +### 2. Configuration RemoteWebDriver for Healenium + +To run using Healenium create RemoteWebDriver with URL ```http://:8085```: + +```py + nodeURL = "http://localhost:8085/" + + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + + current_webdriver = webdriver.Remote( + command_executor=nodeURL, + desired_capabilities=webdriver.DesiredCapabilities.CHROME, + options=options, + ) +``` + +### 3. Run test To run tests in terminal with pytest you need to go to execute next commands: ```sh @@ -79,7 +98,7 @@ python -m pytest ./tests/ > If you want to execute tests from specified file, please use the command: ```python -m pytest ./tests/test_css.py``` >> In case you want to run all tests in project use ```python -m pytest ./tests/``` command -### 3. Monitoring tests running +### 4. Monitoring tests running You can monitor tests running if you using Healenium with Selenoid plus Selenoid Ui, go to:
```sh http://localhost:8080 From dc8288cc348aba53e79556a75257abb0efd73431 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Fri, 19 Aug 2022 13:47:02 +0300 Subject: [PATCH 59/78] Upgrade hlm-proxy to 1.1.0 --- README.md | 13 +++++-------- healenium/docker-compose-selenium-grid.yaml | 2 +- healenium/docker-compose.yaml | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4263aca..96bfc0b 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ docker-compose -f docker-compose-selenium-grid.yaml up -d Verify the next images are Up and Running - `postgres-db` (PostgreSQL database to store etalon selector / healing / report) - `hlm-proxy` (Proxy client request to Selenium server) -- `hlm-bacand` (CRUD service) +- `hlm-backend` (CRUD service) - `selector imitator` (Convert healed locator to convenient format) - `selenoid`/`selenium-grid` (Selenium server) @@ -60,16 +60,13 @@ Verify the next images are Up and Running To run using Healenium create RemoteWebDriver with URL ```http://:8085```: ```py - nodeURL = "http://localhost:8085/" - options = webdriver.ChromeOptions() options.add_argument('--no-sandbox') - - current_webdriver = webdriver.Remote( - command_executor=nodeURL, + options.add_argument("--disable-dev-shm-usage") + self.driver = webdriver.Remote( + command_executor="http://localhost:8085/", desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options, - ) + options=options) ``` ### 3. Run test diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenium-grid.yaml index c95fc57..d7c3519 100644 --- a/healenium/docker-compose-selenium-grid.yaml +++ b/healenium/docker-compose-selenium-grid.yaml @@ -44,7 +44,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.0.0 + image: healenium/hlm-proxy:1.1.0 container_name: hlm-proxy restart: on-failure ports: diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 9f3d129..3a7a3a1 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -44,7 +44,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.0.0 + image: healenium/hlm-proxy:1.1.0 container_name: hlm-proxy restart: on-failure ports: From ab0ad1075406ad8df89eeb48833261993cfc5955 Mon Sep 17 00:00:00 2001 From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com> Date: Tue, 30 Aug 2022 16:08:27 +0300 Subject: [PATCH 60/78] Update docker-compose-selenium-grid.yaml --- healenium/docker-compose-selenium-grid.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenium-grid.yaml index d7c3519..79c29ae 100644 --- a/healenium/docker-compose-selenium-grid.yaml +++ b/healenium/docker-compose-selenium-grid.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.3 + image: healenium/hlm-backend:3.2.4 container_name: healenium restart: on-failure ports: @@ -31,6 +31,7 @@ services: - SPRING_POSTGRES_USER=healenium_user - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - SPRING_POSTGRES_DB_HOST=postgres-db + - KEY_SELECTOR_URL=true networks: - healenium From a19f1741c6349878a2ba0d2e7287fbf1ee3439c8 Mon Sep 17 00:00:00 2001 From: Aliaksei-Ashukha <69298932+Aliaksei-Ashukha@users.noreply.github.com> Date: Tue, 30 Aug 2022 16:08:59 +0300 Subject: [PATCH 61/78] Update docker-compose.yaml --- healenium/docker-compose.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 3a7a3a1..63d2b7b 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.3 + image: healenium/hlm-backend:3.2.4 container_name: healenium restart: on-failure ports: @@ -31,6 +31,7 @@ services: - SPRING_POSTGRES_USER=healenium_user - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - SPRING_POSTGRES_DB_HOST=postgres-db + - KEY_SELECTOR_URL=true networks: - healenium From 6e3f942bbfca6ef037c0b84fd1db7f06ccddb62a Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Thu, 22 Dec 2022 14:28:42 +0300 Subject: [PATCH 62/78] Update docker-compose-selenium-grid.yaml --- healenium/docker-compose-selenium-grid.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenium-grid.yaml index 79c29ae..e1a0eda 100644 --- a/healenium/docker-compose-selenium-grid.yaml +++ b/healenium/docker-compose-selenium-grid.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.4 + image: healenium/hlm-backend:3.2.5 container_name: healenium restart: on-failure ports: @@ -45,7 +45,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.1.0 + image: healenium/hlm-proxy:1.2.1 container_name: hlm-proxy restart: on-failure ports: From 9d136b00a756d23b6e6f7d8bf8c520b5a67a17b9 Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Thu, 22 Dec 2022 14:29:04 +0300 Subject: [PATCH 63/78] Update docker-compose.yaml --- healenium/docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 63d2b7b..3d9ecf3 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.4 + image: healenium/hlm-backend:3.2.5 container_name: healenium restart: on-failure ports: @@ -45,7 +45,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.1.0 + image: healenium/hlm-proxy:1.2.1 container_name: hlm-proxy restart: on-failure ports: From b9c4f6bdd7a4f5d95948c18a2a8e88b711149af8 Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Mon, 29 May 2023 02:07:49 +0300 Subject: [PATCH 64/78] Update docker-compose-selenium-grid.yaml --- healenium/docker-compose-selenium-grid.yaml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenium-grid.yaml index e1a0eda..a0645e7 100644 --- a/healenium/docker-compose-selenium-grid.yaml +++ b/healenium/docker-compose-selenium-grid.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.5 + image: healenium/hlm-backend:3.3.3 container_name: healenium restart: on-failure ports: @@ -31,12 +31,14 @@ services: - SPRING_POSTGRES_USER=healenium_user - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - SPRING_POSTGRES_DB_HOST=postgres-db - - KEY_SELECTOR_URL=true + - KEY_SELECTOR_URL=false + - COLLECT_METRICS=true + - HLM_LOG_LEVEL=info networks: - healenium selector-imitator: - image: healenium/hlm-selector-imitator:1.1 + image: healenium/hlm-selector-imitator:1.2 container_name: selector-imitator restart: on-failure ports: @@ -45,7 +47,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.2.1 + image: healenium/hlm-proxy:1.3.3 container_name: hlm-proxy restart: on-failure ports: @@ -54,10 +56,12 @@ services: - RECOVERY_TRIES=1 - SCORE_CAP=.6 - HEAL_ENABLED=true - - SELENIUM_HOST=selenium-hub - - HEALENIUM_HOST=healenium - - IMITATE_HOST=selector-imitator - - SELENIUM_URL=/ + - SELENIUM_SERVER_URL=http://hlm-selenoid:4444/wd/hub + - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub + - HEALENIUM_SERVER_URL=http://localhost:7878 + - HEALENIUM_SERVICE=http://healenium:7878 + - IMITATE_SERVICE=http://selector-imitator:8000 + - HLM_LOG_LEVEL=info networks: - healenium From 06e68f9d81779064f4de0ee19151dcc29b4d837a Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Mon, 29 May 2023 02:09:08 +0300 Subject: [PATCH 65/78] Update docker-compose.yaml --- healenium/docker-compose.yaml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 3d9ecf3..548bd46 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.2.5 + image: healenium/hlm-backend:3.3.3 container_name: healenium restart: on-failure ports: @@ -31,12 +31,14 @@ services: - SPRING_POSTGRES_USER=healenium_user - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K - SPRING_POSTGRES_DB_HOST=postgres-db - - KEY_SELECTOR_URL=true + - KEY_SELECTOR_URL=false + - COLLECT_METRICS=true + - HLM_LOG_LEVEL=info networks: - healenium selector-imitator: - image: healenium/hlm-selector-imitator:1.1 + image: healenium/hlm-selector-imitator:1.2 container_name: selector-imitator restart: on-failure ports: @@ -45,7 +47,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.2.1 + image: healenium/hlm-proxy:1.3.3 container_name: hlm-proxy restart: on-failure ports: @@ -54,10 +56,12 @@ services: - RECOVERY_TRIES=1 - SCORE_CAP=.6 - HEAL_ENABLED=true - - SELENIUM_HOST=hlm-selenoid - - HEALENIUM_HOST=healenium - - IMITATE_HOST=selector-imitator - - SELENIUM_URL=/wd/hub + - SELENIUM_SERVER_URL=http://hlm-selenoid:4444/wd/hub + - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub + - HEALENIUM_SERVER_URL=http://localhost:7878 + - HEALENIUM_SERVICE=http://healenium:7878 + - IMITATE_SERVICE=http://selector-imitator:8000 + - HLM_LOG_LEVEL=info networks: - healenium From 508b6f97e1f2b7096e095fe092c58f45c2f9de7b Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:37:02 +0300 Subject: [PATCH 66/78] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 662d3fe..c54c231 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -2,7 +2,7 @@ name: Bug report description: Create a report to help us improve title: "[BUG]: " labels: [ bug ] -assignees: ElenaStepuro +assignees: Alex-Reif body: - type: textarea @@ -73,4 +73,4 @@ body: attributes: label: Additional context description: Add any other context about the problem here - placeholder: For example, screenshot or using of additional frameworks like Sizzle library, Robot Framework or JDI, etc. If you can please, send a link to your project. \ No newline at end of file + placeholder: For example, screenshot or using of additional frameworks like Sizzle library, Robot Framework or JDI, etc. If you can please, send a link to your project. From 4a1b9273c824fe3bbb81179740f2f2463e259aa7 Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:37:35 +0300 Subject: [PATCH 67/78] Update custom.yml --- .github/ISSUE_TEMPLATE/custom.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/custom.yml b/.github/ISSUE_TEMPLATE/custom.yml index 68845c8..bdf75c4 100644 --- a/.github/ISSUE_TEMPLATE/custom.yml +++ b/.github/ISSUE_TEMPLATE/custom.yml @@ -2,7 +2,7 @@ name: Help-support template description: Describe you problem happened using Healenium and team will help title: "[Need support]: " labels: [ help wanted ] -assignees: ElenaStepuro +assignees: Alex-Reif body: - type: markdown attributes: @@ -58,4 +58,4 @@ body: attributes: label: Additional context description: Add any other context about the problem here - placeholder: For example, screenshot or using of additional frameworks like Sizzle library, Robot Framework or JDI, etc. If you can please, send a link to your project. \ No newline at end of file + placeholder: For example, screenshot or using of additional frameworks like Sizzle library, Robot Framework or JDI, etc. If you can please, send a link to your project. From 8a682de0b459ddb02b982535dc0fb01829ce3f35 Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Wed, 14 Jun 2023 13:41:33 +0300 Subject: [PATCH 68/78] Update docker-compose-selenium-grid.yaml --- healenium/docker-compose-selenium-grid.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenium-grid.yaml index a0645e7..033a0bf 100644 --- a/healenium/docker-compose-selenium-grid.yaml +++ b/healenium/docker-compose-selenium-grid.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.3.3 + image: healenium/hlm-backend:3.3.4 container_name: healenium restart: on-failure ports: From a9f4b02a01125361ca9f03e8398340c2b114e525 Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Wed, 14 Jun 2023 13:41:47 +0300 Subject: [PATCH 69/78] Update docker-compose.yaml --- healenium/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 548bd46..b5716ac 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.3.3 + image: healenium/hlm-backend:3.3.4 container_name: healenium restart: on-failure ports: From 9c44eeb2bc6f8522618a23d1558ba8b967aeab35 Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Fri, 16 Jun 2023 17:25:04 +0300 Subject: [PATCH 70/78] Update docker-compose-selenium-grid.yaml --- healenium/docker-compose-selenium-grid.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenium-grid.yaml index 033a0bf..cda0a87 100644 --- a/healenium/docker-compose-selenium-grid.yaml +++ b/healenium/docker-compose-selenium-grid.yaml @@ -47,7 +47,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.3 + image: healenium/hlm-proxy:1.3.4 container_name: hlm-proxy restart: on-failure ports: From 223ae3b1e6da6276ac30f3aac4fb9c1f9da4faf9 Mon Sep 17 00:00:00 2001 From: Alex-Reif <69298932+Alex-Reif@users.noreply.github.com> Date: Fri, 16 Jun 2023 17:25:19 +0300 Subject: [PATCH 71/78] Update docker-compose.yaml --- healenium/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index b5716ac..51c853b 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -47,7 +47,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.3 + image: healenium/hlm-proxy:1.3.4 container_name: hlm-proxy restart: on-failure ports: From 94e5430a49754f7b801e22a3bf4da8372b47b4b2 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Mon, 7 Aug 2023 23:46:10 +0300 Subject: [PATCH 72/78] Update readme --- README.md | 11 +++------ src/main/pages/callback_page.py | 7 +----- src/main/pages/testenv_page.py | 8 ++----- tests/test_semantic.py | 24 +++++++++---------- tests/test_wait.py | 42 ++++++++++++++++----------------- 5 files changed, 39 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 96bfc0b..3e94a46 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,8 @@ Verify the next images are Up and Running To run using Healenium create RemoteWebDriver with URL ```http://:8085```: ```py - options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) + options = webdriver.ChromeOptions() + self.driver = webdriver.Remote('http://localhost:8085/', options=options) ``` ### 3. Run test @@ -89,7 +84,7 @@ python -m pip install -U selenium ``` ```sh -python -m pytest ./tests/ +pytest ``` > If you want to execute tests from specified file, please use the command: ```python -m pytest ./tests/test_css.py``` diff --git a/src/main/pages/callback_page.py b/src/main/pages/callback_page.py index b02a498..57b0973 100644 --- a/src/main/pages/callback_page.py +++ b/src/main/pages/callback_page.py @@ -16,12 +16,7 @@ class CallbackPage(BasePage): def __init__(self): options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) + self.driver = webdriver.Remote('http://localhost:8085/', options=options) def open_browser(self): self.driver.get(BasePage.callbackTestPageUrl) diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py index 0ec5f8a..93a327f 100644 --- a/src/main/pages/testenv_page.py +++ b/src/main/pages/testenv_page.py @@ -8,6 +8,7 @@ class TestEnvPage(BasePage): + __test__ = False submit_btn = 'Submit' submit_form_btn = 'Submit_checkbox' @@ -15,12 +16,7 @@ class TestEnvPage(BasePage): def __init__(self): options = webdriver.ChromeOptions() - options.add_argument('--no-sandbox') - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Remote( - command_executor="http://localhost:8085/", - desired_capabilities=webdriver.DesiredCapabilities.CHROME, - options=options) + self.driver = webdriver.Remote('http://localhost:8085/', options=options) def open_browser(self): self.driver.get(BasePage.testEnvPageUrl) diff --git a/tests/test_semantic.py b/tests/test_semantic.py index 418cb5b..cbad9d7 100644 --- a/tests/test_semantic.py +++ b/tests/test_semantic.py @@ -5,18 +5,18 @@ class TestSemantic: - def test_semantic_class_name(self): - main_page = MarkupPage() - - main_page.open_browser() - main_page.click_test_button() - main_page.confirm_alert() - - main_page.generate_markup() - main_page.click_test_button() # should be healed - main_page.confirm_alert() - - main_page.close() + # def test_semantic_class_name(self): + # main_page = MarkupPage() + # + # main_page.open_browser() + # main_page.click_test_button() + # main_page.confirm_alert() + # + # main_page.generate_markup() + # main_page.click_test_button() # should be healed + # main_page.confirm_alert() + # + # main_page.close() def test_semantic_id(self): test_page = TestEnvPage() diff --git a/tests/test_wait.py b/tests/test_wait.py index 1f4321d..7dc2310 100644 --- a/tests/test_wait.py +++ b/tests/test_wait.py @@ -1,21 +1,21 @@ -from src.main.pages.markup_page import MarkupPage - - -class TestWait: - - def test_conditional_wait(self): - markup_page=MarkupPage() - - markup_page.open_browser() - markup_page.click_test_button() - markup_page.confirm_alert() - - markup_page.generate_markup() - markup_page.click_test_button() #should be healed - markup_page.confirm_alert() - - # markup_page.generate_markup() - # markup_page.click_test_button_wait(5) #should be healed - # markup_page.confirm_alert() - - markup_page.close() +# from src.main.pages.markup_page import MarkupPage +# +# +# class TestWait: +# +# # def test_conditional_wait(self): +# # markup_page=MarkupPage() +# # +# # markup_page.open_browser() +# # markup_page.click_test_button() +# # markup_page.confirm_alert() +# # +# # markup_page.generate_markup() +# # markup_page.click_test_button() #should be healed +# # markup_page.confirm_alert() +# # +# # # markup_page.generate_markup() +# # # markup_page.click_test_button_wait(5) #should be healed +# # # markup_page.confirm_alert() +# # +# # markup_page.close() From daa4d946e707bebeb64c6e65b774468178104d70 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Thu, 14 Sep 2023 19:45:43 +0300 Subject: [PATCH 73/78] Update healenium version --- README.md | 22 +- healenium/LICENSE | 201 ++++++++++++++++++ healenium/README.md | 59 ++++- healenium/docker-compose-appium.yaml | 75 +++++++ ...grid.yaml => docker-compose-selenoid.yaml} | 70 +++--- healenium/docker-compose-web.yaml | 54 +++++ healenium/docker-compose.yaml | 74 +++++-- healenium/{ => selenoid-config}/browsers.json | 20 +- .../selenium-grid/download_services.sh | 12 ++ .../selenium-grid/start_healenium.sh | 38 ++++ .../selenium-grid/stop_healenium.sh | 3 + .../web/download_services.sh | 9 + .../healenium-selector-imitator/.coveragerc | 6 + .../web/healenium-selector-imitator/.flake8 | 8 + .../.github/workflows/python-package.yml | 37 ++++ .../healenium-selector-imitator/.gitignore | 6 + .../healenium-selector-imitator/Dockerfile | 8 + .../web/healenium-selector-imitator/README.md | 70 ++++++ .../web/healenium-selector-imitator/app.py | 55 +++++ .../docker-compose.yml | 7 + .../web/healenium-selector-imitator/mypy.ini | 3 + .../requirements.txt | 2 + .../requirements_dev.txt | 9 + .../src/__init__.py | 0 .../src/datamodel/__init__.py | 5 + .../src/datamodel/imitation_request.py | 13 ++ .../src/datamodel/imitation_response.py | 10 + .../healenium-selector-imitator/src/node.py | 20 ++ .../src/selector.py | 158 ++++++++++++++ .../src/selector_imitator.py | 150 +++++++++++++ .../src/selector_parser.py | 132 ++++++++++++ .../src/selector_to_string.py | 79 +++++++ .../test/__init__.py | 0 .../test/test_api.py | 114 ++++++++++ .../test/test_imitate/__init__.py | 0 .../test_imitate_by_class_name.py | 40 ++++ .../test_imitate_by_css_selector.py | 46 ++++ .../test/test_imitate/test_imitate_by_id.py | 21 ++ .../test_imitate/test_imitate_by_link_text.py | 30 +++ .../test/test_imitate/test_imitate_by_name.py | 34 +++ .../test_imitate_by_partial_link_text.py | 37 ++++ .../test_imitate/test_imitate_by_tag_name.py | 21 ++ .../test_imitate/test_imitate_by_xpath.py | 46 ++++ .../test_imitate_unimplemented.py | 16 ++ .../test/test_integration.py | 86 ++++++++ .../test/test_parser/__init__.py | 0 .../test/test_parser/test_css_parser.py | 66 ++++++ .../test/test_parser/test_xpath_parser.py | 98 +++++++++ .../test/test_selector/__init__.py | 0 .../test_selector_from_string.py | 81 +++++++ .../test_selector/test_selector_to_string.py | 83 ++++++++ .../test/test_selector_to_string/__init__.py | 0 .../test_css_selector_constructor.py | 40 ++++ .../test_xpath_constructor.py | 52 +++++ .../shell-installation/web/start_healenium.sh | 25 +++ .../shell-installation/web/stop_healenium.sh | 3 + 56 files changed, 2262 insertions(+), 92 deletions(-) create mode 100644 healenium/LICENSE create mode 100644 healenium/docker-compose-appium.yaml rename healenium/{docker-compose-selenium-grid.yaml => docker-compose-selenoid.yaml} (59%) create mode 100644 healenium/docker-compose-web.yaml rename healenium/{ => selenoid-config}/browsers.json (50%) create mode 100644 healenium/shell-installation/selenium-grid/download_services.sh create mode 100644 healenium/shell-installation/selenium-grid/start_healenium.sh create mode 100644 healenium/shell-installation/selenium-grid/stop_healenium.sh create mode 100644 healenium/shell-installation/web/download_services.sh create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.coveragerc create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.flake8 create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.gitignore create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/Dockerfile create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/README.md create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/app.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/mypy.ini create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/requirements.txt create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/__init__.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/node.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/__init__.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/__init__.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/__init__.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/__init__.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/__init__.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py create mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py create mode 100644 healenium/shell-installation/web/start_healenium.sh create mode 100644 healenium/shell-installation/web/stop_healenium.sh diff --git a/README.md b/README.md index 3e94a46..7264f71 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ Python 3.9.5 + Pytest project with healenium usage example [1. Start Healenium components](#1-start-healenium-components) -* [Healenium with Selenoid](#run-healenium-with-selenoid) * [Healenium with Selenium-Grid](#run-healenium-with-selenium-grid) +* [Healenium with Selenoid](#run-healenium-with-selenoid) + [2. Configuration RemoteWebDriver for Healenium](#2-configuration-remotewebdriver-for-healenium) @@ -23,27 +24,28 @@ Go into healenium folder ```sh cd healenium -``` +``` -#### Run Healenium with Selenoid: +#### Run Healenium with Selenium-Grid: +```sh +docker-compose up -d +``` + +#### Run Healenium with Selenoid > Note: `browsers.json` consists of target browsers and appropriate versions. > Before run healenium you have to manually pull selenoid browser docker images with version specified in browsers.json Example pull selenoid chrome image: ```sh -docker pull selenoid/vnc:chrome_102.0 +docker pull selenoid/vnc:chrome_111.0 ``` Full list of browser images you can find [here](https://hub.docker.com/u/selenoid) -Run healenium with Selenoid: -```sh -docker-compose up -d -``` -#### Run Healenium with Selenium-Grid: +Run healenium with Selenoid: ```sh -docker-compose -f docker-compose-selenium-grid.yaml up -d +docker-compose -f docker-compose-selenoid.yaml up -d ``` ATTENTION diff --git a/healenium/LICENSE b/healenium/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/healenium/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/healenium/README.md b/healenium/README.md index e6f8235..bce8fee 100644 --- a/healenium/README.md +++ b/healenium/README.md @@ -2,14 +2,18 @@ [![Docker Pulls](https://img.shields.io/docker/pulls/healenium/hlm-backend.svg?maxAge=25920)](https://hub.docker.com/u/healenium) [![License](https://img.shields.io/badge/license-Apache-brightgreen.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![@healenium](https://img.shields.io/badge/Telegram-%40healenium-orange.svg)](https://t.me/healenium) ### Table of Contents [Overall information](#overall-information) [Healenium installation](#healenium-installation) -* [Healenium with Selenoid](#run-healenium-with-selenoid) * [Healenium with Selenium-Grid](#run-healenium-with-selenium-grid) +* [Healenium with Selenoid](#run-healenium-with-selenoid) +* [Healenium with Appium](#run-healenium-with-appium-only) + +[Healenium installation without Docker](#healenium-installation-without-docker) [Language Examples](#language-examples) * [Java](#java) @@ -24,10 +28,13 @@ Healenium acts as proxy between client and selenium server. `Docker-compose` includes the following services: - `postgres-db` (PostgreSQL database to store etalon selector / healing / report) - `hlm-proxy` (Proxy client request to Selenium server) -- `hlm-bacand` (CRUD service) +- `hlm-backend` (CRUD service) - `selector imitator` (Convert healed locator to convenient format) - `selenoid`/`selenium-grid` (Selenium server) +image + + ### Healenium installation Clone Healenium repository: @@ -35,6 +42,11 @@ Clone Healenium repository: git clone https://github.com/healenium/healenium.git ``` +#### Run Healenium with Selenium-Grid: +```sh +docker-compose up -d +``` + #### Run Healenium with Selenoid > Note: `browsers.json` consists of target browsers and appropriate versions. @@ -42,19 +54,54 @@ git clone https://github.com/healenium/healenium.git Example pull selenoid chrome image: ```sh -docker pull selenoid/vnc:chrome_102.0 +docker pull selenoid/vnc:chrome_111.0 ``` Full list of browser images you can find [here](https://hub.docker.com/u/selenoid) + Run healenium with Selenoid: ```sh -docker-compose up -d +docker-compose -f docker-compose-selenoid.yaml up -d ``` -#### Run Healenium with Selenium-Grid: +#### Run Healenium with Appium only + ```sh -docker-compose -f docker-compose-selenium-grid.yaml up -d +docker-compose -f docker-compose-appium.yaml up -d ``` +More details about integration Healenium with Appium [here](https://github.com/healenium/healenium-appium) + + +### Healenium installation without Docker + +Go to shell-installation: + +```sh +cd shell-installatio +``` + +There are web and remote options to run healenium. + +1. Start PostgeSql server. +- Create user (healenium_user/YDk2nmNs4s9aCP6K) (example data) +- Set attribute 'Can Login' (true) to user +- Create database (healenium) and set owner healenium_user +- Create schema (healenium) and set owner healenium_user + +2. Specify your db user and password data in the bash script 'start_healenium.sh'. + +3. Setup selenium server (selenium-grid) + +Download healenium services +```sh +download_services.sh +``` + +Run shell command to launch healenium components +```sh +start_healenium.sh +``` + ### Language examples diff --git a/healenium/docker-compose-appium.yaml b/healenium/docker-compose-appium.yaml new file mode 100644 index 0000000..5798f36 --- /dev/null +++ b/healenium/docker-compose-appium.yaml @@ -0,0 +1,75 @@ +version: "3.8" + +services: + + postgres-db: + image: postgres:11-alpine + container_name: postgres-db + restart: always + ports: + - "5432:5432" + volumes: + - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql + environment: + - POSTGRES_DB=healenium + - POSTGRES_USER=healenium_user + - POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + networks: + - healenium + + healenium: + image: healenium/hlm-backend:3.4.0 + container_name: healenium + restart: on-failure + ports: + - "7878:7878" + links: + - postgres-db + environment: + - SPRING_POSTGRES_DB=healenium + - SPRING_POSTGRES_SCHEMA=healenium + - SPRING_POSTGRES_USER=healenium_user + - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + - SPRING_POSTGRES_DB_HOST=postgres-db + - KEY_SELECTOR_URL=false + - COLLECT_METRICS=true + - HLM_LOG_LEVEL=info + volumes: + - ./screenshots/:/screenshots + - ./logs/:/logs + networks: + - healenium + + selector-imitator: + image: healenium/hlm-selector-imitator:1.2 + container_name: selector-imitator + restart: on-failure + ports: + - "8000:8000" + networks: + - healenium + + hlm-proxy: + image: healenium/hlm-proxy:1.3.5 + container_name: hlm-proxy + restart: on-failure + ports: + - "8085:8085" + environment: + - RECOVERY_TRIES=1 + - SCORE_CAP=.6 + - HEAL_ENABLED=true + - SELENIUM_SERVER_URL=http://hlm-selenoid:4444/wd/hub + - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub + - HEALENIUM_SERVER_URL=http://localhost:7878 + - HEALENIUM_SERVICE=http://healenium:7878 + - IMITATE_SERVICE=http://selector-imitator:8000 + - HLM_LOG_LEVEL=info + volumes: + - ./logs/:/logs + networks: + - healenium + +networks: + healenium: + name: healenium diff --git a/healenium/docker-compose-selenium-grid.yaml b/healenium/docker-compose-selenoid.yaml similarity index 59% rename from healenium/docker-compose-selenium-grid.yaml rename to healenium/docker-compose-selenoid.yaml index cda0a87..1b9f599 100644 --- a/healenium/docker-compose-selenium-grid.yaml +++ b/healenium/docker-compose-selenoid.yaml @@ -1,4 +1,4 @@ -version: "3.9" +version: "3.8" services: @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.3.4 + image: healenium/hlm-backend:3.4.0 container_name: healenium restart: on-failure ports: @@ -34,6 +34,9 @@ services: - KEY_SELECTOR_URL=false - COLLECT_METRICS=true - HLM_LOG_LEVEL=info + volumes: + - ./screenshots/:/screenshots + - ./logs/:/logs networks: - healenium @@ -47,7 +50,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.4 + image: healenium/hlm-proxy:1.3.5 container_name: hlm-proxy restart: on-failure ports: @@ -56,61 +59,38 @@ services: - RECOVERY_TRIES=1 - SCORE_CAP=.6 - HEAL_ENABLED=true - - SELENIUM_SERVER_URL=http://hlm-selenoid:4444/wd/hub + - SELENIUM_SERVER_URL=http://selenoid:4444/wd/hub - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub - HEALENIUM_SERVER_URL=http://localhost:7878 - HEALENIUM_SERVICE=http://healenium:7878 - IMITATE_SERVICE=http://selector-imitator:8000 - HLM_LOG_LEVEL=info + volumes: + - ./logs/:/logs networks: - healenium - chrome: - image: selenium/node-chrome:103.0 - container_name: node-chrome - shm_size: 2gb - depends_on: - - selenium-hub - environment: - - SE_EVENT_BUS_HOST=selenium-hub - - SE_EVENT_BUS_PUBLISH_PORT=4442 - - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - networks: - - healenium - - edge: - image: selenium/node-edge:103.0 - container_name: node-edge - shm_size: 2gb - depends_on: - - selenium-hub - environment: - - SE_EVENT_BUS_HOST=selenium-hub - - SE_EVENT_BUS_PUBLISH_PORT=4442 - - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - networks: - - healenium - - firefox: - image: selenium/node-firefox:101.0 - container_name: node-firefox - shm_size: 2gb - depends_on: - - selenium-hub + selenoid: + image: aerokube/selenoid:latest-release + volumes: + - ./selenoid-config:/etc/selenoid + - /var/run/docker.sock:/var/run/docker.sock + - ./selenoid-config/video:/opt/selenoid/video + - ./selenoid-config/logs:/opt/selenoid/logs environment: - - SE_EVENT_BUS_HOST=selenium-hub - - SE_EVENT_BUS_PUBLISH_PORT=4442 - - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + - OVERRIDE_VIDEO_OUTPUT_DIR=/path/to/config/video + command: ["-conf", "/etc/selenoid/browsers.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs", "-container-network", "healenium"] + ports: + - "4444:4444" networks: - healenium - selenium-hub: - image: selenium/hub:4.3.0 - container_name: selenium-hub + selenoid-ui: + image: aerokube/selenoid-ui:latest-release + restart: on-failure ports: - - "4442:4442" - - "4443:4443" - - "4444:4444" + - "8080:8080" + command: [ "--selenoid-uri", "http://selenoid:4444/" ] networks: - healenium diff --git a/healenium/docker-compose-web.yaml b/healenium/docker-compose-web.yaml new file mode 100644 index 0000000..3df5fa1 --- /dev/null +++ b/healenium/docker-compose-web.yaml @@ -0,0 +1,54 @@ +version: "3.8" + +services: + + postgres-db: + image: postgres:11-alpine + container_name: postgres-db + restart: always + ports: + - "5432:5432" + volumes: + - ./db/sql/init.sql:/docker-entrypoint-initdb.d/init.sql + environment: + - POSTGRES_DB=healenium + - POSTGRES_USER=healenium_user + - POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + networks: + - healenium + + healenium: + image: healenium/hlm-backend:3.4.0 + container_name: healenium + restart: on-failure + ports: + - "7878:7878" + links: + - postgres-db + environment: + - SPRING_POSTGRES_DB=healenium + - SPRING_POSTGRES_SCHEMA=healenium + - SPRING_POSTGRES_USER=healenium_user + - SPRING_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K + - SPRING_POSTGRES_DB_HOST=postgres-db + - KEY_SELECTOR_URL=false + - COLLECT_METRICS=true + - HLM_LOG_LEVEL=info + volumes: + - ./screenshots/:/screenshots + - ./logs/:/logs + networks: + - healenium + + selector-imitator: + image: healenium/hlm-selector-imitator:1.2 + container_name: selector-imitator + restart: on-failure + ports: + - "8000:8000" + networks: + - healenium + +networks: + healenium: + name: healenium diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 51c853b..96d2316 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -1,4 +1,4 @@ -version: "3.9" +version: "3.8" services: @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.3.4 + image: healenium/hlm-backend:3.4.0 container_name: healenium restart: on-failure ports: @@ -34,6 +34,9 @@ services: - KEY_SELECTOR_URL=false - COLLECT_METRICS=true - HLM_LOG_LEVEL=info + volumes: + - ./screenshots/:/screenshots + - ./logs/:/logs networks: - healenium @@ -47,7 +50,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.4 + image: healenium/hlm-proxy:1.3.5 container_name: hlm-proxy restart: on-failure ports: @@ -56,37 +59,66 @@ services: - RECOVERY_TRIES=1 - SCORE_CAP=.6 - HEAL_ENABLED=true - - SELENIUM_SERVER_URL=http://hlm-selenoid:4444/wd/hub + - SELENIUM_SERVER_URL=http://selenium-hub:4444/wd/hub - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub - HEALENIUM_SERVER_URL=http://localhost:7878 - HEALENIUM_SERVICE=http://healenium:7878 - IMITATE_SERVICE=http://selector-imitator:8000 - HLM_LOG_LEVEL=info + volumes: + - ./logs/:/logs networks: - healenium - selenoid: - image: healenium/hlm-selenoid:0.1.0 - container_name: hlm-selenoid - restart: on-failure - ports: - - "4444:4444" + chrome: + image: selenium/node-chrome:latest + container_name: node-chrome + shm_size: 2gb + depends_on: + - selenium-hub environment: - - PROXY_CONTAINER_NAME=hlm-proxy - volumes: - - .:/etc/selenoid/:ro - - /var/run/docker.sock:/var/run/docker.sock - command: ["-container-network", "healenium"] + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + - SE_NODE_MAX_INSTANCES=5 + - SE_NODE_MAX_SESSIONS=5 + - SE_NODE_SESSION_TIMEOUT=20 networks: - healenium - selenoid-ui: - image: aerokube/selenoid-ui:1.10.5 - container_name: selenoid-ui - restart: on-failure + edge: + image: selenium/node-edge:latest + container_name: node-edge + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + firefox: + image: selenium/node-firefox:latest + container_name: node-firefox + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + networks: + - healenium + + selenium-hub: + image: selenium/hub:latest + container_name: selenium-hub ports: - - "8080:8080" - command: [ "--selenoid-uri", "http://hlm-selenoid:4444/" ] + - "4442:4442" + - "4443:4443" + - "4444:4444" networks: - healenium diff --git a/healenium/browsers.json b/healenium/selenoid-config/browsers.json similarity index 50% rename from healenium/browsers.json rename to healenium/selenoid-config/browsers.json index 2029555..9807ec3 100644 --- a/healenium/browsers.json +++ b/healenium/selenoid-config/browsers.json @@ -1,27 +1,27 @@ { "chrome": { - "default": "102.0", + "default": "111.0", "versions": { - "102.0": { - "image": "selenoid/vnc:chrome_102.0", + "111.0": { + "image": "selenoid/vnc:chrome_111.0", "port": "4444" }, - "101.0": { - "image": "selenoid/vnc:chrome_101.0", + "110.0": { + "image": "selenoid/vnc:chrome_110.0", "port": "4444" } } }, "firefox": { - "default": "101.0", + "default": "111.0", "versions": { - "101.0": { - "image": "selenoid/vnc:firefox_101.0", + "111.0": { + "image": "selenoid/vnc:firefox_111.0", "port": "4444", "path": "/wd/hub" }, - "100.0": { - "image": "selenoid/vnc:firefox_100.0", + "110.0": { + "image": "selenoid/vnc:firefox_110.0", "port": "4444", "path": "/wd/hub" } diff --git a/healenium/shell-installation/selenium-grid/download_services.sh b/healenium/shell-installation/selenium-grid/download_services.sh new file mode 100644 index 0000000..7749340 --- /dev/null +++ b/healenium/shell-installation/selenium-grid/download_services.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Versions of the services +HLM_BACKEND_VERSION="3.4.0" +HLM_PROXY_VERSION="1.3.5" + +# Downloading +curl -L https://github.com/healenium/healenium-backend/releases/download/${HLM_BACKEND_VERSION}/healenium-backend-${HLM_BACKEND_VERSION}.jar > hlm-backend.jar + +curl -L https://github.com/healenium/healenium-proxy/releases/download/${HLM_PROXY_VERSION}/hlm-proxy-${HLM_PROXY_VERSION}.jar > hlm-proxy.jar + +git clone https://github.com/healenium/healenium-selector-imitator.git diff --git a/healenium/shell-installation/selenium-grid/start_healenium.sh b/healenium/shell-installation/selenium-grid/start_healenium.sh new file mode 100644 index 0000000..90d8e87 --- /dev/null +++ b/healenium/shell-installation/selenium-grid/start_healenium.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Requirements hlm_backend +HLM_POSTGRES_DB=healenium +HLM_POSTGRES_SCHEMA=healenium +HLM_POSTGRES_USER=healenium_user +HLM_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K +HLM_COLLECT_METRICS=true +HLM_SERVER_PORT=7878 +HLM_LOG_LEVEL=info + +# Requirements hlm_proxy +RECOVERY_TRIES=1 +SCORE_CAP=.6 +HEAL_ENABLED=true +SELENIUM_SERVER_URL=http://localhost:4444 +APPIUM_SERVER_URL=http://localhost:4723/wd/hub +HEALENIUM_SERVER_URL=http://localhost:7878 +HEALENIUM_SERVICE=http://localhost:7878 +IMITATE_SERVICE=http://localhost:8000 + +# Deploy the hlm-backend service +SPRING_POSTGRES_DB=$HLM_POSTGRES_DB SPRING_POSTGRES_SCHEMA=$HLM_POSTGRES_SCHEMA SPRING_POSTGRES_USER=$HLM_POSTGRES_USER SPRING_POSTGRES_PASSWORD=$HLM_POSTGRES_PASSWORD COLLECT_METRICS=$HLM_COLLECT_METRICS SPRING_SERVER_PORT=$HLM_SERVER_PORT HLM_LOG_LEVEL=$HLM_LOG_LEVEL java -jar hlm-backend.jar 2>&1 & echo $! > ./pid-hlm-backend.file & + +# Deploy the hlm-proxy service +RECOVERY_TRIES=$RECOVERY_TRIES SCORE_CAP=$SCORE_CAP HEAL_ENABLED=$HEAL_ENABLED SELENIUM_SERVER_URL=$SELENIUM_SERVER_URL APPIUM_SERVER_URL=$APPIUM_SERVER_URL HEALENIUM_SERVER_URL=$HEALENIUM_SERVER_URL HEALENIUM_SERVICE=$HEALENIUM_SERVICE IMITATE_SERVICE=$IMITATE_SERVICE HLM_LOG_LEVEL=$HLM_LOG_LEVEL java -jar hlm-proxy.jar 2>&1 & echo $! > ./pid-hlm-proxy.file & + +# Deploy the imitator service +pip install --upgrade pip + +pip install -r healenium-selector-imitator/requirements.txt + +if [[ $OSTYPE == 'msys'* ]]; +then + python healenium-selector-imitator/app.py & echo $! > ./pid-selector.file & +else + python3 healenium-selector-imitator/app.py & echo $! > ./pid-selector.file & +fi diff --git a/healenium/shell-installation/selenium-grid/stop_healenium.sh b/healenium/shell-installation/selenium-grid/stop_healenium.sh new file mode 100644 index 0000000..813ac0d --- /dev/null +++ b/healenium/shell-installation/selenium-grid/stop_healenium.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +kill $(cat ./pid-hlm-backend.file) $(cat ./pid-hlm-proxy.file) $(cat ./pid-selector.file) \ No newline at end of file diff --git a/healenium/shell-installation/web/download_services.sh b/healenium/shell-installation/web/download_services.sh new file mode 100644 index 0000000..e13a8f6 --- /dev/null +++ b/healenium/shell-installation/web/download_services.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Versions of the services +HLM_BACKEND_VERSION="3.4.0" + +# Downloading +curl -L https://github.com/healenium/healenium-backend/releases/download/${HLM_BACKEND_VERSION}/healenium-backend-${HLM_BACKEND_VERSION}.jar > hlm-backend.jar + +git clone https://github.com/healenium/healenium-selector-imitator.git diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.coveragerc b/healenium/shell-installation/web/healenium-selector-imitator/.coveragerc new file mode 100644 index 0000000..521e972 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/.coveragerc @@ -0,0 +1,6 @@ +# .coveragerc to control coverage.py +[run] +omit = + */site-packages/* + */distutils/* + tests/* \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.flake8 b/healenium/shell-installation/web/healenium-selector-imitator/.flake8 new file mode 100644 index 0000000..2aa8cb4 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/.flake8 @@ -0,0 +1,8 @@ +[flake8] +max-line-length = 88 +exclude = + .git + .idea + .pytest_cache + env + dist \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml b/healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml new file mode 100644 index 0000000..e4d0f13 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml @@ -0,0 +1,37 @@ +name: Python package + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v2 + with: + python-version: '3.7' + - name: Install dependencies + run: | + pip install -r requirements_dev.txt + - name: Check types with mypy + run: | + mypy src app.py + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Lint with black + run: | + black --check . + - name: Test with pytest + run: | + pytest diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.gitignore b/healenium/shell-installation/web/healenium-selector-imitator/.gitignore new file mode 100644 index 0000000..2149682 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/.gitignore @@ -0,0 +1,6 @@ +env +.idea +__pycache__ +.coverage +htmlcov +dist \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/Dockerfile b/healenium/shell-installation/web/healenium-selector-imitator/Dockerfile new file mode 100644 index 0000000..b160254 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.7-slim-stretch + +COPY ./requirements.txt ./requirements.txt +COPY ./dist ./ + +RUN pip install -r requirements.txt + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/README.md b/healenium/shell-installation/web/healenium-selector-imitator/README.md new file mode 100644 index 0000000..71f2c1f --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/README.md @@ -0,0 +1,70 @@ +# Healenium selector imitator + +Selector imitator tries to reconstruct a user selector, changing only fields that the user modified in HTML. It works with the original selector (and its type) and target node (so it should be applied after the best node is already found). It proposes possible healed selectors or raises an error. +Proposed selectors are not guaranteed to find a unique web element. Uniqueness must be checked outside of the service with a selenium driver. + +1. [Example request and response](#example) +2. [Image and container resources consumption](#resources) +3. [Installation with Docker](#installation) +4. [Creating your own image from Dockerfile](#image) +5. [Local testing](#testing) + +### Example request and response + +Here is an example request. You can try out different requests with Swagger after the [installation](#installation). +![image](https://user-images.githubusercontent.com/40484210/127833016-10da747f-ae4d-480f-9624-ec1266cbbb25.png) +![image](https://user-images.githubusercontent.com/40484210/127833088-59eeced9-17c3-4b60-b1ee-f97d4421ff33.png) + + +### Image and container resources consumption +![image](https://user-images.githubusercontent.com/40484210/123598005-b7c7ff00-d7fc-11eb-9be6-fa20c181bb47.png) +![image](https://user-images.githubusercontent.com/40484210/123598058-c3b3c100-d7fc-11eb-85d9-380bacd53d6a.png) + + +### Installation with Docker +Run the following command to install and use Healenium selector imitator as a stand-alone service (you'll need docker installed): +``` +docker compose up +``` +Now you can access an API at http://localhost:8000/ +Check http://localhost:8000/docs for Swagger documentation. + +### Creating your own image from Dockerfile +If you wish to make some changes to the code and test them in Docker environment, you will need to create an image. +Run these commands from the repository directory, change the version if needed (you'll need docker and pyarmor installed): +``` +pyarmor obfuscate --platform linux.x86_64.7 --recursive --output dist/src src/__init__.py +pyarmor obfuscate --platform linux.x86_64.7 --exact app.py +docker build -t healenium/hlm-selector-imitator:1 . +docker run -d -p 8000:8000 healenium/hlm-selector-imitator:1 +``` + +### Local testing +To test or lint locally, create a virtual environment with Pyhton 3.7 and install packages from requirements_dev.txt + +Test with [pytest](https://docs.pytest.org/en/6.2.x/): +``` +pytest +``` + +Get [coverage](https://coverage.readthedocs.io/en/coverage-5.5/#) report: +``` +coverage run -m pytest +coverage report -m +coverage html +``` + +Check types with [mypy](https://mypy.readthedocs.io/en/stable/): +``` +mypy src app.py +``` + +Lint with [flake8](https://flake8.pycqa.org/en/latest/): +``` +flake8 +``` + +Format code with [black](https://github.com/psf/black): +``` +black . +``` diff --git a/healenium/shell-installation/web/healenium-selector-imitator/app.py b/healenium/shell-installation/web/healenium-selector-imitator/app.py new file mode 100644 index 0000000..f08a81d --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/app.py @@ -0,0 +1,55 @@ +import os +import uvicorn + +from fastapi import FastAPI, Request +from fastapi.responses import JSONResponse +from src.datamodel import ImitationRequestModel, ImitationResponseModel +from src.selector import Selector +from src.selector_imitator import SelectorImitator, ImitationError +from src.selector_parser import ParsingError +from typing import List + + +app = FastAPI() + + +@app.exception_handler(ImitationError) +async def imitation_error_handler( + request: Request, exc: ImitationError +) -> JSONResponse: + return JSONResponse( + status_code=422, + content={"detail": [{"msg": f"Unable to imitate the user selector: {exc}"}]}, + ) + + +@app.exception_handler(ParsingError) +async def parsing_error_handler(request: Request, exc: ParsingError) -> JSONResponse: + return JSONResponse( + status_code=422, + content={"detail": [{"msg": f"Unable to parse the user selector: {exc}"}]}, + ) + + +@app.get("/") +def main() -> str: + return "this is an entry point of the selector imitator" + + +@app.post("/imitate", response_model=List[ImitationResponseModel]) +async def imitate(request: ImitationRequestModel) -> List[ImitationResponseModel]: + user_selector = Selector.from_type_and_value( + selector_type=request.user_selector.type, value=request.user_selector.value + ) + imitator = SelectorImitator(user_selector, request.target_node) + result = [ + ImitationResponseModel( + selector_type=selector.selector_type, selector_value=str(selector) + ) + for selector in imitator.imitate() + ] + return result + + +if __name__ == "__main__": + uvicorn.run("app:app", host="0.0.0.0", port=os.getenv("PORT", 8000)) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml b/healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml new file mode 100644 index 0000000..0a8b649 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml @@ -0,0 +1,7 @@ +version: "3.9" +services: + selector-imitator: + image: "healenium/hlm-selector-imitator:1" + restart: on-failure + ports: + - "8000:8000" diff --git a/healenium/shell-installation/web/healenium-selector-imitator/mypy.ini b/healenium/shell-installation/web/healenium-selector-imitator/mypy.ini new file mode 100644 index 0000000..3e81927 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +ignore_missing_imports = True +strict = True \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/requirements.txt b/healenium/shell-installation/web/healenium-selector-imitator/requirements.txt new file mode 100644 index 0000000..f185c39 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/requirements.txt @@ -0,0 +1,2 @@ +fastapi==0.65.2 +uvicorn==0.13.4 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt b/healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt new file mode 100644 index 0000000..21f3a76 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt @@ -0,0 +1,9 @@ +black==21.6b0 +coverage==5.5 +fastapi==0.65.2 +flake8==3.9.2 +mypy==0.910 +pyarmor==6.7.3 +pytest==6.2.4 +requests==2.25.1 +uvicorn==0.13.4 \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py new file mode 100644 index 0000000..b689ec3 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py @@ -0,0 +1,5 @@ +from .imitation_request import ImitationRequestModel +from .imitation_response import ImitationResponseModel + + +__all__ = ["ImitationRequestModel", "ImitationResponseModel"] diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py new file mode 100644 index 0000000..01a24bd --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel, Field +from src.node import Node +from src.selector import SelectorType + + +class UserSelector(BaseModel): + type: SelectorType + value: str + + +class ImitationRequestModel(BaseModel): + user_selector: UserSelector = Field(..., alias="userSelector") + target_node: Node = Field(..., alias="targetNode") diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py new file mode 100644 index 0000000..f36a01b --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel, Field +from src.selector import SelectorType + + +class ImitationResponseModel(BaseModel): + selector_type: SelectorType = Field(..., alias="type") + selector_value: str = Field(..., alias="value") + + class Config: + allow_population_by_field_name = True diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/node.py b/healenium/shell-installation/web/healenium-selector-imitator/src/node.py new file mode 100644 index 0000000..b25883b --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/node.py @@ -0,0 +1,20 @@ +from pydantic import BaseModel, Field, validator +from typing import Optional, List, Dict, Union + + +class Node(BaseModel): + tag: str = "" + id: str = "" + classes: List[str] = Field(default_factory=list) + index: Optional[int] = None + other_attributes: Dict[str, str] = Field(default_factory=dict, alias="other") + inner_text: str = Field("", alias="innerText") + + class Config: + allow_population_by_field_name = True + + @validator("classes", pre=True) + def split_classes(cls, value: Union[str, List[str]]) -> List[str]: + if isinstance(value, str): + return value.split() + return value diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector.py new file mode 100644 index 0000000..29cfcfa --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/selector.py @@ -0,0 +1,158 @@ +from dataclasses import dataclass +from enum import Enum +from .selector_parser import CSSSelectorParser, XPathParser +from .selector_to_string import CSSSelectorConstructor, XPathConstructor +from typing import Optional, List, Dict + + +class SelectorType(str, Enum): + BY_CLASS_NAME = "By.className" + BY_CSS_SELECTOR = "By.cssSelector" + BY_ID = "By.id" + BY_LINK_TEXT = "By.linkText" + BY_NAME = "By.name" + BY_PARTIAL_LINK_TEXT = "By.partialLinkText" + BY_TAG_NAME = "By.tagName" + BY_XPATH = "By.xpath" + + +@dataclass +class Selector: + selector_type: SelectorType + tag: Optional[str] = None + id: Optional[str] = None + classes: Optional[List[str]] = None + index: Optional[int] = None + other_attributes: Optional[Dict[str, str]] = None + inner_text: Optional[str] = None + split_classes: bool = False + + @classmethod + def from_type_and_value(cls, selector_type: SelectorType, value: str) -> "Selector": + if selector_type is SelectorType.BY_CLASS_NAME: + return cls.from_class_name(value) + elif selector_type is SelectorType.BY_CSS_SELECTOR: + return cls.from_css(value) + elif selector_type is SelectorType.BY_ID: + return cls.from_id(value) + elif selector_type is SelectorType.BY_LINK_TEXT: + return cls.from_link_text(value) + elif selector_type is SelectorType.BY_NAME: + return cls.from_name(value) + elif selector_type is SelectorType.BY_PARTIAL_LINK_TEXT: + return cls.from_partial_link_text(value) + elif selector_type is SelectorType.BY_TAG_NAME: + return cls.from_tag_name(value) + elif selector_type is SelectorType.BY_XPATH: + return cls.from_xpath(value) + else: + raise ValueError(f"Invalid selector type: {selector_type}") + + @classmethod + def from_class_name(cls, class_name: str) -> "Selector": + return cls(selector_type=SelectorType.BY_CLASS_NAME, classes=[class_name]) + + @classmethod + def from_css(cls, css_selector: str) -> "Selector": + parser = CSSSelectorParser(css_selector) + return cls( + selector_type=SelectorType.BY_CSS_SELECTOR, + tag=parser.get_tag() or None, + id=parser.get_id() or None, + classes=parser.get_classes() or None, + other_attributes=parser.get_attributes() or None, + ) + + @classmethod + def from_id(cls, element_id: str) -> "Selector": + return cls(selector_type=SelectorType.BY_ID, id=element_id) + + @classmethod + def from_link_text(cls, link_text: str) -> "Selector": + return cls(selector_type=SelectorType.BY_LINK_TEXT, inner_text=link_text) + + @classmethod + def from_name(cls, name: str) -> "Selector": + return cls(selector_type=SelectorType.BY_NAME, other_attributes={"name": name}) + + @classmethod + def from_partial_link_text(cls, partial_link_text: str) -> "Selector": + return cls( + selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, + inner_text=partial_link_text, + ) + + @classmethod + def from_tag_name(cls, tag_name: str) -> "Selector": + return cls(selector_type=SelectorType.BY_TAG_NAME, tag=tag_name) + + @classmethod + def from_xpath(cls, xpath_selector: str) -> "Selector": + parser = XPathParser(xpath_selector) + return cls( + selector_type=SelectorType.BY_XPATH, + tag=parser.get_tag() or None, + id=parser.get_id() or None, + classes=parser.get_classes() or None, + index=parser.get_index(), + other_attributes=parser.get_attributes() or None, + inner_text=parser.get_inner_text() or None, + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Selector): + return False + return ( + self.selector_type == other.selector_type + and self.tag == other.tag + and self.id == other.id + and set(self.classes or []) == set(other.classes or []) + and self.index == other.index + and self.other_attributes == other.other_attributes + and self.inner_text == other.inner_text + ) + + def __str__(self) -> str: + if self.selector_type is SelectorType.BY_CLASS_NAME: + if self.classes is None or len(self.classes) == 0: + raise ValueError( + "BY_CLASS_NAME selector must contain one class name, none provided." + ) + if len(self.classes) == 1: + return self.classes[0] + else: + raise ValueError( + "Ambiguous BY_CLASS_NAME selector. " + "Only one class allowed for this selector type." + ) + elif self.selector_type is SelectorType.BY_CSS_SELECTOR: + return CSSSelectorConstructor( + tag=self.tag, + element_id=self.id, + classes=self.classes, + other_attributes=self.other_attributes, + ).get_string_representation() + elif self.selector_type is SelectorType.BY_ID: + return self.id if self.id is not None else "" + elif self.selector_type is SelectorType.BY_LINK_TEXT: + return self.inner_text if self.inner_text is not None else "" + elif self.selector_type is SelectorType.BY_NAME: + if self.other_attributes is not None and "name" in self.other_attributes: + return self.other_attributes["name"] + else: + raise ValueError("BY_NAME selector must contain name attribute.") + elif self.selector_type is SelectorType.BY_PARTIAL_LINK_TEXT: + return self.inner_text if self.inner_text is not None else "" + elif self.selector_type is SelectorType.BY_TAG_NAME: + return self.tag if self.tag is not None else "" + elif self.selector_type is SelectorType.BY_XPATH: + return XPathConstructor( + tag=self.tag, + element_id=self.id, + classes=self.classes, + other_attributes=self.other_attributes, + index=self.index, + inner_text=self.inner_text, + ).get_string_representation(split_classes=self.split_classes) + else: + return "" diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py new file mode 100644 index 0000000..ebfa4f9 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py @@ -0,0 +1,150 @@ +from .node import Node +from .selector import Selector, SelectorType +from typing import List + + +class ImitationError(Exception): + pass + + +class SelectorImitator: + def __init__(self, user_selector: Selector, target_node: Node): + self.user_selector = user_selector + self.target_node = target_node + + def _imitate_class_name(self) -> List[Selector]: + if self.target_node.classes: + return [ + Selector( + selector_type=SelectorType.BY_CLASS_NAME, + classes=[target_class], + ) + for target_class in self.target_node.classes + ] + else: + raise ImitationError("Target node does not belong to any class.") + + def _imitate_css_or_xpath(self) -> Selector: + target_selector = Selector(selector_type=self.user_selector.selector_type) + if self.user_selector.tag is not None: + if self.target_node.tag: + target_selector.tag = self.target_node.tag + else: + raise ImitationError("Target node does not have any tag.") + if self.user_selector.id is not None: + if self.target_node.id: + target_selector.id = self.target_node.id + else: + raise ImitationError("Target node does not have any id.") + if self.user_selector.classes is not None: + if self.target_node.classes: + target_selector.classes = [] + some_class_changed = False + for cls in self.user_selector.classes: + if cls in self.target_node.classes: + target_selector.classes.append(cls) + else: + some_class_changed = True + break + if some_class_changed: + target_selector.classes = self.target_node.classes + else: + raise ImitationError("Target node does not belong to any classes.") + target_selector.split_classes = ( + target_selector.classes != self.target_node.classes + ) + if self.user_selector.index is not None: + if self.target_node.index: + target_selector.index = self.target_node.index + else: + raise ImitationError("Target node does not have any index.") + if self.user_selector.inner_text is not None: + if self.target_node.inner_text: + target_selector.inner_text = self.target_node.inner_text + else: + raise ImitationError("Target node does not have any inner text.") + if self.user_selector.other_attributes is not None: + target_selector.other_attributes = {} + for attribute in self.user_selector.other_attributes: + if attribute in self.target_node.other_attributes: + target_selector.other_attributes[ + attribute + ] = self.target_node.other_attributes[attribute] + else: + raise ImitationError( + f"Target node does not have an attribute {attribute}." + ) + return target_selector + + def _imitate_id(self) -> Selector: + if self.target_node.id: + return Selector(selector_type=SelectorType.BY_ID, id=self.target_node.id) + else: + raise ImitationError("Target node does not have an id.") + + def _imitate_link_text(self) -> Selector: + if self.target_node.tag != "a": + raise ImitationError( + "Target node is not an anchor tag, " + "so selector by link text is not applicable." + ) + if self.target_node.inner_text: + return Selector( + selector_type=self.user_selector.selector_type, + inner_text=self.target_node.inner_text, + ) + else: + raise ImitationError("Target node does not have a link text.") + + def _imitate_name(self) -> Selector: + if "name" in self.target_node.other_attributes: + if ( + self.user_selector.other_attributes is not None + and "name" in self.user_selector.other_attributes + ): + return Selector( + selector_type=SelectorType.BY_NAME, + other_attributes={ + "name": self.target_node.other_attributes["name"] + }, + ) + else: + raise ImitationError( + "Invalid user selector: does not contain name attribute, " + "while type is BY_NAME." + ) + else: + raise ImitationError("Target node does not have a name attribute.") + + def _by_tag_name(self) -> Selector: + if self.target_node.tag: + return Selector( + selector_type=SelectorType.BY_TAG_NAME, tag=self.target_node.tag + ) + else: + raise ImitationError("Target node does not have a tag name.") + + def imitate(self) -> List[Selector]: + """Return a list of possible selectors for a target node that imitates a user selector. + Raise ImitationError if the target node cannot be imitated. + """ + if self.user_selector.selector_type is SelectorType.BY_CLASS_NAME: + return self._imitate_class_name() + elif self.user_selector.selector_type in [ + SelectorType.BY_CSS_SELECTOR, + SelectorType.BY_XPATH, + ]: + return [self._imitate_css_or_xpath()] + elif self.user_selector.selector_type is SelectorType.BY_ID: + return [self._imitate_id()] + elif self.user_selector.selector_type in [ + SelectorType.BY_LINK_TEXT, + SelectorType.BY_PARTIAL_LINK_TEXT, + ]: + return [self._imitate_link_text()] + elif self.user_selector.selector_type is SelectorType.BY_NAME: + return [self._imitate_name()] + elif self.user_selector.selector_type is SelectorType.BY_TAG_NAME: + return [self._by_tag_name()] + else: + raise ImitationError("Imitation for user selector type is not implemented.") diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py new file mode 100644 index 0000000..dea3347 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py @@ -0,0 +1,132 @@ +import re + +from typing import List, Dict, Optional + + +class ParsingError(Exception): + pass + + +class ParsingUtils: + @staticmethod + def remove_quoted_text(text: str) -> str: + result = text + single_quote_expression = re.compile("'.*'") + double_quote_expression = re.compile('".*"') + for expression in [single_quote_expression, double_quote_expression]: + while True: + search_result = expression.search(result) + if search_result is not None: + idx_from, idx_to = search_result.span() + result = result[:idx_from] + result[idx_to:] + else: + break + return result + + +class CSSSelectorParser: + def __init__(self, selector: str): + self.selector = selector + self.validate() + + def validate(self) -> None: + expression = re.compile(r"^([\w-]*)(\.[\w-]+|#[\w-]+|\[.+])*$") + if expression.match(self.selector) is None: + raise ParsingError("Cannot parse CSS selector.") + + def get_tag(self) -> str: + expression = re.compile(r"^[\w-]+") + search_result = expression.search(self.selector) + if search_result is None: + return "" + else: + return search_result.group() + + def get_id(self) -> str: + expression = re.compile(r"#([\w-]+)") + search_result = expression.search( + ParsingUtils.remove_quoted_text(self.selector) + ) + if search_result is None: + return "" + else: + return search_result.group(1) + + def get_classes(self) -> List[str]: + expression = re.compile(r"\.([\w-]+)") + return expression.findall(ParsingUtils.remove_quoted_text(self.selector)) + + def get_attributes(self) -> Dict[str, str]: + attributes = {} + raw_attributes = re.compile(r"\[[^\[\]]+]").findall(self.selector) + for attribute in raw_attributes: + attribute_expression = re.compile(r"""\[([\w-]+)=('.*'|".*")]""") + attribute_match = attribute_expression.match(attribute) + if attribute_match is not None: + attributes[attribute_match.group(1)] = ( + attribute_match.group(2).strip("'").strip('"') + ) + return attributes + + +class XPathParser: + def __init__(self, selector: str): + self.selector = selector + self.validate() + + def validate(self) -> None: + expression = re.compile(r"^//([\w-]*|\*)(\[.+])*$") + if expression.match(self.selector) is None: + raise ParsingError("Cannot parse XPath selector.") + + def get_tag(self) -> str: + expression = re.compile(r"^//([\w-]+)") + search_result = expression.search(self.selector) + if search_result is None: + return "" + else: + return search_result.group(1) + + def get_id(self) -> str: + expression = re.compile(r"""@id=('[\w-]+'|"[\w-]+")""") + search_result = expression.search(self.selector) + if search_result is None: + return "" + else: + return search_result.group(1).strip('"').strip("'") + + def get_classes(self) -> List[str]: + expression_all_classes = r"""@class=('[ \w-]+'|"[ \w-]+")""" + expression_contains = r"""contains\(@class,('[ \w-]+'|"[ \w-]+")\)""" + for expression in [expression_all_classes, expression_contains]: + search_result = re.compile(expression).search(self.selector) + if search_result is not None: + return search_result.group(1).strip('"').strip("'").split(" ") + return [] + + def get_attributes(self) -> Dict[str, str]: + attributes = {} + attribute_expression = re.compile(r"""@([\w-]+)=('[^']*'|"[^"]*")""") + raw_attributes = attribute_expression.findall(self.selector) + for attr_name, attr_value in raw_attributes: + if attr_name not in ["class", "id"]: + attributes[attr_name] = attr_value.strip("'").strip('"') + return attributes + + def get_index(self) -> Optional[int]: + expression = re.compile(r"""\[(\d)+]""") + search_result = expression.search( + ParsingUtils.remove_quoted_text(self.selector) + ) + if search_result is None: + return None + else: + return int(search_result.group(1)) + + def get_inner_text(self) -> str: + expression = re.compile(r"""text\(\)=('[^']*'|"[^"]*")""") + search_result = expression.search(self.selector) + if search_result is None: + return "" + else: + return search_result.group(1)[1:-1] diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py new file mode 100644 index 0000000..2ea9492 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py @@ -0,0 +1,79 @@ +from typing import List, Dict, Optional + + +class CSSSelectorConstructor: + def __init__( + self, + tag: Optional[str] = None, + element_id: Optional[str] = None, + classes: Optional[List[str]] = None, + other_attributes: Optional[Dict[str, str]] = None, + ): + self.tag = tag + self.element_id = element_id + if classes is not None: + self.classes = classes + else: + self.classes = [] + if other_attributes is not None: + self.other_attributes = other_attributes + else: + self.other_attributes = {} + + def get_string_representation(self) -> str: + result = "" + if self.tag is not None: + result += self.tag + if self.element_id is not None: + result += f"#{self.element_id}" + for class_name in self.classes: + result += f".{class_name}" + for attr_name, attr_value in self.other_attributes.items(): + result += f"[{attr_name}='{attr_value}']" + return result + + +class XPathConstructor: + def __init__( + self, + tag: Optional[str] = None, + element_id: Optional[str] = None, + classes: Optional[List[str]] = None, + other_attributes: Optional[Dict[str, str]] = None, + index: Optional[int] = None, + inner_text: Optional[str] = None, + ): + self.tag = tag + self.element_id = element_id + if classes is not None: + self.classes = classes + else: + self.classes = [] + if other_attributes is not None: + self.other_attributes = other_attributes + else: + self.other_attributes = {} + self.index = index + self.inner_text = inner_text + + def get_string_representation(self, split_classes: bool = False) -> str: + result = "//" + if self.tag is None: + result += "*" + else: + result += self.tag + if self.element_id is not None: + result += f"[@id='{self.element_id}']" + if split_classes: + for classname in self.classes: + result += f"[contains(@class, '{classname}')]" + elif self.classes: + merged_classes = " ".join(self.classes) + result += f"[@class='{merged_classes}']" + for attr_name, attr_value in self.other_attributes.items(): + result += f"[@{attr_name}='{attr_value}']" + if self.inner_text is not None: + result += f"[text()='{self.inner_text}']" + if self.index is not None: + result += f"[{self.index}]" + return result diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py new file mode 100644 index 0000000..7e25754 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py @@ -0,0 +1,114 @@ +import pytest + +from app import app +from fastapi.testclient import TestClient +from typing import List + + +@pytest.fixture() +def user_selector() -> dict: + return {"type": "By.xpath", "value": "//*[@value='Log In']"} + + +@pytest.fixture() +def user_selector_invalid_value() -> dict: + return {"type": "By.xpath", "value": "hello, world!"} + + +@pytest.fixture() +def target_node() -> dict: + return { + "tag": "input", + "classes": "fadeIn fourth", + "other": { + "_ngcontent-wvw-c3": "", + "type": "button", + "value": "Log In New", + }, + "parent": None, + "children": [], + } + + +@pytest.fixture() +def target_node_no_value() -> dict: + return { + "tag": "input", + "classes": "fadeIn fourth", + "other": { + "_ngcontent-wvw-c3": "", + "type": "button", + }, + "parent": None, + "children": [], + } + + +@pytest.fixture() +def expected_response() -> List[dict]: + return [{"type": "By.xpath", "value": "//*[@value='Log In New']"}] + + +@pytest.fixture() +def expected_response_imitation_error() -> dict: + return { + "detail": [ + { + "msg": "Unable to imitate the user selector: " + "Target node does not have an attribute value." + } + ] + } + + +@pytest.fixture() +def expected_response_parsing_error() -> dict: + return { + "detail": [ + { + "msg": "Unable to parse the user selector: " + "Cannot parse XPath selector." + } + ] + } + + +def test_main(): + with TestClient(app) as client: + response = client.get("/") + assert response.status_code == 200 + + +def test_imitate(user_selector: dict, target_node: dict, expected_response: List[dict]): + request_data = {"userSelector": user_selector, "targetNode": target_node} + with TestClient(app) as client: + response = client.post("/imitate", json=request_data) + assert response.status_code == 200 + assert response.json() == expected_response + + +def test_imitation_error( + user_selector: dict, + target_node_no_value: dict, + expected_response_imitation_error: List[dict], +): + request_data = {"userSelector": user_selector, "targetNode": target_node_no_value} + with TestClient(app) as client: + response = client.post("/imitate", json=request_data) + assert response.status_code == 422 + assert response.json() == expected_response_imitation_error + + +def test_parsing_error( + user_selector_invalid_value: dict, + target_node: dict, + expected_response_parsing_error: List[dict], +): + request_data = { + "userSelector": user_selector_invalid_value, + "targetNode": target_node, + } + with TestClient(app) as client: + response = client.post("/imitate", json=request_data) + assert response.status_code == 422 + assert response.json() == expected_response_parsing_error diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py new file mode 100644 index 0000000..f2b9b59 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py @@ -0,0 +1,40 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_class_name(): + user_selector = Selector( + selector_type=SelectorType.BY_CLASS_NAME, classes=["class1"] + ) + target_node = Node(classes=["class2"]) + expected_result = Selector( + selector_type=SelectorType.BY_CLASS_NAME, classes=["class2"] + ) + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_class_name_two_classes(): + user_selector = Selector( + selector_type=SelectorType.BY_CLASS_NAME, classes=["class1"] + ) + target_node = Node(classes=["class2", "class3"]) + expected_result = [ + Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["class2"]), + Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["class3"]), + ] + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert expected_result == imitated_selectors + + +def test_imitate_by_class_name_invalid_target(): + user_selector = Selector( + selector_type=SelectorType.BY_CLASS_NAME, classes=["class1"] + ) + target_node = Node(tag="p", id="some_id") + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py new file mode 100644 index 0000000..f391b27 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py @@ -0,0 +1,46 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_css_selector(): + user_selector = Selector( + selector_type=SelectorType.BY_CSS_SELECTOR, + tag="img", + other_attributes={"name": "some_name"}, + ) + target_node = Node(tag="img", other_attributes={"name": "name2"}) + expected_result = Selector( + selector_type=SelectorType.BY_CSS_SELECTOR, + tag="img", + other_attributes={"name": "name2"}, + ) + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_css_selector_invalid_attributes(): + user_selector = Selector( + selector_type=SelectorType.BY_CSS_SELECTOR, + classes=["class1"], + other_attributes={"name": "some_name"}, + ) + target_node = Node( + classes=["class2", "class3"], other_attributes={"src": "img.png"} + ) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() + + +def test_imitate_by_css_selector_invalid_target(): + user_selector = Selector( + selector_type=SelectorType.BY_CSS_SELECTOR, + tag="img", + other_attributes={"name": "some_name"}, + ) + target_node = Node(other_attributes={"name": "some_name"}) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py new file mode 100644 index 0000000..db36557 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py @@ -0,0 +1,21 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_id(): + user_selector = Selector(selector_type=SelectorType.BY_ID, id="element1") + target_node = Node(tag="h1", id="element2") + expected_result = Selector(selector_type=SelectorType.BY_ID, id="element2") + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_id_invalid_target(): + user_selector = Selector(selector_type=SelectorType.BY_ID, id="element1") + target_node = Node(tag="p", other_attributes={"name": "element1"}) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py new file mode 100644 index 0000000..9f3aea6 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py @@ -0,0 +1,30 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_link_text(): + user_selector = Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="link") + target_node = Node(tag="a", inner_text="link to external page") + expected_result = Selector( + selector_type=SelectorType.BY_LINK_TEXT, inner_text="link to external page" + ) + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_link_text_not_anchor(): + user_selector = Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="link") + target_node = Node(tag="p", inner_text="link to external page") + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() + + +def test_imitate_by_link_text_invalid_target(): + user_selector = Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="link") + target_node = Node(tag="a", inner_text="") + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py new file mode 100644 index 0000000..214afbc --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py @@ -0,0 +1,34 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_name(): + user_selector = Selector( + selector_type=SelectorType.BY_NAME, other_attributes={"name": "element1"} + ) + target_node = Node(tag="h1", other_attributes={"name": "element_1"}) + expected_result = Selector( + selector_type=SelectorType.BY_NAME, other_attributes={"name": "element_1"} + ) + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_name_invalid_target(): + user_selector = Selector( + selector_type=SelectorType.BY_NAME, other_attributes={"name": "element1"} + ) + target_node = Node(tag="h1") + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() + + +def test_imitate_by_name_invalid_user_selector(): + user_selector = Selector(selector_type=SelectorType.BY_NAME) + target_node = Node(tag="h1", other_attributes={"name": "element_1"}) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py new file mode 100644 index 0000000..bada68f --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py @@ -0,0 +1,37 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_partial_link_text(): + user_selector = Selector( + selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="link" + ) + target_node = Node(tag="a", inner_text="link to external page") + expected_result = Selector( + selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, + inner_text="link to external page", + ) + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_partial_link_text_not_anchor(): + user_selector = Selector( + selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="link" + ) + target_node = Node(tag="p", inner_text="link to external page") + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() + + +def test_imitate_by_partial_link_text_invalid_target(): + user_selector = Selector( + selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="link" + ) + target_node = Node(tag="a", inner_text="") + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py new file mode 100644 index 0000000..81ac612 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py @@ -0,0 +1,21 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_tag_name(): + user_selector = Selector(selector_type=SelectorType.BY_TAG_NAME, tag="h1") + target_node = Node(tag="h2", id="element2") + expected_result = Selector(selector_type=SelectorType.BY_TAG_NAME, tag="h2") + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_tag_name_invalid_target(): + user_selector = Selector(selector_type=SelectorType.BY_TAG_NAME, tag="h1") + target_node = Node(other_attributes={"name": "element1"}) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py new file mode 100644 index 0000000..092af98 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py @@ -0,0 +1,46 @@ +import pytest + +from src.node import Node +from src.selector import Selector, SelectorType +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_by_xpath(): + user_selector = Selector( + selector_type=SelectorType.BY_XPATH, + tag="img", + other_attributes={"name": "some_name"}, + ) + target_node = Node(tag="img", other_attributes={"name": "name2"}) + expected_result = Selector( + selector_type=SelectorType.BY_XPATH, + tag="img", + other_attributes={"name": "name2"}, + ) + imitated_selectors = SelectorImitator(user_selector, target_node).imitate() + assert len(imitated_selectors) == 1 + assert imitated_selectors[0] == expected_result + + +def test_imitate_by_xpath_invalid_attributes(): + user_selector = Selector( + selector_type=SelectorType.BY_XPATH, + classes=["class1"], + other_attributes={"name": "some_name"}, + ) + target_node = Node( + classes=["class2", "class3"], other_attributes={"src": "img.png"} + ) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() + + +def test_imitate_by_xpath_invalid_target(): + user_selector = Selector( + selector_type=SelectorType.BY_XPATH, + tag="img", + other_attributes={"name": "some_name"}, + ) + target_node = Node(other_attributes={"name": "some_name"}) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py new file mode 100644 index 0000000..067aada --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py @@ -0,0 +1,16 @@ +import pytest + +from src.node import Node +from src.selector import Selector +from src.selector_imitator import SelectorImitator, ImitationError + + +def test_imitate_unimplemented(): + user_selector = Selector( + selector_type=42, + tag="img", + other_attributes={"name": "some_name"}, + ) + target_node = Node(other_attributes={"name": "some_name"}) + with pytest.raises(ImitationError): + SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py new file mode 100644 index 0000000..8cafefe --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py @@ -0,0 +1,86 @@ +from src.node import Node +from src.selector import Selector +from src.selector_imitator import SelectorImitator + + +def test_integration_css_selector_name(): + user_selector = Selector.from_css("div[name='old_name']") + target_node = Node( + tag="div", + id="my_div", + classes=["cls1", "class2"], + index=1, + other_attributes={"name": "new_name", "href": "some_link"}, + inner_text="hello world", + ) + assert ( + str(SelectorImitator(user_selector, target_node).imitate()[0]) + == "div[name='new_name']" + ) + + +def test_integration_css_selector_tag(): + user_selector = Selector.from_css("h1.cls1[name='some_name']") + target_node = Node( + tag="h2", + id="my_div", + classes=["cls1", "class2"], + index=1, + other_attributes={"name": "some_name", "href": "some_link"}, + inner_text="hello world", + ) + assert ( + str(SelectorImitator(user_selector, target_node).imitate()[0]) + == "h2.cls1[name='some_name']" + ) + + +def test_integration_xpath_selector_value(): + user_selector = Selector.from_xpath("//*[@value='Log In']") + target_node = Node( + tag="input", + classes=["fadeIn", "fourth"], + other_attributes={ + "_ngcontent-wvw-c3": "", + "type": "button", + "value": "Log In New", + }, + ) + assert ( + str(SelectorImitator(user_selector, target_node).imitate()[0]) + == "//*[@value='Log In New']" + ) + + +def test_integration_xpath_selector_class_split(): + user_selector = Selector.from_xpath("//*[@class='fadeIn'][@value='Log In']") + target_node = Node( + tag="input", + classes=["fadeIn", "fourth"], + other_attributes={ + "_ngcontent-wvw-c3": "", + "type": "button", + "value": "Log In New", + }, + ) + assert ( + str(SelectorImitator(user_selector, target_node).imitate()[0]) + == "//*[contains(@class, 'fadeIn')][@value='Log In New']" + ) + + +def test_integration_xpath_selector_class_no_split(): + user_selector = Selector.from_xpath("//*[@class='fadeIn fourth'][@value='Log In']") + target_node = Node( + tag="input", + classes=["fadeIn", "fourth"], + other_attributes={ + "_ngcontent-wvw-c3": "", + "type": "button", + "value": "Log In New", + }, + ) + assert ( + str(SelectorImitator(user_selector, target_node).imitate()[0]) + == "//*[@class='fadeIn fourth'][@value='Log In New']" + ) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py new file mode 100644 index 0000000..30108a4 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py @@ -0,0 +1,66 @@ +import pytest + +from src.selector_parser import CSSSelectorParser, ParsingError + + +def test_validation_success_simple(): + CSSSelectorParser("p") + + +def test_validation_success_complex(): + CSSSelectorParser("div#hello.class_name[href='https://github.com/hello%20world']") + + +def test_validation_error(): + with pytest.raises(ParsingError): + CSSSelectorParser("div#hello_world span.some_class") + + +def test_parse_tag(): + selector = "div#hello.class_name[href='https://github.com/hello%20world']" + assert CSSSelectorParser(selector).get_tag() == "div" + + +def test_parse_no_tag(): + selector = "#hello.class_name[href='https://github.com/hello%20world']" + assert CSSSelectorParser(selector).get_tag() == "" + + +def test_parse_tag_simple(): + selector = "div" + assert CSSSelectorParser(selector).get_tag() == "div" + + +def test_parse_id(): + selector = "div#hello-world.class_name[href='https://github.com/hello%20world']" + assert CSSSelectorParser(selector).get_id() == "hello-world" + + +def test_parse_id_empty(): + selector = "div.class_name[href='https://github.com/hello%20#world']" + assert CSSSelectorParser(selector).get_id() == "" + + +def test_parse_classes(): + selector = "div#hello.class_name.class_2[href='https://github.com/hello%20world']" + assert set(CSSSelectorParser(selector).get_classes()) == {"class_name", "class_2"} + + +def test_parse_classes_empty(): + selector = "div#hello[href='https://github.com/hello.world']" + assert set(CSSSelectorParser(selector).get_classes()) == set() + + +def test_parse_attributes_single_quotes(): + selector = "div#hello[href='https://github.com/hello.world']" + assert CSSSelectorParser(selector).get_attributes() == {"href": "hello.world"} + + +def test_parse_attributes_double_quotes(): + selector = 'div.class_hello[name="hello"]' + assert CSSSelectorParser(selector).get_attributes() == {"name": "hello"} + + +def test_parse_attributes_empty(): + selector = "div.class_hello" + assert CSSSelectorParser(selector).get_attributes() == {} diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py new file mode 100644 index 0000000..199c7ad --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py @@ -0,0 +1,98 @@ +import pytest + +from src.selector_parser import XPathParser, ParsingError + + +def test_validation_success_simple(): + XPathParser("//p") + + +def test_validation_success_complex(): + XPathParser("//label[@id='message23']") + + +def test_validation_success_no_tag(): + XPathParser("//*[@class='cls1'][1]") + + +def test_validation_error(): + with pytest.raises(ParsingError): + XPathParser("//*[@type='text']//following::input") + + +def test_parse_tag(): + assert XPathParser("//label[@id='message23']").get_tag() == "label" + + +def test_parse_tag_simple(): + assert XPathParser("//h2").get_tag() == "h2" + + +def test_parse_no_tag(): + assert XPathParser("//*[@class='cls1'][1]").get_tag() == "" + + +def test_parse_id(): + assert XPathParser("//*[@id='rt-feature']").get_id() == "rt-feature" + + +def test_parse_no_id(): + assert XPathParser("//*[@class='cls1'][1]").get_id() == "" + + +def test_parse_classes_single_class(): + assert XPathParser("//*[@class='cls1'][1]").get_classes() == ["cls1"] + + +def test_parse_classes_multiple_classes(): + assert XPathParser("//*[@class='cls1 cls2'][1]").get_classes() == ["cls1", "cls2"] + + +def test_parse_classes_single_class_with_contains(): + assert XPathParser("//*[contains(@class,'btn')]").get_classes() == ["btn"] + + +def test_parse_classes_multiple_classes_with_contains(): + assert XPathParser("//*[contains(@class,'btn cls1')]").get_classes() == [ + "btn", + "cls1", + ] + + +def test_parse_classes_no_class(): + assert XPathParser("//label[@id='message23']").get_classes() == [] + + +def test_parse_attributes_single_quotes(): + selector = "//div[@id='hello'][@href='https://github.com/hello.world']" + assert XPathParser(selector).get_attributes() == {"href": "hello.world"} + + +def test_parse_attributes_double_quotes(): + selector = '//*[@class="class-hello" and @name="hello"]' + assert XPathParser(selector).get_attributes() == {"name": "hello"} + + +def test_parse_attributes_empty(): + selector = "//*[@class='class-hello']" + assert XPathParser(selector).get_attributes() == {} + + +def test_parse_index(): + selector = "//*[@class='cls1 cls2'][2]" + assert XPathParser(selector).get_index() == 2 + + +def test_parse_index_no_index(): + selector = "//div[@id='hello'][@href='https://github.com/hello.world']" + assert XPathParser(selector).get_index() is None + + +def test_parse_inner_text(): + selector = "//p[@class='cls1 cls2' and text()='Hello, World!'][2]" + assert XPathParser(selector).get_inner_text() == "Hello, World!" + + +def test_parse_inner_text_empty(): + selector = "//p[@class='cls1 cls2'][2]" + assert XPathParser(selector).get_inner_text() == "" diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py new file mode 100644 index 0000000..976e13b --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py @@ -0,0 +1,81 @@ +from src.selector import Selector, SelectorType + + +def test_from_class_name(): + assert Selector.from_class_name("cls") == Selector( + selector_type=SelectorType.BY_CLASS_NAME, classes=["cls"] + ) + + +def test_from_css_simple(): + css_selector = "div" + assert Selector.from_css(css_selector) == Selector( + selector_type=SelectorType.BY_CSS_SELECTOR, tag="div" + ) + + +def test_from_css_complex(): + css_selector = "div#hello.class_name.cls2[href='https://github.com/hello%20world']" + assert Selector.from_css(css_selector) == Selector( + selector_type=SelectorType.BY_CSS_SELECTOR, + tag="div", + id="hello", + classes=["class_name", "cls2"], + other_attributes={"href": "hello world"}, + ) + + +def test_from_id(): + assert Selector.from_id("hello") == Selector( + selector_type=SelectorType.BY_ID, id="hello" + ) + + +def test_from_link_text(): + assert Selector.from_link_text("hello_world") == Selector( + selector_type=SelectorType.BY_LINK_TEXT, inner_text="hello_world" + ) + + +def test_from_name(): + assert Selector.from_name("some_name") == Selector( + selector_type=SelectorType.BY_NAME, other_attributes={"name": "some_name"} + ) + + +def test_from_partial_link_text(): + assert Selector.from_partial_link_text("hello_world") == Selector( + selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="hello_world" + ) + + +def test_from_tag_name(): + assert Selector.from_tag_name("h3") == Selector( + selector_type=SelectorType.BY_TAG_NAME, tag="h3" + ) + + +def test_from_xpath_simple(): + xpath_selector = "//div" + assert Selector.from_xpath(xpath_selector) == Selector( + selector_type=SelectorType.BY_XPATH, tag="div" + ) + + +def test_from_xpath_complex(): + xpath_selector = ( + "//div[@name='my_selector'][@class='classname cls2' and text()='hello world']" + ) + assert Selector.from_xpath(xpath_selector) == Selector( + selector_type=SelectorType.BY_XPATH, + tag="div", + classes=["classname", "cls2"], + other_attributes={"name": "my_selector"}, + inner_text="hello world", + ) + + +def test_from_type_and_value(): + assert Selector.from_type_and_value( + SelectorType.BY_ID, value="some-id" + ) == Selector(selector_type=SelectorType.BY_ID, id="some-id") diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py new file mode 100644 index 0000000..0e130c2 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py @@ -0,0 +1,83 @@ +import pytest + +from src.selector import Selector, SelectorType + + +def test_class_name_selector_to_string(): + assert ( + str(Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["clsname"])) + == "clsname" + ) + + +def test_class_name_selector_to_string_many_classes(): + with pytest.raises(ValueError): + str( + Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["cls1", "cls2"]) + ) + + +def test_class_name_selector_to_string_no_class(): + with pytest.raises(ValueError): + str(Selector(selector_type=SelectorType.BY_CLASS_NAME)) + + +def test_css_selector_to_string(): + assert ( + str(Selector(selector_type=SelectorType.BY_CSS_SELECTOR, tag="h2", id="hello")) + == "h2#hello" + ) + + +def test_id_selector_to_string(): + assert str(Selector(selector_type=SelectorType.BY_ID, id="hello")) == "hello" + + +def test_link_text_selector_to_string(): + assert ( + str(Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="hello world")) + == "hello world" + ) + + +def test_name_selector_to_string(): + assert ( + str( + Selector( + selector_type=SelectorType.BY_NAME, other_attributes={"name": "hello"} + ) + ) + == "hello" + ) + + +def test_name_selector_to_string_no_name(): + with pytest.raises(ValueError): + str( + Selector( + selector_type=SelectorType.BY_NAME, other_attributes={"value": "hello"} + ) + ) + + +def test_partial_link_text_selector_to_string(): + assert ( + str( + Selector( + selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, + inner_text="hello world", + ) + ) + == "hello world" + ) + + +def test_tag_selector_to_string(): + assert str(Selector(selector_type=SelectorType.BY_TAG_NAME, tag="div")) == "div" + + +def test_xpath_selector_to_string(): + assert ( + str(Selector(selector_type=SelectorType.BY_XPATH, tag="h2", id="hello")) + == "//h2[@id='hello']" + ) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py new file mode 100644 index 0000000..5885090 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py @@ -0,0 +1,40 @@ +from src.selector_to_string import CSSSelectorConstructor + + +def test_css_constructor_only_tag(): + assert CSSSelectorConstructor(tag="h2").get_string_representation() == "h2" + + +def test_css_constructor_only_id(): + assert ( + CSSSelectorConstructor(element_id="hello").get_string_representation() + == "#hello" + ) + + +def test_css_constructor_id_with_tag(): + assert ( + CSSSelectorConstructor(tag="h2", element_id="hello").get_string_representation() + == "h2#hello" + ) + + +def test_css_constructor_id_with_tag_and_classes(): + assert ( + CSSSelectorConstructor( + tag="h2", element_id="hello", classes=["class1", "cls2"] + ).get_string_representation() + == "h2#hello.class1.cls2" + ) + + +def test_css_constructor_all_elements(): + assert ( + CSSSelectorConstructor( + tag="h2", + element_id="hello", + classes=["class1", "cls2"], + other_attributes={"href": "hello world"}, + ).get_string_representation() + == "h2#hello.class1.cls2[href='https://github.com/hello%20world']" + ) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py new file mode 100644 index 0000000..37ded56 --- /dev/null +++ b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py @@ -0,0 +1,52 @@ +from src.selector_to_string import XPathConstructor + + +def test_xpath_constructor_only_tag(): + assert XPathConstructor(tag="h2").get_string_representation() == "//h2" + + +def test_xpath_constructor_only_id(): + assert ( + XPathConstructor(element_id="hello").get_string_representation() + == "//*[@id='hello']" + ) + + +def test_xpath_constructor_id_with_tag(): + assert ( + XPathConstructor(tag="h2", element_id="hello").get_string_representation() + == "//h2[@id='hello']" + ) + + +def test_xpath_constructor_id_with_tag_and_classes(): + assert ( + XPathConstructor( + tag="h2", element_id="hello", classes=["class1", "cls2"] + ).get_string_representation() + == "//h2[@id='hello'][@class='class1 cls2']" + ) + + +def test_xpath_constructor_id_with_tag_and_classes_split(): + assert ( + XPathConstructor( + tag="h2", element_id="hello", classes=["class1", "cls2"] + ).get_string_representation(split_classes=True) + == "//h2[@id='hello'][contains(@class, 'class1')][contains(@class, 'cls2')]" + ) + + +def test_xpath_constructor_all_elements(): + assert ( + XPathConstructor( + tag="h2", + element_id="hello", + classes=["cls1"], + other_attributes={"value": "42"}, + index=11, + inner_text="Hello, World!", + ).get_string_representation() + == "//h2[@id='hello'][@class='cls1']" + "[@value='42'][text()='Hello, World!'][11]" + ) diff --git a/healenium/shell-installation/web/start_healenium.sh b/healenium/shell-installation/web/start_healenium.sh new file mode 100644 index 0000000..d05a43e --- /dev/null +++ b/healenium/shell-installation/web/start_healenium.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Requirements hlm_backend +HLM_POSTGRES_DB=healenium +HLM_POSTGRES_SCHEMA=healenium +HLM_POSTGRES_USER=healenium_user +HLM_POSTGRES_PASSWORD=YDk2nmNs4s9aCP6K +HLM_COLLECT_METRICS=true +HLM_SERVER_PORT=7878 +HLM_LOG_LEVEL=info + +# Deploy the hlm-backend service +SPRING_POSTGRES_DB=$HLM_POSTGRES_DB SPRING_POSTGRES_SCHEMA=$HLM_POSTGRES_SCHEMA SPRING_POSTGRES_USER=$HLM_POSTGRES_USER SPRING_POSTGRES_PASSWORD=$HLM_POSTGRES_PASSWORD COLLECT_METRICS=$HLM_COLLECT_METRICS SPRING_SERVER_PORT=$HLM_SERVER_PORT HLM_LOG_LEVEL=$HLM_LOG_LEVEL java -jar hlm-backend.jar 2>&1 & echo $! > ./pid-hlm-backend.file & + +# Deploy the imitator service +pip install --upgrade pip + +pip install -r healenium-selector-imitator/requirements.txt + +if [[ $OSTYPE == 'msys'* ]]; +then + python healenium-selector-imitator/app.py & echo $! > ./pid-selector.file & +else + python3 healenium-selector-imitator/app.py & echo $! > ./pid-selector.file & +fi diff --git a/healenium/shell-installation/web/stop_healenium.sh b/healenium/shell-installation/web/stop_healenium.sh new file mode 100644 index 0000000..d71537d --- /dev/null +++ b/healenium/shell-installation/web/stop_healenium.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +kill $(cat ./pid-hlm-backend.file) $(cat ./pid-selector.file) \ No newline at end of file From 2d3f6c73da3a852f6507c5dc62683f5bf73a52ab Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Thu, 14 Sep 2023 19:47:09 +0300 Subject: [PATCH 74/78] remove imitator source --- .../healenium-selector-imitator/.coveragerc | 6 - .../web/healenium-selector-imitator/.flake8 | 8 - .../.github/workflows/python-package.yml | 37 ---- .../healenium-selector-imitator/.gitignore | 6 - .../healenium-selector-imitator/Dockerfile | 8 - .../web/healenium-selector-imitator/README.md | 70 -------- .../web/healenium-selector-imitator/app.py | 55 ------ .../docker-compose.yml | 7 - .../web/healenium-selector-imitator/mypy.ini | 3 - .../requirements.txt | 2 - .../requirements_dev.txt | 9 - .../src/__init__.py | 0 .../src/datamodel/__init__.py | 5 - .../src/datamodel/imitation_request.py | 13 -- .../src/datamodel/imitation_response.py | 10 -- .../healenium-selector-imitator/src/node.py | 20 --- .../src/selector.py | 158 ------------------ .../src/selector_imitator.py | 150 ----------------- .../src/selector_parser.py | 132 --------------- .../src/selector_to_string.py | 79 --------- .../test/__init__.py | 0 .../test/test_api.py | 114 ------------- .../test/test_imitate/__init__.py | 0 .../test_imitate_by_class_name.py | 40 ----- .../test_imitate_by_css_selector.py | 46 ----- .../test/test_imitate/test_imitate_by_id.py | 21 --- .../test_imitate/test_imitate_by_link_text.py | 30 ---- .../test/test_imitate/test_imitate_by_name.py | 34 ---- .../test_imitate_by_partial_link_text.py | 37 ---- .../test_imitate/test_imitate_by_tag_name.py | 21 --- .../test_imitate/test_imitate_by_xpath.py | 46 ----- .../test_imitate_unimplemented.py | 16 -- .../test/test_integration.py | 86 ---------- .../test/test_parser/__init__.py | 0 .../test/test_parser/test_css_parser.py | 66 -------- .../test/test_parser/test_xpath_parser.py | 98 ----------- .../test/test_selector/__init__.py | 0 .../test_selector_from_string.py | 81 --------- .../test_selector/test_selector_to_string.py | 83 --------- .../test/test_selector_to_string/__init__.py | 0 .../test_css_selector_constructor.py | 40 ----- .../test_xpath_constructor.py | 52 ------ 42 files changed, 1689 deletions(-) delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.coveragerc delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.flake8 delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/.gitignore delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/Dockerfile delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/README.md delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/app.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/mypy.ini delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/requirements.txt delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/__init__.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/node.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/__init__.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/__init__.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/__init__.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/__init__.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/__init__.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py delete mode 100644 healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.coveragerc b/healenium/shell-installation/web/healenium-selector-imitator/.coveragerc deleted file mode 100644 index 521e972..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/.coveragerc +++ /dev/null @@ -1,6 +0,0 @@ -# .coveragerc to control coverage.py -[run] -omit = - */site-packages/* - */distutils/* - tests/* \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.flake8 b/healenium/shell-installation/web/healenium-selector-imitator/.flake8 deleted file mode 100644 index 2aa8cb4..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/.flake8 +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -max-line-length = 88 -exclude = - .git - .idea - .pytest_cache - env - dist \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml b/healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml deleted file mode 100644 index e4d0f13..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/.github/workflows/python-package.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Python package - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Install dependencies - run: | - pip install -r requirements_dev.txt - - name: Check types with mypy - run: | - mypy src app.py - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Lint with black - run: | - black --check . - - name: Test with pytest - run: | - pytest diff --git a/healenium/shell-installation/web/healenium-selector-imitator/.gitignore b/healenium/shell-installation/web/healenium-selector-imitator/.gitignore deleted file mode 100644 index 2149682..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -env -.idea -__pycache__ -.coverage -htmlcov -dist \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/Dockerfile b/healenium/shell-installation/web/healenium-selector-imitator/Dockerfile deleted file mode 100644 index b160254..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM python:3.7-slim-stretch - -COPY ./requirements.txt ./requirements.txt -COPY ./dist ./ - -RUN pip install -r requirements.txt - -CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/README.md b/healenium/shell-installation/web/healenium-selector-imitator/README.md deleted file mode 100644 index 71f2c1f..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# Healenium selector imitator - -Selector imitator tries to reconstruct a user selector, changing only fields that the user modified in HTML. It works with the original selector (and its type) and target node (so it should be applied after the best node is already found). It proposes possible healed selectors or raises an error. -Proposed selectors are not guaranteed to find a unique web element. Uniqueness must be checked outside of the service with a selenium driver. - -1. [Example request and response](#example) -2. [Image and container resources consumption](#resources) -3. [Installation with Docker](#installation) -4. [Creating your own image from Dockerfile](#image) -5. [Local testing](#testing) - -### Example request and response - -Here is an example request. You can try out different requests with Swagger after the [installation](#installation). -![image](https://user-images.githubusercontent.com/40484210/127833016-10da747f-ae4d-480f-9624-ec1266cbbb25.png) -![image](https://user-images.githubusercontent.com/40484210/127833088-59eeced9-17c3-4b60-b1ee-f97d4421ff33.png) - - -### Image and container resources consumption -![image](https://user-images.githubusercontent.com/40484210/123598005-b7c7ff00-d7fc-11eb-9be6-fa20c181bb47.png) -![image](https://user-images.githubusercontent.com/40484210/123598058-c3b3c100-d7fc-11eb-85d9-380bacd53d6a.png) - - -### Installation with Docker -Run the following command to install and use Healenium selector imitator as a stand-alone service (you'll need docker installed): -``` -docker compose up -``` -Now you can access an API at http://localhost:8000/ -Check http://localhost:8000/docs for Swagger documentation. - -### Creating your own image from Dockerfile -If you wish to make some changes to the code and test them in Docker environment, you will need to create an image. -Run these commands from the repository directory, change the version if needed (you'll need docker and pyarmor installed): -``` -pyarmor obfuscate --platform linux.x86_64.7 --recursive --output dist/src src/__init__.py -pyarmor obfuscate --platform linux.x86_64.7 --exact app.py -docker build -t healenium/hlm-selector-imitator:1 . -docker run -d -p 8000:8000 healenium/hlm-selector-imitator:1 -``` - -### Local testing -To test or lint locally, create a virtual environment with Pyhton 3.7 and install packages from requirements_dev.txt - -Test with [pytest](https://docs.pytest.org/en/6.2.x/): -``` -pytest -``` - -Get [coverage](https://coverage.readthedocs.io/en/coverage-5.5/#) report: -``` -coverage run -m pytest -coverage report -m -coverage html -``` - -Check types with [mypy](https://mypy.readthedocs.io/en/stable/): -``` -mypy src app.py -``` - -Lint with [flake8](https://flake8.pycqa.org/en/latest/): -``` -flake8 -``` - -Format code with [black](https://github.com/psf/black): -``` -black . -``` diff --git a/healenium/shell-installation/web/healenium-selector-imitator/app.py b/healenium/shell-installation/web/healenium-selector-imitator/app.py deleted file mode 100644 index f08a81d..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/app.py +++ /dev/null @@ -1,55 +0,0 @@ -import os -import uvicorn - -from fastapi import FastAPI, Request -from fastapi.responses import JSONResponse -from src.datamodel import ImitationRequestModel, ImitationResponseModel -from src.selector import Selector -from src.selector_imitator import SelectorImitator, ImitationError -from src.selector_parser import ParsingError -from typing import List - - -app = FastAPI() - - -@app.exception_handler(ImitationError) -async def imitation_error_handler( - request: Request, exc: ImitationError -) -> JSONResponse: - return JSONResponse( - status_code=422, - content={"detail": [{"msg": f"Unable to imitate the user selector: {exc}"}]}, - ) - - -@app.exception_handler(ParsingError) -async def parsing_error_handler(request: Request, exc: ParsingError) -> JSONResponse: - return JSONResponse( - status_code=422, - content={"detail": [{"msg": f"Unable to parse the user selector: {exc}"}]}, - ) - - -@app.get("/") -def main() -> str: - return "this is an entry point of the selector imitator" - - -@app.post("/imitate", response_model=List[ImitationResponseModel]) -async def imitate(request: ImitationRequestModel) -> List[ImitationResponseModel]: - user_selector = Selector.from_type_and_value( - selector_type=request.user_selector.type, value=request.user_selector.value - ) - imitator = SelectorImitator(user_selector, request.target_node) - result = [ - ImitationResponseModel( - selector_type=selector.selector_type, selector_value=str(selector) - ) - for selector in imitator.imitate() - ] - return result - - -if __name__ == "__main__": - uvicorn.run("app:app", host="0.0.0.0", port=os.getenv("PORT", 8000)) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml b/healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml deleted file mode 100644 index 0a8b649..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/docker-compose.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: "3.9" -services: - selector-imitator: - image: "healenium/hlm-selector-imitator:1" - restart: on-failure - ports: - - "8000:8000" diff --git a/healenium/shell-installation/web/healenium-selector-imitator/mypy.ini b/healenium/shell-installation/web/healenium-selector-imitator/mypy.ini deleted file mode 100644 index 3e81927..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/mypy.ini +++ /dev/null @@ -1,3 +0,0 @@ -[mypy] -ignore_missing_imports = True -strict = True \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/requirements.txt b/healenium/shell-installation/web/healenium-selector-imitator/requirements.txt deleted file mode 100644 index f185c39..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -fastapi==0.65.2 -uvicorn==0.13.4 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt b/healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt deleted file mode 100644 index 21f3a76..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/requirements_dev.txt +++ /dev/null @@ -1,9 +0,0 @@ -black==21.6b0 -coverage==5.5 -fastapi==0.65.2 -flake8==3.9.2 -mypy==0.910 -pyarmor==6.7.3 -pytest==6.2.4 -requests==2.25.1 -uvicorn==0.13.4 \ No newline at end of file diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py deleted file mode 100644 index b689ec3..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .imitation_request import ImitationRequestModel -from .imitation_response import ImitationResponseModel - - -__all__ = ["ImitationRequestModel", "ImitationResponseModel"] diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py deleted file mode 100644 index 01a24bd..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_request.py +++ /dev/null @@ -1,13 +0,0 @@ -from pydantic import BaseModel, Field -from src.node import Node -from src.selector import SelectorType - - -class UserSelector(BaseModel): - type: SelectorType - value: str - - -class ImitationRequestModel(BaseModel): - user_selector: UserSelector = Field(..., alias="userSelector") - target_node: Node = Field(..., alias="targetNode") diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py b/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py deleted file mode 100644 index f36a01b..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/datamodel/imitation_response.py +++ /dev/null @@ -1,10 +0,0 @@ -from pydantic import BaseModel, Field -from src.selector import SelectorType - - -class ImitationResponseModel(BaseModel): - selector_type: SelectorType = Field(..., alias="type") - selector_value: str = Field(..., alias="value") - - class Config: - allow_population_by_field_name = True diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/node.py b/healenium/shell-installation/web/healenium-selector-imitator/src/node.py deleted file mode 100644 index b25883b..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/node.py +++ /dev/null @@ -1,20 +0,0 @@ -from pydantic import BaseModel, Field, validator -from typing import Optional, List, Dict, Union - - -class Node(BaseModel): - tag: str = "" - id: str = "" - classes: List[str] = Field(default_factory=list) - index: Optional[int] = None - other_attributes: Dict[str, str] = Field(default_factory=dict, alias="other") - inner_text: str = Field("", alias="innerText") - - class Config: - allow_population_by_field_name = True - - @validator("classes", pre=True) - def split_classes(cls, value: Union[str, List[str]]) -> List[str]: - if isinstance(value, str): - return value.split() - return value diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector.py deleted file mode 100644 index 29cfcfa..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/selector.py +++ /dev/null @@ -1,158 +0,0 @@ -from dataclasses import dataclass -from enum import Enum -from .selector_parser import CSSSelectorParser, XPathParser -from .selector_to_string import CSSSelectorConstructor, XPathConstructor -from typing import Optional, List, Dict - - -class SelectorType(str, Enum): - BY_CLASS_NAME = "By.className" - BY_CSS_SELECTOR = "By.cssSelector" - BY_ID = "By.id" - BY_LINK_TEXT = "By.linkText" - BY_NAME = "By.name" - BY_PARTIAL_LINK_TEXT = "By.partialLinkText" - BY_TAG_NAME = "By.tagName" - BY_XPATH = "By.xpath" - - -@dataclass -class Selector: - selector_type: SelectorType - tag: Optional[str] = None - id: Optional[str] = None - classes: Optional[List[str]] = None - index: Optional[int] = None - other_attributes: Optional[Dict[str, str]] = None - inner_text: Optional[str] = None - split_classes: bool = False - - @classmethod - def from_type_and_value(cls, selector_type: SelectorType, value: str) -> "Selector": - if selector_type is SelectorType.BY_CLASS_NAME: - return cls.from_class_name(value) - elif selector_type is SelectorType.BY_CSS_SELECTOR: - return cls.from_css(value) - elif selector_type is SelectorType.BY_ID: - return cls.from_id(value) - elif selector_type is SelectorType.BY_LINK_TEXT: - return cls.from_link_text(value) - elif selector_type is SelectorType.BY_NAME: - return cls.from_name(value) - elif selector_type is SelectorType.BY_PARTIAL_LINK_TEXT: - return cls.from_partial_link_text(value) - elif selector_type is SelectorType.BY_TAG_NAME: - return cls.from_tag_name(value) - elif selector_type is SelectorType.BY_XPATH: - return cls.from_xpath(value) - else: - raise ValueError(f"Invalid selector type: {selector_type}") - - @classmethod - def from_class_name(cls, class_name: str) -> "Selector": - return cls(selector_type=SelectorType.BY_CLASS_NAME, classes=[class_name]) - - @classmethod - def from_css(cls, css_selector: str) -> "Selector": - parser = CSSSelectorParser(css_selector) - return cls( - selector_type=SelectorType.BY_CSS_SELECTOR, - tag=parser.get_tag() or None, - id=parser.get_id() or None, - classes=parser.get_classes() or None, - other_attributes=parser.get_attributes() or None, - ) - - @classmethod - def from_id(cls, element_id: str) -> "Selector": - return cls(selector_type=SelectorType.BY_ID, id=element_id) - - @classmethod - def from_link_text(cls, link_text: str) -> "Selector": - return cls(selector_type=SelectorType.BY_LINK_TEXT, inner_text=link_text) - - @classmethod - def from_name(cls, name: str) -> "Selector": - return cls(selector_type=SelectorType.BY_NAME, other_attributes={"name": name}) - - @classmethod - def from_partial_link_text(cls, partial_link_text: str) -> "Selector": - return cls( - selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, - inner_text=partial_link_text, - ) - - @classmethod - def from_tag_name(cls, tag_name: str) -> "Selector": - return cls(selector_type=SelectorType.BY_TAG_NAME, tag=tag_name) - - @classmethod - def from_xpath(cls, xpath_selector: str) -> "Selector": - parser = XPathParser(xpath_selector) - return cls( - selector_type=SelectorType.BY_XPATH, - tag=parser.get_tag() or None, - id=parser.get_id() or None, - classes=parser.get_classes() or None, - index=parser.get_index(), - other_attributes=parser.get_attributes() or None, - inner_text=parser.get_inner_text() or None, - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Selector): - return False - return ( - self.selector_type == other.selector_type - and self.tag == other.tag - and self.id == other.id - and set(self.classes or []) == set(other.classes or []) - and self.index == other.index - and self.other_attributes == other.other_attributes - and self.inner_text == other.inner_text - ) - - def __str__(self) -> str: - if self.selector_type is SelectorType.BY_CLASS_NAME: - if self.classes is None or len(self.classes) == 0: - raise ValueError( - "BY_CLASS_NAME selector must contain one class name, none provided." - ) - if len(self.classes) == 1: - return self.classes[0] - else: - raise ValueError( - "Ambiguous BY_CLASS_NAME selector. " - "Only one class allowed for this selector type." - ) - elif self.selector_type is SelectorType.BY_CSS_SELECTOR: - return CSSSelectorConstructor( - tag=self.tag, - element_id=self.id, - classes=self.classes, - other_attributes=self.other_attributes, - ).get_string_representation() - elif self.selector_type is SelectorType.BY_ID: - return self.id if self.id is not None else "" - elif self.selector_type is SelectorType.BY_LINK_TEXT: - return self.inner_text if self.inner_text is not None else "" - elif self.selector_type is SelectorType.BY_NAME: - if self.other_attributes is not None and "name" in self.other_attributes: - return self.other_attributes["name"] - else: - raise ValueError("BY_NAME selector must contain name attribute.") - elif self.selector_type is SelectorType.BY_PARTIAL_LINK_TEXT: - return self.inner_text if self.inner_text is not None else "" - elif self.selector_type is SelectorType.BY_TAG_NAME: - return self.tag if self.tag is not None else "" - elif self.selector_type is SelectorType.BY_XPATH: - return XPathConstructor( - tag=self.tag, - element_id=self.id, - classes=self.classes, - other_attributes=self.other_attributes, - index=self.index, - inner_text=self.inner_text, - ).get_string_representation(split_classes=self.split_classes) - else: - return "" diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py deleted file mode 100644 index ebfa4f9..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_imitator.py +++ /dev/null @@ -1,150 +0,0 @@ -from .node import Node -from .selector import Selector, SelectorType -from typing import List - - -class ImitationError(Exception): - pass - - -class SelectorImitator: - def __init__(self, user_selector: Selector, target_node: Node): - self.user_selector = user_selector - self.target_node = target_node - - def _imitate_class_name(self) -> List[Selector]: - if self.target_node.classes: - return [ - Selector( - selector_type=SelectorType.BY_CLASS_NAME, - classes=[target_class], - ) - for target_class in self.target_node.classes - ] - else: - raise ImitationError("Target node does not belong to any class.") - - def _imitate_css_or_xpath(self) -> Selector: - target_selector = Selector(selector_type=self.user_selector.selector_type) - if self.user_selector.tag is not None: - if self.target_node.tag: - target_selector.tag = self.target_node.tag - else: - raise ImitationError("Target node does not have any tag.") - if self.user_selector.id is not None: - if self.target_node.id: - target_selector.id = self.target_node.id - else: - raise ImitationError("Target node does not have any id.") - if self.user_selector.classes is not None: - if self.target_node.classes: - target_selector.classes = [] - some_class_changed = False - for cls in self.user_selector.classes: - if cls in self.target_node.classes: - target_selector.classes.append(cls) - else: - some_class_changed = True - break - if some_class_changed: - target_selector.classes = self.target_node.classes - else: - raise ImitationError("Target node does not belong to any classes.") - target_selector.split_classes = ( - target_selector.classes != self.target_node.classes - ) - if self.user_selector.index is not None: - if self.target_node.index: - target_selector.index = self.target_node.index - else: - raise ImitationError("Target node does not have any index.") - if self.user_selector.inner_text is not None: - if self.target_node.inner_text: - target_selector.inner_text = self.target_node.inner_text - else: - raise ImitationError("Target node does not have any inner text.") - if self.user_selector.other_attributes is not None: - target_selector.other_attributes = {} - for attribute in self.user_selector.other_attributes: - if attribute in self.target_node.other_attributes: - target_selector.other_attributes[ - attribute - ] = self.target_node.other_attributes[attribute] - else: - raise ImitationError( - f"Target node does not have an attribute {attribute}." - ) - return target_selector - - def _imitate_id(self) -> Selector: - if self.target_node.id: - return Selector(selector_type=SelectorType.BY_ID, id=self.target_node.id) - else: - raise ImitationError("Target node does not have an id.") - - def _imitate_link_text(self) -> Selector: - if self.target_node.tag != "a": - raise ImitationError( - "Target node is not an anchor tag, " - "so selector by link text is not applicable." - ) - if self.target_node.inner_text: - return Selector( - selector_type=self.user_selector.selector_type, - inner_text=self.target_node.inner_text, - ) - else: - raise ImitationError("Target node does not have a link text.") - - def _imitate_name(self) -> Selector: - if "name" in self.target_node.other_attributes: - if ( - self.user_selector.other_attributes is not None - and "name" in self.user_selector.other_attributes - ): - return Selector( - selector_type=SelectorType.BY_NAME, - other_attributes={ - "name": self.target_node.other_attributes["name"] - }, - ) - else: - raise ImitationError( - "Invalid user selector: does not contain name attribute, " - "while type is BY_NAME." - ) - else: - raise ImitationError("Target node does not have a name attribute.") - - def _by_tag_name(self) -> Selector: - if self.target_node.tag: - return Selector( - selector_type=SelectorType.BY_TAG_NAME, tag=self.target_node.tag - ) - else: - raise ImitationError("Target node does not have a tag name.") - - def imitate(self) -> List[Selector]: - """Return a list of possible selectors for a target node that imitates a user selector. - Raise ImitationError if the target node cannot be imitated. - """ - if self.user_selector.selector_type is SelectorType.BY_CLASS_NAME: - return self._imitate_class_name() - elif self.user_selector.selector_type in [ - SelectorType.BY_CSS_SELECTOR, - SelectorType.BY_XPATH, - ]: - return [self._imitate_css_or_xpath()] - elif self.user_selector.selector_type is SelectorType.BY_ID: - return [self._imitate_id()] - elif self.user_selector.selector_type in [ - SelectorType.BY_LINK_TEXT, - SelectorType.BY_PARTIAL_LINK_TEXT, - ]: - return [self._imitate_link_text()] - elif self.user_selector.selector_type is SelectorType.BY_NAME: - return [self._imitate_name()] - elif self.user_selector.selector_type is SelectorType.BY_TAG_NAME: - return [self._by_tag_name()] - else: - raise ImitationError("Imitation for user selector type is not implemented.") diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py deleted file mode 100644 index dea3347..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_parser.py +++ /dev/null @@ -1,132 +0,0 @@ -import re - -from typing import List, Dict, Optional - - -class ParsingError(Exception): - pass - - -class ParsingUtils: - @staticmethod - def remove_quoted_text(text: str) -> str: - result = text - single_quote_expression = re.compile("'.*'") - double_quote_expression = re.compile('".*"') - for expression in [single_quote_expression, double_quote_expression]: - while True: - search_result = expression.search(result) - if search_result is not None: - idx_from, idx_to = search_result.span() - result = result[:idx_from] + result[idx_to:] - else: - break - return result - - -class CSSSelectorParser: - def __init__(self, selector: str): - self.selector = selector - self.validate() - - def validate(self) -> None: - expression = re.compile(r"^([\w-]*)(\.[\w-]+|#[\w-]+|\[.+])*$") - if expression.match(self.selector) is None: - raise ParsingError("Cannot parse CSS selector.") - - def get_tag(self) -> str: - expression = re.compile(r"^[\w-]+") - search_result = expression.search(self.selector) - if search_result is None: - return "" - else: - return search_result.group() - - def get_id(self) -> str: - expression = re.compile(r"#([\w-]+)") - search_result = expression.search( - ParsingUtils.remove_quoted_text(self.selector) - ) - if search_result is None: - return "" - else: - return search_result.group(1) - - def get_classes(self) -> List[str]: - expression = re.compile(r"\.([\w-]+)") - return expression.findall(ParsingUtils.remove_quoted_text(self.selector)) - - def get_attributes(self) -> Dict[str, str]: - attributes = {} - raw_attributes = re.compile(r"\[[^\[\]]+]").findall(self.selector) - for attribute in raw_attributes: - attribute_expression = re.compile(r"""\[([\w-]+)=('.*'|".*")]""") - attribute_match = attribute_expression.match(attribute) - if attribute_match is not None: - attributes[attribute_match.group(1)] = ( - attribute_match.group(2).strip("'").strip('"') - ) - return attributes - - -class XPathParser: - def __init__(self, selector: str): - self.selector = selector - self.validate() - - def validate(self) -> None: - expression = re.compile(r"^//([\w-]*|\*)(\[.+])*$") - if expression.match(self.selector) is None: - raise ParsingError("Cannot parse XPath selector.") - - def get_tag(self) -> str: - expression = re.compile(r"^//([\w-]+)") - search_result = expression.search(self.selector) - if search_result is None: - return "" - else: - return search_result.group(1) - - def get_id(self) -> str: - expression = re.compile(r"""@id=('[\w-]+'|"[\w-]+")""") - search_result = expression.search(self.selector) - if search_result is None: - return "" - else: - return search_result.group(1).strip('"').strip("'") - - def get_classes(self) -> List[str]: - expression_all_classes = r"""@class=('[ \w-]+'|"[ \w-]+")""" - expression_contains = r"""contains\(@class,('[ \w-]+'|"[ \w-]+")\)""" - for expression in [expression_all_classes, expression_contains]: - search_result = re.compile(expression).search(self.selector) - if search_result is not None: - return search_result.group(1).strip('"').strip("'").split(" ") - return [] - - def get_attributes(self) -> Dict[str, str]: - attributes = {} - attribute_expression = re.compile(r"""@([\w-]+)=('[^']*'|"[^"]*")""") - raw_attributes = attribute_expression.findall(self.selector) - for attr_name, attr_value in raw_attributes: - if attr_name not in ["class", "id"]: - attributes[attr_name] = attr_value.strip("'").strip('"') - return attributes - - def get_index(self) -> Optional[int]: - expression = re.compile(r"""\[(\d)+]""") - search_result = expression.search( - ParsingUtils.remove_quoted_text(self.selector) - ) - if search_result is None: - return None - else: - return int(search_result.group(1)) - - def get_inner_text(self) -> str: - expression = re.compile(r"""text\(\)=('[^']*'|"[^"]*")""") - search_result = expression.search(self.selector) - if search_result is None: - return "" - else: - return search_result.group(1)[1:-1] diff --git a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py b/healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py deleted file mode 100644 index 2ea9492..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/src/selector_to_string.py +++ /dev/null @@ -1,79 +0,0 @@ -from typing import List, Dict, Optional - - -class CSSSelectorConstructor: - def __init__( - self, - tag: Optional[str] = None, - element_id: Optional[str] = None, - classes: Optional[List[str]] = None, - other_attributes: Optional[Dict[str, str]] = None, - ): - self.tag = tag - self.element_id = element_id - if classes is not None: - self.classes = classes - else: - self.classes = [] - if other_attributes is not None: - self.other_attributes = other_attributes - else: - self.other_attributes = {} - - def get_string_representation(self) -> str: - result = "" - if self.tag is not None: - result += self.tag - if self.element_id is not None: - result += f"#{self.element_id}" - for class_name in self.classes: - result += f".{class_name}" - for attr_name, attr_value in self.other_attributes.items(): - result += f"[{attr_name}='{attr_value}']" - return result - - -class XPathConstructor: - def __init__( - self, - tag: Optional[str] = None, - element_id: Optional[str] = None, - classes: Optional[List[str]] = None, - other_attributes: Optional[Dict[str, str]] = None, - index: Optional[int] = None, - inner_text: Optional[str] = None, - ): - self.tag = tag - self.element_id = element_id - if classes is not None: - self.classes = classes - else: - self.classes = [] - if other_attributes is not None: - self.other_attributes = other_attributes - else: - self.other_attributes = {} - self.index = index - self.inner_text = inner_text - - def get_string_representation(self, split_classes: bool = False) -> str: - result = "//" - if self.tag is None: - result += "*" - else: - result += self.tag - if self.element_id is not None: - result += f"[@id='{self.element_id}']" - if split_classes: - for classname in self.classes: - result += f"[contains(@class, '{classname}')]" - elif self.classes: - merged_classes = " ".join(self.classes) - result += f"[@class='{merged_classes}']" - for attr_name, attr_value in self.other_attributes.items(): - result += f"[@{attr_name}='{attr_value}']" - if self.inner_text is not None: - result += f"[text()='{self.inner_text}']" - if self.index is not None: - result += f"[{self.index}]" - return result diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py deleted file mode 100644 index 7e25754..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_api.py +++ /dev/null @@ -1,114 +0,0 @@ -import pytest - -from app import app -from fastapi.testclient import TestClient -from typing import List - - -@pytest.fixture() -def user_selector() -> dict: - return {"type": "By.xpath", "value": "//*[@value='Log In']"} - - -@pytest.fixture() -def user_selector_invalid_value() -> dict: - return {"type": "By.xpath", "value": "hello, world!"} - - -@pytest.fixture() -def target_node() -> dict: - return { - "tag": "input", - "classes": "fadeIn fourth", - "other": { - "_ngcontent-wvw-c3": "", - "type": "button", - "value": "Log In New", - }, - "parent": None, - "children": [], - } - - -@pytest.fixture() -def target_node_no_value() -> dict: - return { - "tag": "input", - "classes": "fadeIn fourth", - "other": { - "_ngcontent-wvw-c3": "", - "type": "button", - }, - "parent": None, - "children": [], - } - - -@pytest.fixture() -def expected_response() -> List[dict]: - return [{"type": "By.xpath", "value": "//*[@value='Log In New']"}] - - -@pytest.fixture() -def expected_response_imitation_error() -> dict: - return { - "detail": [ - { - "msg": "Unable to imitate the user selector: " - "Target node does not have an attribute value." - } - ] - } - - -@pytest.fixture() -def expected_response_parsing_error() -> dict: - return { - "detail": [ - { - "msg": "Unable to parse the user selector: " - "Cannot parse XPath selector." - } - ] - } - - -def test_main(): - with TestClient(app) as client: - response = client.get("/") - assert response.status_code == 200 - - -def test_imitate(user_selector: dict, target_node: dict, expected_response: List[dict]): - request_data = {"userSelector": user_selector, "targetNode": target_node} - with TestClient(app) as client: - response = client.post("/imitate", json=request_data) - assert response.status_code == 200 - assert response.json() == expected_response - - -def test_imitation_error( - user_selector: dict, - target_node_no_value: dict, - expected_response_imitation_error: List[dict], -): - request_data = {"userSelector": user_selector, "targetNode": target_node_no_value} - with TestClient(app) as client: - response = client.post("/imitate", json=request_data) - assert response.status_code == 422 - assert response.json() == expected_response_imitation_error - - -def test_parsing_error( - user_selector_invalid_value: dict, - target_node: dict, - expected_response_parsing_error: List[dict], -): - request_data = { - "userSelector": user_selector_invalid_value, - "targetNode": target_node, - } - with TestClient(app) as client: - response = client.post("/imitate", json=request_data) - assert response.status_code == 422 - assert response.json() == expected_response_parsing_error diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py deleted file mode 100644 index f2b9b59..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_class_name.py +++ /dev/null @@ -1,40 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_class_name(): - user_selector = Selector( - selector_type=SelectorType.BY_CLASS_NAME, classes=["class1"] - ) - target_node = Node(classes=["class2"]) - expected_result = Selector( - selector_type=SelectorType.BY_CLASS_NAME, classes=["class2"] - ) - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_class_name_two_classes(): - user_selector = Selector( - selector_type=SelectorType.BY_CLASS_NAME, classes=["class1"] - ) - target_node = Node(classes=["class2", "class3"]) - expected_result = [ - Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["class2"]), - Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["class3"]), - ] - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert expected_result == imitated_selectors - - -def test_imitate_by_class_name_invalid_target(): - user_selector = Selector( - selector_type=SelectorType.BY_CLASS_NAME, classes=["class1"] - ) - target_node = Node(tag="p", id="some_id") - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py deleted file mode 100644 index f391b27..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_css_selector.py +++ /dev/null @@ -1,46 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_css_selector(): - user_selector = Selector( - selector_type=SelectorType.BY_CSS_SELECTOR, - tag="img", - other_attributes={"name": "some_name"}, - ) - target_node = Node(tag="img", other_attributes={"name": "name2"}) - expected_result = Selector( - selector_type=SelectorType.BY_CSS_SELECTOR, - tag="img", - other_attributes={"name": "name2"}, - ) - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_css_selector_invalid_attributes(): - user_selector = Selector( - selector_type=SelectorType.BY_CSS_SELECTOR, - classes=["class1"], - other_attributes={"name": "some_name"}, - ) - target_node = Node( - classes=["class2", "class3"], other_attributes={"src": "img.png"} - ) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() - - -def test_imitate_by_css_selector_invalid_target(): - user_selector = Selector( - selector_type=SelectorType.BY_CSS_SELECTOR, - tag="img", - other_attributes={"name": "some_name"}, - ) - target_node = Node(other_attributes={"name": "some_name"}) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py deleted file mode 100644 index db36557..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_id.py +++ /dev/null @@ -1,21 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_id(): - user_selector = Selector(selector_type=SelectorType.BY_ID, id="element1") - target_node = Node(tag="h1", id="element2") - expected_result = Selector(selector_type=SelectorType.BY_ID, id="element2") - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_id_invalid_target(): - user_selector = Selector(selector_type=SelectorType.BY_ID, id="element1") - target_node = Node(tag="p", other_attributes={"name": "element1"}) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py deleted file mode 100644 index 9f3aea6..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_link_text.py +++ /dev/null @@ -1,30 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_link_text(): - user_selector = Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="link") - target_node = Node(tag="a", inner_text="link to external page") - expected_result = Selector( - selector_type=SelectorType.BY_LINK_TEXT, inner_text="link to external page" - ) - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_link_text_not_anchor(): - user_selector = Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="link") - target_node = Node(tag="p", inner_text="link to external page") - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() - - -def test_imitate_by_link_text_invalid_target(): - user_selector = Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="link") - target_node = Node(tag="a", inner_text="") - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py deleted file mode 100644 index 214afbc..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_name.py +++ /dev/null @@ -1,34 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_name(): - user_selector = Selector( - selector_type=SelectorType.BY_NAME, other_attributes={"name": "element1"} - ) - target_node = Node(tag="h1", other_attributes={"name": "element_1"}) - expected_result = Selector( - selector_type=SelectorType.BY_NAME, other_attributes={"name": "element_1"} - ) - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_name_invalid_target(): - user_selector = Selector( - selector_type=SelectorType.BY_NAME, other_attributes={"name": "element1"} - ) - target_node = Node(tag="h1") - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() - - -def test_imitate_by_name_invalid_user_selector(): - user_selector = Selector(selector_type=SelectorType.BY_NAME) - target_node = Node(tag="h1", other_attributes={"name": "element_1"}) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py deleted file mode 100644 index bada68f..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_partial_link_text.py +++ /dev/null @@ -1,37 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_partial_link_text(): - user_selector = Selector( - selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="link" - ) - target_node = Node(tag="a", inner_text="link to external page") - expected_result = Selector( - selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, - inner_text="link to external page", - ) - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_partial_link_text_not_anchor(): - user_selector = Selector( - selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="link" - ) - target_node = Node(tag="p", inner_text="link to external page") - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() - - -def test_imitate_by_partial_link_text_invalid_target(): - user_selector = Selector( - selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="link" - ) - target_node = Node(tag="a", inner_text="") - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py deleted file mode 100644 index 81ac612..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_tag_name.py +++ /dev/null @@ -1,21 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_tag_name(): - user_selector = Selector(selector_type=SelectorType.BY_TAG_NAME, tag="h1") - target_node = Node(tag="h2", id="element2") - expected_result = Selector(selector_type=SelectorType.BY_TAG_NAME, tag="h2") - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_tag_name_invalid_target(): - user_selector = Selector(selector_type=SelectorType.BY_TAG_NAME, tag="h1") - target_node = Node(other_attributes={"name": "element1"}) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py deleted file mode 100644 index 092af98..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_by_xpath.py +++ /dev/null @@ -1,46 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector, SelectorType -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_by_xpath(): - user_selector = Selector( - selector_type=SelectorType.BY_XPATH, - tag="img", - other_attributes={"name": "some_name"}, - ) - target_node = Node(tag="img", other_attributes={"name": "name2"}) - expected_result = Selector( - selector_type=SelectorType.BY_XPATH, - tag="img", - other_attributes={"name": "name2"}, - ) - imitated_selectors = SelectorImitator(user_selector, target_node).imitate() - assert len(imitated_selectors) == 1 - assert imitated_selectors[0] == expected_result - - -def test_imitate_by_xpath_invalid_attributes(): - user_selector = Selector( - selector_type=SelectorType.BY_XPATH, - classes=["class1"], - other_attributes={"name": "some_name"}, - ) - target_node = Node( - classes=["class2", "class3"], other_attributes={"src": "img.png"} - ) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() - - -def test_imitate_by_xpath_invalid_target(): - user_selector = Selector( - selector_type=SelectorType.BY_XPATH, - tag="img", - other_attributes={"name": "some_name"}, - ) - target_node = Node(other_attributes={"name": "some_name"}) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py deleted file mode 100644 index 067aada..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_imitate/test_imitate_unimplemented.py +++ /dev/null @@ -1,16 +0,0 @@ -import pytest - -from src.node import Node -from src.selector import Selector -from src.selector_imitator import SelectorImitator, ImitationError - - -def test_imitate_unimplemented(): - user_selector = Selector( - selector_type=42, - tag="img", - other_attributes={"name": "some_name"}, - ) - target_node = Node(other_attributes={"name": "some_name"}) - with pytest.raises(ImitationError): - SelectorImitator(user_selector, target_node).imitate() diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py deleted file mode 100644 index 8cafefe..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_integration.py +++ /dev/null @@ -1,86 +0,0 @@ -from src.node import Node -from src.selector import Selector -from src.selector_imitator import SelectorImitator - - -def test_integration_css_selector_name(): - user_selector = Selector.from_css("div[name='old_name']") - target_node = Node( - tag="div", - id="my_div", - classes=["cls1", "class2"], - index=1, - other_attributes={"name": "new_name", "href": "some_link"}, - inner_text="hello world", - ) - assert ( - str(SelectorImitator(user_selector, target_node).imitate()[0]) - == "div[name='new_name']" - ) - - -def test_integration_css_selector_tag(): - user_selector = Selector.from_css("h1.cls1[name='some_name']") - target_node = Node( - tag="h2", - id="my_div", - classes=["cls1", "class2"], - index=1, - other_attributes={"name": "some_name", "href": "some_link"}, - inner_text="hello world", - ) - assert ( - str(SelectorImitator(user_selector, target_node).imitate()[0]) - == "h2.cls1[name='some_name']" - ) - - -def test_integration_xpath_selector_value(): - user_selector = Selector.from_xpath("//*[@value='Log In']") - target_node = Node( - tag="input", - classes=["fadeIn", "fourth"], - other_attributes={ - "_ngcontent-wvw-c3": "", - "type": "button", - "value": "Log In New", - }, - ) - assert ( - str(SelectorImitator(user_selector, target_node).imitate()[0]) - == "//*[@value='Log In New']" - ) - - -def test_integration_xpath_selector_class_split(): - user_selector = Selector.from_xpath("//*[@class='fadeIn'][@value='Log In']") - target_node = Node( - tag="input", - classes=["fadeIn", "fourth"], - other_attributes={ - "_ngcontent-wvw-c3": "", - "type": "button", - "value": "Log In New", - }, - ) - assert ( - str(SelectorImitator(user_selector, target_node).imitate()[0]) - == "//*[contains(@class, 'fadeIn')][@value='Log In New']" - ) - - -def test_integration_xpath_selector_class_no_split(): - user_selector = Selector.from_xpath("//*[@class='fadeIn fourth'][@value='Log In']") - target_node = Node( - tag="input", - classes=["fadeIn", "fourth"], - other_attributes={ - "_ngcontent-wvw-c3": "", - "type": "button", - "value": "Log In New", - }, - ) - assert ( - str(SelectorImitator(user_selector, target_node).imitate()[0]) - == "//*[@class='fadeIn fourth'][@value='Log In New']" - ) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py deleted file mode 100644 index 30108a4..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_css_parser.py +++ /dev/null @@ -1,66 +0,0 @@ -import pytest - -from src.selector_parser import CSSSelectorParser, ParsingError - - -def test_validation_success_simple(): - CSSSelectorParser("p") - - -def test_validation_success_complex(): - CSSSelectorParser("div#hello.class_name[href='https://github.com/hello%20world']") - - -def test_validation_error(): - with pytest.raises(ParsingError): - CSSSelectorParser("div#hello_world span.some_class") - - -def test_parse_tag(): - selector = "div#hello.class_name[href='https://github.com/hello%20world']" - assert CSSSelectorParser(selector).get_tag() == "div" - - -def test_parse_no_tag(): - selector = "#hello.class_name[href='https://github.com/hello%20world']" - assert CSSSelectorParser(selector).get_tag() == "" - - -def test_parse_tag_simple(): - selector = "div" - assert CSSSelectorParser(selector).get_tag() == "div" - - -def test_parse_id(): - selector = "div#hello-world.class_name[href='https://github.com/hello%20world']" - assert CSSSelectorParser(selector).get_id() == "hello-world" - - -def test_parse_id_empty(): - selector = "div.class_name[href='https://github.com/hello%20#world']" - assert CSSSelectorParser(selector).get_id() == "" - - -def test_parse_classes(): - selector = "div#hello.class_name.class_2[href='https://github.com/hello%20world']" - assert set(CSSSelectorParser(selector).get_classes()) == {"class_name", "class_2"} - - -def test_parse_classes_empty(): - selector = "div#hello[href='https://github.com/hello.world']" - assert set(CSSSelectorParser(selector).get_classes()) == set() - - -def test_parse_attributes_single_quotes(): - selector = "div#hello[href='https://github.com/hello.world']" - assert CSSSelectorParser(selector).get_attributes() == {"href": "hello.world"} - - -def test_parse_attributes_double_quotes(): - selector = 'div.class_hello[name="hello"]' - assert CSSSelectorParser(selector).get_attributes() == {"name": "hello"} - - -def test_parse_attributes_empty(): - selector = "div.class_hello" - assert CSSSelectorParser(selector).get_attributes() == {} diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py deleted file mode 100644 index 199c7ad..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_parser/test_xpath_parser.py +++ /dev/null @@ -1,98 +0,0 @@ -import pytest - -from src.selector_parser import XPathParser, ParsingError - - -def test_validation_success_simple(): - XPathParser("//p") - - -def test_validation_success_complex(): - XPathParser("//label[@id='message23']") - - -def test_validation_success_no_tag(): - XPathParser("//*[@class='cls1'][1]") - - -def test_validation_error(): - with pytest.raises(ParsingError): - XPathParser("//*[@type='text']//following::input") - - -def test_parse_tag(): - assert XPathParser("//label[@id='message23']").get_tag() == "label" - - -def test_parse_tag_simple(): - assert XPathParser("//h2").get_tag() == "h2" - - -def test_parse_no_tag(): - assert XPathParser("//*[@class='cls1'][1]").get_tag() == "" - - -def test_parse_id(): - assert XPathParser("//*[@id='rt-feature']").get_id() == "rt-feature" - - -def test_parse_no_id(): - assert XPathParser("//*[@class='cls1'][1]").get_id() == "" - - -def test_parse_classes_single_class(): - assert XPathParser("//*[@class='cls1'][1]").get_classes() == ["cls1"] - - -def test_parse_classes_multiple_classes(): - assert XPathParser("//*[@class='cls1 cls2'][1]").get_classes() == ["cls1", "cls2"] - - -def test_parse_classes_single_class_with_contains(): - assert XPathParser("//*[contains(@class,'btn')]").get_classes() == ["btn"] - - -def test_parse_classes_multiple_classes_with_contains(): - assert XPathParser("//*[contains(@class,'btn cls1')]").get_classes() == [ - "btn", - "cls1", - ] - - -def test_parse_classes_no_class(): - assert XPathParser("//label[@id='message23']").get_classes() == [] - - -def test_parse_attributes_single_quotes(): - selector = "//div[@id='hello'][@href='https://github.com/hello.world']" - assert XPathParser(selector).get_attributes() == {"href": "hello.world"} - - -def test_parse_attributes_double_quotes(): - selector = '//*[@class="class-hello" and @name="hello"]' - assert XPathParser(selector).get_attributes() == {"name": "hello"} - - -def test_parse_attributes_empty(): - selector = "//*[@class='class-hello']" - assert XPathParser(selector).get_attributes() == {} - - -def test_parse_index(): - selector = "//*[@class='cls1 cls2'][2]" - assert XPathParser(selector).get_index() == 2 - - -def test_parse_index_no_index(): - selector = "//div[@id='hello'][@href='https://github.com/hello.world']" - assert XPathParser(selector).get_index() is None - - -def test_parse_inner_text(): - selector = "//p[@class='cls1 cls2' and text()='Hello, World!'][2]" - assert XPathParser(selector).get_inner_text() == "Hello, World!" - - -def test_parse_inner_text_empty(): - selector = "//p[@class='cls1 cls2'][2]" - assert XPathParser(selector).get_inner_text() == "" diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py deleted file mode 100644 index 976e13b..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_from_string.py +++ /dev/null @@ -1,81 +0,0 @@ -from src.selector import Selector, SelectorType - - -def test_from_class_name(): - assert Selector.from_class_name("cls") == Selector( - selector_type=SelectorType.BY_CLASS_NAME, classes=["cls"] - ) - - -def test_from_css_simple(): - css_selector = "div" - assert Selector.from_css(css_selector) == Selector( - selector_type=SelectorType.BY_CSS_SELECTOR, tag="div" - ) - - -def test_from_css_complex(): - css_selector = "div#hello.class_name.cls2[href='https://github.com/hello%20world']" - assert Selector.from_css(css_selector) == Selector( - selector_type=SelectorType.BY_CSS_SELECTOR, - tag="div", - id="hello", - classes=["class_name", "cls2"], - other_attributes={"href": "hello world"}, - ) - - -def test_from_id(): - assert Selector.from_id("hello") == Selector( - selector_type=SelectorType.BY_ID, id="hello" - ) - - -def test_from_link_text(): - assert Selector.from_link_text("hello_world") == Selector( - selector_type=SelectorType.BY_LINK_TEXT, inner_text="hello_world" - ) - - -def test_from_name(): - assert Selector.from_name("some_name") == Selector( - selector_type=SelectorType.BY_NAME, other_attributes={"name": "some_name"} - ) - - -def test_from_partial_link_text(): - assert Selector.from_partial_link_text("hello_world") == Selector( - selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, inner_text="hello_world" - ) - - -def test_from_tag_name(): - assert Selector.from_tag_name("h3") == Selector( - selector_type=SelectorType.BY_TAG_NAME, tag="h3" - ) - - -def test_from_xpath_simple(): - xpath_selector = "//div" - assert Selector.from_xpath(xpath_selector) == Selector( - selector_type=SelectorType.BY_XPATH, tag="div" - ) - - -def test_from_xpath_complex(): - xpath_selector = ( - "//div[@name='my_selector'][@class='classname cls2' and text()='hello world']" - ) - assert Selector.from_xpath(xpath_selector) == Selector( - selector_type=SelectorType.BY_XPATH, - tag="div", - classes=["classname", "cls2"], - other_attributes={"name": "my_selector"}, - inner_text="hello world", - ) - - -def test_from_type_and_value(): - assert Selector.from_type_and_value( - SelectorType.BY_ID, value="some-id" - ) == Selector(selector_type=SelectorType.BY_ID, id="some-id") diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py deleted file mode 100644 index 0e130c2..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector/test_selector_to_string.py +++ /dev/null @@ -1,83 +0,0 @@ -import pytest - -from src.selector import Selector, SelectorType - - -def test_class_name_selector_to_string(): - assert ( - str(Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["clsname"])) - == "clsname" - ) - - -def test_class_name_selector_to_string_many_classes(): - with pytest.raises(ValueError): - str( - Selector(selector_type=SelectorType.BY_CLASS_NAME, classes=["cls1", "cls2"]) - ) - - -def test_class_name_selector_to_string_no_class(): - with pytest.raises(ValueError): - str(Selector(selector_type=SelectorType.BY_CLASS_NAME)) - - -def test_css_selector_to_string(): - assert ( - str(Selector(selector_type=SelectorType.BY_CSS_SELECTOR, tag="h2", id="hello")) - == "h2#hello" - ) - - -def test_id_selector_to_string(): - assert str(Selector(selector_type=SelectorType.BY_ID, id="hello")) == "hello" - - -def test_link_text_selector_to_string(): - assert ( - str(Selector(selector_type=SelectorType.BY_LINK_TEXT, inner_text="hello world")) - == "hello world" - ) - - -def test_name_selector_to_string(): - assert ( - str( - Selector( - selector_type=SelectorType.BY_NAME, other_attributes={"name": "hello"} - ) - ) - == "hello" - ) - - -def test_name_selector_to_string_no_name(): - with pytest.raises(ValueError): - str( - Selector( - selector_type=SelectorType.BY_NAME, other_attributes={"value": "hello"} - ) - ) - - -def test_partial_link_text_selector_to_string(): - assert ( - str( - Selector( - selector_type=SelectorType.BY_PARTIAL_LINK_TEXT, - inner_text="hello world", - ) - ) - == "hello world" - ) - - -def test_tag_selector_to_string(): - assert str(Selector(selector_type=SelectorType.BY_TAG_NAME, tag="div")) == "div" - - -def test_xpath_selector_to_string(): - assert ( - str(Selector(selector_type=SelectorType.BY_XPATH, tag="h2", id="hello")) - == "//h2[@id='hello']" - ) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/__init__.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py deleted file mode 100644 index 5885090..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_css_selector_constructor.py +++ /dev/null @@ -1,40 +0,0 @@ -from src.selector_to_string import CSSSelectorConstructor - - -def test_css_constructor_only_tag(): - assert CSSSelectorConstructor(tag="h2").get_string_representation() == "h2" - - -def test_css_constructor_only_id(): - assert ( - CSSSelectorConstructor(element_id="hello").get_string_representation() - == "#hello" - ) - - -def test_css_constructor_id_with_tag(): - assert ( - CSSSelectorConstructor(tag="h2", element_id="hello").get_string_representation() - == "h2#hello" - ) - - -def test_css_constructor_id_with_tag_and_classes(): - assert ( - CSSSelectorConstructor( - tag="h2", element_id="hello", classes=["class1", "cls2"] - ).get_string_representation() - == "h2#hello.class1.cls2" - ) - - -def test_css_constructor_all_elements(): - assert ( - CSSSelectorConstructor( - tag="h2", - element_id="hello", - classes=["class1", "cls2"], - other_attributes={"href": "hello world"}, - ).get_string_representation() - == "h2#hello.class1.cls2[href='https://github.com/hello%20world']" - ) diff --git a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py b/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py deleted file mode 100644 index 37ded56..0000000 --- a/healenium/shell-installation/web/healenium-selector-imitator/test/test_selector_to_string/test_xpath_constructor.py +++ /dev/null @@ -1,52 +0,0 @@ -from src.selector_to_string import XPathConstructor - - -def test_xpath_constructor_only_tag(): - assert XPathConstructor(tag="h2").get_string_representation() == "//h2" - - -def test_xpath_constructor_only_id(): - assert ( - XPathConstructor(element_id="hello").get_string_representation() - == "//*[@id='hello']" - ) - - -def test_xpath_constructor_id_with_tag(): - assert ( - XPathConstructor(tag="h2", element_id="hello").get_string_representation() - == "//h2[@id='hello']" - ) - - -def test_xpath_constructor_id_with_tag_and_classes(): - assert ( - XPathConstructor( - tag="h2", element_id="hello", classes=["class1", "cls2"] - ).get_string_representation() - == "//h2[@id='hello'][@class='class1 cls2']" - ) - - -def test_xpath_constructor_id_with_tag_and_classes_split(): - assert ( - XPathConstructor( - tag="h2", element_id="hello", classes=["class1", "cls2"] - ).get_string_representation(split_classes=True) - == "//h2[@id='hello'][contains(@class, 'class1')][contains(@class, 'cls2')]" - ) - - -def test_xpath_constructor_all_elements(): - assert ( - XPathConstructor( - tag="h2", - element_id="hello", - classes=["cls1"], - other_attributes={"value": "42"}, - index=11, - inner_text="Hello, World!", - ).get_string_representation() - == "//h2[@id='hello'][@class='cls1']" - "[@value='42'][text()='Hello, World!'][11]" - ) From 4920855a6be0493a83161c6ec663ded0450380d1 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Sun, 8 Oct 2023 23:17:24 +0300 Subject: [PATCH 75/78] update to 1.3.6 --- healenium/docker-compose-appium.yaml | 5 +++-- healenium/docker-compose-selenoid.yaml | 3 ++- healenium/docker-compose-web.yaml | 2 +- healenium/docker-compose.yaml | 5 +++-- .../shell-installation/selenium-grid/download_services.sh | 4 ++-- healenium/shell-installation/web/download_services.sh | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/healenium/docker-compose-appium.yaml b/healenium/docker-compose-appium.yaml index 5798f36..4abf355 100644 --- a/healenium/docker-compose-appium.yaml +++ b/healenium/docker-compose-appium.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.0 + image: healenium/hlm-backend:3.4.1 container_name: healenium restart: on-failure ports: @@ -33,6 +33,7 @@ services: - SPRING_POSTGRES_DB_HOST=postgres-db - KEY_SELECTOR_URL=false - COLLECT_METRICS=true + - FIND_ELEMENTS_AUTO_HEALING=false - HLM_LOG_LEVEL=info volumes: - ./screenshots/:/screenshots @@ -50,7 +51,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.5 + image: healenium/hlm-proxy:1.3.6 container_name: hlm-proxy restart: on-failure ports: diff --git a/healenium/docker-compose-selenoid.yaml b/healenium/docker-compose-selenoid.yaml index 1b9f599..8dd517c 100644 --- a/healenium/docker-compose-selenoid.yaml +++ b/healenium/docker-compose-selenoid.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.0 + image: healenium/hlm-backend:3.4.1 container_name: healenium restart: on-failure ports: @@ -33,6 +33,7 @@ services: - SPRING_POSTGRES_DB_HOST=postgres-db - KEY_SELECTOR_URL=false - COLLECT_METRICS=true + - FIND_ELEMENTS_AUTO_HEALING=false - HLM_LOG_LEVEL=info volumes: - ./screenshots/:/screenshots diff --git a/healenium/docker-compose-web.yaml b/healenium/docker-compose-web.yaml index 3df5fa1..230eb62 100644 --- a/healenium/docker-compose-web.yaml +++ b/healenium/docker-compose-web.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.0 + image: healenium/hlm-backend:3.4.1 container_name: healenium restart: on-failure ports: diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 96d2316..3c1a74f 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.0 + image: healenium/hlm-backend:3.4.1 container_name: healenium restart: on-failure ports: @@ -33,6 +33,7 @@ services: - SPRING_POSTGRES_DB_HOST=postgres-db - KEY_SELECTOR_URL=false - COLLECT_METRICS=true + - FIND_ELEMENTS_AUTO_HEALING=false - HLM_LOG_LEVEL=info volumes: - ./screenshots/:/screenshots @@ -50,7 +51,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.5 + image: healenium/hlm-proxy:1.3.6 container_name: hlm-proxy restart: on-failure ports: diff --git a/healenium/shell-installation/selenium-grid/download_services.sh b/healenium/shell-installation/selenium-grid/download_services.sh index 7749340..81a58f4 100644 --- a/healenium/shell-installation/selenium-grid/download_services.sh +++ b/healenium/shell-installation/selenium-grid/download_services.sh @@ -1,8 +1,8 @@ #!/bin/bash # Versions of the services -HLM_BACKEND_VERSION="3.4.0" -HLM_PROXY_VERSION="1.3.5" +HLM_BACKEND_VERSION="3.4.1" +HLM_PROXY_VERSION="1.3.6" # Downloading curl -L https://github.com/healenium/healenium-backend/releases/download/${HLM_BACKEND_VERSION}/healenium-backend-${HLM_BACKEND_VERSION}.jar > hlm-backend.jar diff --git a/healenium/shell-installation/web/download_services.sh b/healenium/shell-installation/web/download_services.sh index e13a8f6..811a7d1 100644 --- a/healenium/shell-installation/web/download_services.sh +++ b/healenium/shell-installation/web/download_services.sh @@ -1,7 +1,7 @@ #!/bin/bash # Versions of the services -HLM_BACKEND_VERSION="3.4.0" +HLM_BACKEND_VERSION="3.4.1" # Downloading curl -L https://github.com/healenium/healenium-backend/releases/download/${HLM_BACKEND_VERSION}/healenium-backend-${HLM_BACKEND_VERSION}.jar > hlm-backend.jar From f5ed26ecb7c2e811aaa3d328d29ddb944c8800cb Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Tue, 22 Oct 2024 18:00:48 +0300 Subject: [PATCH 76/78] update to 2.1.1 --- README.md | 8 ++++++++ src/main/pages/callback_page.py | 8 ++++++-- src/main/pages/testenv_page.py | 14 +++++++++++++ tests/test_wait.py | 36 ++++++++++++++------------------- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 7264f71..e1b14f3 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,14 @@ To run using Healenium create RemoteWebDriver with URL ```http:// Date: Wed, 23 Oct 2024 03:16:59 +0300 Subject: [PATCH 77/78] update to 2.1.1 --- src/main/pages/testenv_page.py | 11 ++++------- tests/test_wait.py | 6 +++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/pages/testenv_page.py b/src/main/pages/testenv_page.py index 4525f7d..65916b8 100644 --- a/src/main/pages/testenv_page.py +++ b/src/main/pages/testenv_page.py @@ -53,14 +53,11 @@ def find_test_element(self, locator_type, selector): def click_wait_btn(self): self.driver.find_element(By.ID, "Wait_Submit").click() - def disable_healing_true(self): - self.driver.execute_script("disable_healing_true") + def execute_script(self, script): + self.driver.execute_script(script) - def disable_healing_false(self): - self.driver.execute_script("disable_healing_false") - - def click_test_button_wait(self, seconds): - WebDriverWait(self.driver, seconds).until(ec.visibility_of_element_located((By.ID, "wait_new_element"))) + def click_test_button_wait(self): + WebDriverWait(self.driver, 10).until(ec.visibility_of_element_located((By.ID, "wait_new_element"))) def close(self): self.driver.quit() diff --git a/tests/test_wait.py b/tests/test_wait.py index 6beef0a..9c7e021 100644 --- a/tests/test_wait.py +++ b/tests/test_wait.py @@ -8,8 +8,8 @@ def test_conditional_wait(self): test_page.open_browser() test_page.click_wait_btn() - test_page.disable_healing_true() - test_page.click_test_button_wait(10) - test_page.disable_healing_false() + test_page.execute_script("disable_healing_true") + test_page.click_test_button_wait() + test_page.execute_script("disable_healing_false") test_page.close() From 8fb3ed9aa5e963572cd77f20f66045982fc3a472 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 23 Oct 2024 13:37:27 +0300 Subject: [PATCH 78/78] update to 2.1.1 --- healenium/docker-compose-appium.yaml | 11 +++++------ healenium/docker-compose-selenoid.yaml | 9 ++++----- healenium/docker-compose-web.yaml | 6 +++--- healenium/docker-compose.yaml | 9 ++++----- .../selenium-grid/download_services.sh | 4 ++-- .../selenium-grid/start_healenium.sh | 3 +-- healenium/shell-installation/web/download_services.sh | 2 +- 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/healenium/docker-compose-appium.yaml b/healenium/docker-compose-appium.yaml index 4abf355..d1472db 100644 --- a/healenium/docker-compose-appium.yaml +++ b/healenium/docker-compose-appium.yaml @@ -3,7 +3,7 @@ version: "3.8" services: postgres-db: - image: postgres:11-alpine + image: postgres:15.5-alpine container_name: postgres-db restart: always ports: @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.1 + image: healenium/hlm-backend:3.4.6 container_name: healenium restart: on-failure ports: @@ -42,7 +42,7 @@ services: - healenium selector-imitator: - image: healenium/hlm-selector-imitator:1.2 + image: healenium/hlm-selector-imitator:1.4 container_name: selector-imitator restart: on-failure ports: @@ -51,7 +51,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.6 + image: healenium/hlm-proxy:2.1.1 container_name: hlm-proxy restart: on-failure ports: @@ -60,8 +60,7 @@ services: - RECOVERY_TRIES=1 - SCORE_CAP=.6 - HEAL_ENABLED=true - - SELENIUM_SERVER_URL=http://hlm-selenoid:4444/wd/hub - - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub + - SELENIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub - HEALENIUM_SERVER_URL=http://localhost:7878 - HEALENIUM_SERVICE=http://healenium:7878 - IMITATE_SERVICE=http://selector-imitator:8000 diff --git a/healenium/docker-compose-selenoid.yaml b/healenium/docker-compose-selenoid.yaml index 8dd517c..3cba688 100644 --- a/healenium/docker-compose-selenoid.yaml +++ b/healenium/docker-compose-selenoid.yaml @@ -3,7 +3,7 @@ version: "3.8" services: postgres-db: - image: postgres:11-alpine + image: postgres:15.5-alpine container_name: postgres-db restart: always ports: @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.1 + image: healenium/hlm-backend:3.4.6 container_name: healenium restart: on-failure ports: @@ -42,7 +42,7 @@ services: - healenium selector-imitator: - image: healenium/hlm-selector-imitator:1.2 + image: healenium/hlm-selector-imitator:1.4 container_name: selector-imitator restart: on-failure ports: @@ -51,7 +51,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.5 + image: healenium/hlm-proxy:2.1.1 container_name: hlm-proxy restart: on-failure ports: @@ -61,7 +61,6 @@ services: - SCORE_CAP=.6 - HEAL_ENABLED=true - SELENIUM_SERVER_URL=http://selenoid:4444/wd/hub - - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub - HEALENIUM_SERVER_URL=http://localhost:7878 - HEALENIUM_SERVICE=http://healenium:7878 - IMITATE_SERVICE=http://selector-imitator:8000 diff --git a/healenium/docker-compose-web.yaml b/healenium/docker-compose-web.yaml index 230eb62..12f9291 100644 --- a/healenium/docker-compose-web.yaml +++ b/healenium/docker-compose-web.yaml @@ -3,7 +3,7 @@ version: "3.8" services: postgres-db: - image: postgres:11-alpine + image: postgres:15.5-alpine container_name: postgres-db restart: always ports: @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.1 + image: healenium/hlm-backend:3.4.6 container_name: healenium restart: on-failure ports: @@ -41,7 +41,7 @@ services: - healenium selector-imitator: - image: healenium/hlm-selector-imitator:1.2 + image: healenium/hlm-selector-imitator:1.4 container_name: selector-imitator restart: on-failure ports: diff --git a/healenium/docker-compose.yaml b/healenium/docker-compose.yaml index 3c1a74f..2a8ff27 100644 --- a/healenium/docker-compose.yaml +++ b/healenium/docker-compose.yaml @@ -3,7 +3,7 @@ version: "3.8" services: postgres-db: - image: postgres:11-alpine + image: postgres:15.5-alpine container_name: postgres-db restart: always ports: @@ -18,7 +18,7 @@ services: - healenium healenium: - image: healenium/hlm-backend:3.4.1 + image: healenium/hlm-backend:3.4.6 container_name: healenium restart: on-failure ports: @@ -42,7 +42,7 @@ services: - healenium selector-imitator: - image: healenium/hlm-selector-imitator:1.2 + image: healenium/hlm-selector-imitator:1.4 container_name: selector-imitator restart: on-failure ports: @@ -51,7 +51,7 @@ services: - healenium hlm-proxy: - image: healenium/hlm-proxy:1.3.6 + image: healenium/hlm-proxy:2.1.1 container_name: hlm-proxy restart: on-failure ports: @@ -61,7 +61,6 @@ services: - SCORE_CAP=.6 - HEAL_ENABLED=true - SELENIUM_SERVER_URL=http://selenium-hub:4444/wd/hub - - APPIUM_SERVER_URL=http://host.docker.internal:4723/wd/hub - HEALENIUM_SERVER_URL=http://localhost:7878 - HEALENIUM_SERVICE=http://healenium:7878 - IMITATE_SERVICE=http://selector-imitator:8000 diff --git a/healenium/shell-installation/selenium-grid/download_services.sh b/healenium/shell-installation/selenium-grid/download_services.sh index 81a58f4..90b33c1 100644 --- a/healenium/shell-installation/selenium-grid/download_services.sh +++ b/healenium/shell-installation/selenium-grid/download_services.sh @@ -1,8 +1,8 @@ #!/bin/bash # Versions of the services -HLM_BACKEND_VERSION="3.4.1" -HLM_PROXY_VERSION="1.3.6" +HLM_BACKEND_VERSION="3.4.6" +HLM_PROXY_VERSION="2.1.1" # Downloading curl -L https://github.com/healenium/healenium-backend/releases/download/${HLM_BACKEND_VERSION}/healenium-backend-${HLM_BACKEND_VERSION}.jar > hlm-backend.jar diff --git a/healenium/shell-installation/selenium-grid/start_healenium.sh b/healenium/shell-installation/selenium-grid/start_healenium.sh index 90d8e87..88fe583 100644 --- a/healenium/shell-installation/selenium-grid/start_healenium.sh +++ b/healenium/shell-installation/selenium-grid/start_healenium.sh @@ -14,7 +14,6 @@ RECOVERY_TRIES=1 SCORE_CAP=.6 HEAL_ENABLED=true SELENIUM_SERVER_URL=http://localhost:4444 -APPIUM_SERVER_URL=http://localhost:4723/wd/hub HEALENIUM_SERVER_URL=http://localhost:7878 HEALENIUM_SERVICE=http://localhost:7878 IMITATE_SERVICE=http://localhost:8000 @@ -23,7 +22,7 @@ IMITATE_SERVICE=http://localhost:8000 SPRING_POSTGRES_DB=$HLM_POSTGRES_DB SPRING_POSTGRES_SCHEMA=$HLM_POSTGRES_SCHEMA SPRING_POSTGRES_USER=$HLM_POSTGRES_USER SPRING_POSTGRES_PASSWORD=$HLM_POSTGRES_PASSWORD COLLECT_METRICS=$HLM_COLLECT_METRICS SPRING_SERVER_PORT=$HLM_SERVER_PORT HLM_LOG_LEVEL=$HLM_LOG_LEVEL java -jar hlm-backend.jar 2>&1 & echo $! > ./pid-hlm-backend.file & # Deploy the hlm-proxy service -RECOVERY_TRIES=$RECOVERY_TRIES SCORE_CAP=$SCORE_CAP HEAL_ENABLED=$HEAL_ENABLED SELENIUM_SERVER_URL=$SELENIUM_SERVER_URL APPIUM_SERVER_URL=$APPIUM_SERVER_URL HEALENIUM_SERVER_URL=$HEALENIUM_SERVER_URL HEALENIUM_SERVICE=$HEALENIUM_SERVICE IMITATE_SERVICE=$IMITATE_SERVICE HLM_LOG_LEVEL=$HLM_LOG_LEVEL java -jar hlm-proxy.jar 2>&1 & echo $! > ./pid-hlm-proxy.file & +RECOVERY_TRIES=$RECOVERY_TRIES SCORE_CAP=$SCORE_CAP HEAL_ENABLED=$HEAL_ENABLED SELENIUM_SERVER_URL=$SELENIUM_SERVER_URL HEALENIUM_SERVER_URL=$HEALENIUM_SERVER_URL HEALENIUM_SERVICE=$HEALENIUM_SERVICE IMITATE_SERVICE=$IMITATE_SERVICE HLM_LOG_LEVEL=$HLM_LOG_LEVEL java -jar hlm-proxy.jar 2>&1 & echo $! > ./pid-hlm-proxy.file & # Deploy the imitator service pip install --upgrade pip diff --git a/healenium/shell-installation/web/download_services.sh b/healenium/shell-installation/web/download_services.sh index 811a7d1..a35c6be 100644 --- a/healenium/shell-installation/web/download_services.sh +++ b/healenium/shell-installation/web/download_services.sh @@ -1,7 +1,7 @@ #!/bin/bash # Versions of the services -HLM_BACKEND_VERSION="3.4.1" +HLM_BACKEND_VERSION="3.4.6" # Downloading curl -L https://github.com/healenium/healenium-backend/releases/download/${HLM_BACKEND_VERSION}/healenium-backend-${HLM_BACKEND_VERSION}.jar > hlm-backend.jar