Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

petesong/Add a Python example for the test practice #1924

Open
wants to merge 20 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9e12012
Fix the Hugo installation link
PeteSong Sep 5, 2024
3b5d7f9
Add an example for the test practices
PeteSong Sep 5, 2024
62bc307
Update design_strategies.en.md
PeteSong Sep 5, 2024
df92fe5
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Sep 9, 2024
c8f302c
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Sep 13, 2024
6a24856
Create using_best_practice.py
PeteSong Sep 26, 2024
06cb3e8
Update design_strategies.zh-cn.md
PeteSong Sep 26, 2024
71a5f39
Update design_strategies.ja.md
PeteSong Sep 26, 2024
4fa310f
Update design_strategies.pt-br.md
PeteSong Sep 26, 2024
2e94b42
small update
PeteSong Sep 26, 2024
9704b43
Merge branch 'petesong/add-practice-example' of https://github.com/Pe…
PeteSong Sep 26, 2024
21d942c
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Sep 26, 2024
502afa9
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Sep 27, 2024
449f037
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Oct 8, 2024
4b279f8
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Oct 26, 2024
5bb14ff
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Oct 29, 2024
1c8174a
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Nov 1, 2024
3200342
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Nov 5, 2024
c84a764
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Nov 11, 2024
21c48d8
Merge branch 'trunk' into petesong/add-practice-example
PeteSong Dec 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ to use **[Hugo 0.125.4](https://github.com/gohugoio/hugo/releases/tag/v0.125.4)*

Steps needed to have this working locally and work on it:

- Follow the [Install Hugo](https://www.docsy.dev/docs/get-started/other-options/#install-hugo) instructions from Docsy
- [Install Hugo](https://gohugo.io/installation/) and follow the [Get Started](https://www.docsy.dev/docs/get-started/) instructions from Docsy
- [Install go](https://go.dev/doc/install)
- Clone this repository
- Run `cd website_and_docs`
Expand Down
266 changes: 266 additions & 0 deletions examples/python/tests/design_strategy/using_best_practice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
"""
An example of `python + pytest + selenium`
which implemented "**Action Bot**, **Loadable Component** and **Page Object**".
"""

import pytest
from selenium import webdriver
from selenium.common import (
ElementNotInteractableException,
NoSuchElementException,
StaleElementReferenceException,
)
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


@pytest.fixture(scope="function")
def chrome_driver():
with webdriver.Chrome() as driver:
driver.set_window_size(1024, 768)
driver.implicitly_wait(0.5)
yield driver


class ActionBot:
def __init__(self, driver) -> None:
self.driver = driver
self.wait = WebDriverWait(
driver,
timeout=10,
poll_frequency=2,
ignored_exceptions=[
NoSuchElementException,
StaleElementReferenceException,
ElementNotInteractableException,
],
)

def element(self, locator: tuple) -> WebElement:
self.wait.until(lambda driver: driver.find_element(*locator))
return self.driver.find_element(*locator)

def elements(self, locator: tuple) -> list[WebElement]:
return self.driver.find_elements(*locator)

def hover(self, locator: tuple) -> None:
element = self.element(locator)
ActionChains(self.driver).move_to_element(element).perform()

def click(self, locator: tuple) -> None:
element = self.element(locator)
element.click()

def type(self, locator: tuple, value: str) -> None:
element = self.element(locator)
element.clear()
element.send_keys(value)

def text(self, locator: tuple) -> str:
element = self.element(locator)
return element.text


class LoadableComponent:
def load(self):
raise NotImplementedError("Subclasses must implement this method")

def is_loaded(self):
raise NotImplementedError("Subclasses must implement this method")

def get(self):
if not self.is_loaded():
self.load()
if not self.is_loaded():
raise Exception("Page not loaded properly.")
return self


class TodoPage(LoadableComponent):
url = "https://todomvc.com/examples/react/dist/"

new_todo_by = (By.CSS_SELECTOR, "input.new-todo")
count_todo_left_by = (By.CSS_SELECTOR, "span.todo-count")
todo_items_by = (By.CSS_SELECTOR, "ul.todo-list>li")

view_all_by = (By.LINK_TEXT, "All")
view_active_by = (By.LINK_TEXT, "Active")
view_completed_by = (By.LINK_TEXT, "Completed")

toggle_all_by = (By.CSS_SELECTOR, "input.toggle-all")
clear_completed_by = (By.CSS_SELECTOR, "button.clear-completed")

@staticmethod
def build_todo_by(s: str) -> tuple:
p = f"//li[.//label[contains(text(), '{s}')]]"
return By.XPATH, p

@staticmethod
def build_todo_item_label_by(s: str) -> tuple:
p = f"//label[contains(text(), '{s}')]"
return By.XPATH, p

@staticmethod
def build_todo_item_toggle_by(s: str) -> tuple:
by, using = TodoPage.build_todo_item_label_by(s)
p = f"{using}/../input[@class='toggle']"
return by, p

@staticmethod
def build_todo_item_delete_by(s: str) -> tuple:
by, using = TodoPage.build_todo_item_label_by(s)
p = f"{using}/../button[@class='destroy']"
return by, p

def build_count_todo_left(self, count: int) -> str:
if count == 1:
return "1 item left!"
else:
return f"{count} items left!"

def __init__(self, driver):
self.driver = driver
self.bot = ActionBot(driver)

def load(self):
self.driver.get(self.url)

def is_loaded(self):
try:
WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(self.new_todo_by))
return True
except:
return False

# business domain below
def count_todo_items_left(self) -> str:
return self.bot.text(self.count_todo_left_by)

def todo_count(self) -> int:
return len(self.bot.elements(self.todo_items_by))

def new_todo(self, s: str):
self.bot.type(self.new_todo_by, s + "\n")

def toggle_todo(self, s: str):
self.bot.click(self.build_todo_item_toggle_by(s))

def hover_todo(self, s: str) -> None:
self.bot.hover(self.build_todo_by(s))

def delete_todo(self, s: str):
self.hover_todo(s)
self.bot.click(self.build_todo_item_delete_by(s))

def clear_completed_todo(self):
self.bot.click(self.clear_completed_by)

def toggle_all_todo(self):
self.bot.click(self.toggle_all_by)

def view_all_todo(self):
self.bot.click(self.view_all_by)

def view_active_todo(self):
self.bot.click(self.view_active_by)

def view_completed_todo(self):
self.bot.click(self.view_completed_by)


@pytest.fixture
def page(chrome_driver) -> TodoPage:
driver = chrome_driver
return TodoPage(driver).get()


class TestTodoPage:
def test_new_todo(self, page: TodoPage):
assert page.todo_count() == 0
page.new_todo("aaa")
assert page.count_todo_items_left() == page.build_count_todo_left(1)

def test_todo_toggle(self, page: TodoPage):
s = "aaa"
page.new_todo(s)
assert page.count_todo_items_left() == page.build_count_todo_left(1)

page.toggle_todo(s)
assert page.count_todo_items_left() == page.build_count_todo_left(0)

page.toggle_todo(s)
assert page.count_todo_items_left() == page.build_count_todo_left(1)

def test_todo_delete(self, page: TodoPage):
s1 = "aaa"
s2 = "bbb"
page.new_todo(s1)
page.new_todo(s2)
assert page.count_todo_items_left() == page.build_count_todo_left(2)

page.delete_todo(s1)
assert page.count_todo_items_left() == page.build_count_todo_left(1)

page.delete_todo(s2)
assert page.todo_count() == 0

def test_new_100_todo(self, page: TodoPage):
for i in range(100):
s = f"ToDo{i}"
page.new_todo(s)
assert page.count_todo_items_left() == page.build_count_todo_left(100)

def test_toggle_all_todo(self, page: TodoPage):
for i in range(10):
s = f"ToDo{i}"
page.new_todo(s)
assert page.count_todo_items_left() == page.build_count_todo_left(10)
assert page.todo_count() == 10

page.toggle_all_todo()
assert page.count_todo_items_left() == page.build_count_todo_left(0)
assert page.todo_count() == 10

page.toggle_all_todo()
assert page.count_todo_items_left() == page.build_count_todo_left(10)
assert page.todo_count() == 10

def test_clear_completed_todo(self, page: TodoPage):
for i in range(10):
s = f"ToDo{i}"
page.new_todo(s)
assert page.count_todo_items_left() == page.build_count_todo_left(10)
assert page.todo_count() == 10

for i in range(5):
s = f"ToDo{i}"
page.toggle_todo(s)
assert page.count_todo_items_left() == page.build_count_todo_left(5)
assert page.todo_count() == 10

page.clear_completed_todo()
assert page.count_todo_items_left() == page.build_count_todo_left(5)
assert page.todo_count() == 5

def test_view_todo(self, page: TodoPage):
for i in range(10):
s = f"ToDo{i}"
page.new_todo(s)
for i in range(4):
s = f"ToDo{i}"
page.toggle_todo(s)

page.view_all_todo()
assert page.count_todo_items_left() == page.build_count_todo_left(6)
assert page.todo_count() == 10

page.view_active_todo()
assert page.count_todo_items_left() == page.build_count_todo_left(6)
assert page.todo_count() == 6

page.view_completed_todo()
assert page.count_todo_items_left() == page.build_count_todo_left(6)
assert page.todo_count() == 4
Loading