Repository: ElSnoMan/pyleniumio
Branch: main
Commit: b82f9301f95e
Files: 139
Total size: 340.2 KB
Directory structure:
gitextract_fn7bqonu/
├── .flake8
├── .gitbook.yaml
├── .github/
│ └── workflows/
│ ├── publish-pylenium.yml
│ └── test-pylenium.yml
├── .gitignore
├── .gitpod.Dockerfile
├── .gitpod.yml
├── .vscode/
│ └── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── conftest.py
├── docker-compose.yml
├── docs/
│ ├── README.md
│ ├── SUMMARY.md
│ ├── changelog.md
│ ├── cli/
│ │ ├── pylenium-cli.md
│ │ └── report-portal.md
│ ├── configuration/
│ │ ├── driver.md
│ │ ├── logging.md
│ │ ├── pylenium.json.md
│ │ └── report-portal.md
│ ├── contribute/
│ │ └── clone-and-setup-the-project.md
│ ├── element-commands/
│ │ ├── check.md
│ │ ├── children.md
│ │ ├── click.md
│ │ ├── css_value.md
│ │ ├── deselect.md
│ │ ├── double_click.md
│ │ ├── drag_to.md
│ │ ├── drag_to_element.md
│ │ ├── first.md
│ │ ├── get_attribute.md
│ │ ├── get_property.md
│ │ ├── hover.md
│ │ ├── is_checked.md
│ │ ├── is_displayed.md
│ │ ├── is_empty.md
│ │ ├── is_enabled.md
│ │ ├── is_selected.md
│ │ ├── last.md
│ │ ├── length.md
│ │ ├── open_shadow_dom.md
│ │ ├── parent.md
│ │ ├── right_click.md
│ │ ├── screenshot.md
│ │ ├── scroll_into_view.md
│ │ ├── select.md
│ │ ├── select_many.md
│ │ ├── should.md
│ │ ├── siblings.md
│ │ ├── submit.md
│ │ ├── tag_name.md
│ │ ├── text.md
│ │ ├── type.md
│ │ ├── uncheck.md
│ │ └── webelement.md
│ ├── examples/
│ │ └── test_sample.py
│ ├── fixtures/
│ │ ├── axe.md
│ │ ├── fake.md
│ │ └── requests.md
│ ├── getting-started/
│ │ ├── project-structure-with-pytest.md
│ │ ├── setup-pytest.md
│ │ ├── virtual-environments.md
│ │ └── writing-tests-with-pylenium.md
│ ├── guides/
│ │ ├── run-tests-in-containers.md
│ │ └── run-tests-in-parallel.md
│ ├── misc/
│ │ └── install-chromedriver.md
│ └── pylenium-commands/
│ ├── commands.md
│ ├── contains.md
│ ├── delete_all_cookies.md
│ ├── delete_cookie.md
│ ├── execute_script.md
│ ├── fake.md
│ ├── find.md
│ ├── find_xpath.md
│ ├── get.md
│ ├── get_cookie.md
│ ├── get_cookies.md
│ ├── get_xpath.md
│ ├── go.md
│ ├── maximize_window.md
│ ├── quit.md
│ ├── request.md
│ ├── screenshot.md
│ ├── scroll_to.md
│ ├── set_cookie.md
│ ├── should.md
│ ├── switch_to.default_content.md
│ ├── switch_to.frame.md
│ ├── switch_to.parent_frame.md
│ ├── switch_to.window.md
│ ├── title.md
│ ├── url.md
│ ├── viewport.md
│ ├── visit.md
│ ├── wait.md
│ ├── webdriver.md
│ ├── window_handles.md
│ ├── window_size.md
│ └── xpath.md
├── poetry.toml
├── pylenium/
│ ├── __init__.py
│ ├── a11y.py
│ ├── cdp.py
│ ├── config.py
│ ├── driver.py
│ ├── element.py
│ ├── jquery.py
│ ├── log.py
│ ├── performance.py
│ ├── scripts/
│ │ ├── __init__.py
│ │ ├── allure_reporting.py
│ │ ├── cli.py
│ │ ├── cli_utils.py
│ │ ├── conftest.py
│ │ ├── drag_and_drop.js
│ │ ├── load_jquery.js
│ │ ├── pylenium.json
│ │ └── pytest.ini
│ ├── switch_to.py
│ ├── utils.py
│ ├── wait.py
│ └── webdriver_factory.py
├── pylenium.json
├── pyproject.toml
├── pytest.ini
└── tests/
├── __init__.py
├── performance/
│ ├── test_cdp_performance.py
│ └── test_performance.py
├── test_flows.py
├── ui/
│ ├── Get CRX.crx
│ ├── test_element.py
│ ├── test_element_actions.py
│ └── test_pydriver.py
└── unit/
├── test_config.py
├── test_faker.py
└── test_requests.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .flake8
================================================
[flake8]
extend-ignore = E203, C901
exclude =
.git,
__pycache__,
docs,
old,
dist
max-line-length = 160
================================================
FILE: .gitbook.yaml
================================================
root: ./docs
structure:
readme: README.md
summary: SUMMARY.md
================================================
FILE: .github/workflows/publish-pylenium.yml
================================================
name: Upload Pylenium Package to pypi
on:
release:
types: [created]
workflow_dispatch:
inputs:
version: # keeping this for eventaul autobump feature
description: "The version to publish to pypi"
required: false
default: "develop"
jobs:
pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.x"
- name: Build distributions
run: |
pip install poetry
poetry build
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
================================================
FILE: .github/workflows/test-pylenium.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Build and Test Pylenium with Selenoid
on:
push:
branches: [main]
paths-ignore:
- "docs/**"
pull_request:
branches: [main]
paths-ignore:
- "docs/**"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
pip install poetry
# install dependencies from pyproject.toml
poetry install
- name: Lint with flake8
run: |
# Using .flake8 file
poetry run poe lint
- name: Run Unit Tests
run: |
poetry run pytest tests/unit
- name: Start Selenoid Server
# https://github.com/marketplace/actions/start-selenoid-server?version=v2
uses: Xotabu4/selenoid-github-action@v2
with:
selenoid-start-arguments: |
--args "-timeout 250s"
- name: Run UI Tests
run: |
poetry run pytest tests/ui --remote_url "http://localhost:4444/wd/hub"
================================================
FILE: .gitignore
================================================
# General
.idea
test_results
allure-report
test_env
tests/data
data
# LambdaTest
.hyperexecute
logs
.updatedhyperexecute.yaml
hyperexecute-code*
hyperexecuteupdate
# 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/
pip-wheel-metadata/
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/
# 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
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.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
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__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/
Pipfile.lock
.pypyrc
a11y.json
================================================
FILE: .gitpod.Dockerfile
================================================
FROM gitpod/workspace-full-vnc
USER root
# RUN apt-get update -qqy && apt-get install -y wget curl gnupg2
# So we can install browsers and browser drivers later
RUN wget https://packages.microsoft.com/config/ubuntu/21.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
&& dpkg -i packages-microsoft-prod.deb && rm packages-microsoft-prod.deb
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list
RUN mkdir -p /home/gitpod/selenium /var/run/supervisor /var/log/supervisor && \
chmod -R 777 /var/run/supervisor /var/log/supervisor
ENV DEBIAN_FRONTEND=noninteractive
# Browsers
RUN apt-get update -qqy && \
apt-get -qy install google-chrome-stable firefox && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# Browser Drivers
RUN CHROME_MAJOR_VERSION=$(google-chrome --version | sed -E "s/.* ([0-9]+)(\.[0-9]+){3}.*/\1/") \
&& CHROME_DRIVER_VERSION=$(wget --no-verbose -O - "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_${CHROME_MAJOR_VERSION}") \
&& echo "Using ChromeDriver version: "$CHROME_DRIVER_VERSION \
&& wget --no-verbose -O /tmp/chromedriver_linux64.zip https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip \
&& rm -rf /home/gitpod/selenium/chromedriver \
&& unzip /tmp/chromedriver_linux64.zip -d /home/gitpod/selenium \
&& rm /tmp/chromedriver_linux64.zip \
&& mv /home/gitpod/selenium/chromedriver /home/gitpod/selenium/chromedriver-$CHROME_DRIVER_VERSION \
&& chmod 755 /home/gitpod/selenium/chromedriver-$CHROME_DRIVER_VERSION \
&& sudo ln -fs /home/gitpod/selenium/chromedriver-$CHROME_DRIVER_VERSION /usr/bin/chromedriver
RUN GK_VERSION="0.31.0" \
&& echo "Using GeckoDriver version: "$GK_VERSION \
&& wget --no-verbose -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GK_VERSION/geckodriver-v$GK_VERSION-linux64.tar.gz \
&& rm -rf /home/gitpod/selenium/geckodriver \
&& tar -C /home/gitpod/selenium -zxf /tmp/geckodriver.tar.gz \
&& rm /tmp/geckodriver.tar.gz \
&& mv /home/gitpod/selenium/geckodriver /home/gitpod/selenium/geckodriver-$GK_VERSION \
&& chmod 755 /home/gitpod/selenium/geckodriver-$GK_VERSION \
&& ln -fs /home/gitpod/selenium/geckodriver-$GK_VERSION /usr/bin/geckodriver
# To run browser tests
ENV DISPLAY :99.0
ENV DISPLAY_NUM 99
ENV SCREEN_WIDTH 1360
ENV SCREEN_HEIGHT 1020
ENV SCREEN_DEPTH 24
ENV SCREEN_DPI 96
ENV VNC_PORT 5900
ENV NO_VNC_PORT 7900
================================================
FILE: .gitpod.yml
================================================
vscode:
extensions:
- ms-python.python
- bungcip.better-toml
- PKief.material-icon-theme
image:
file: .gitpod.Dockerfile
tasks:
- name: "Setup"
command: |
git config --global pull.rebase false
pip install poetry
poetry install
ports:
- port: 5900
onOpen: ignore
- port: 6080
onOpen: open-browser
- port: 10000
onOpen: ignore
================================================
FILE: .vscode/settings.json
================================================
{
"python.testing.pytestArgs": ["tests"],
"python.defaultInterpreterPath": ".venv/bin/python",
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
"**/*.crswap": true,
"**/*.pytest_cache": true,
"**/*__pycache__": true
},
"python.linting.enabled": true,
"flake8.args": ["--rcfile=.flake8"],
"python.formatting.provider": "none",
"workbench.iconTheme": "material-icon-theme",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [contributor-covenant.org/version/2/0/code_of_conduct/][version]
[homepage]: https://contributor-covenant.org
[version]: https://www.contributor-covenant.org/version/2/0/code_of_conduct/
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Pylenium
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
The following is a set of guidelines for contributing to Pylenium on GitHub. These are mostly guidelines, not rules.
Use your best judgment, and feel free to propose changes to this document in a pull request.
#### Table of Contents
* [Code of Conduct](./CODE_OF_CONDUCT.md)
* [What should I know before getting started?](#what-should-i-know-before-getting-started)
* [How can I Contribute?](#how-can-i-contribute)
* [Setup the Project](#setup-the-project-to-start)
* [Report a Bug](#report-a-bug)
* [Before submitting a Bug](#before-submitting-a-bug-report)
* [How do I submit a (good) Bug](#how-do-i-submit-a-good-bug-report)
* [Suggesting Enhancements](#suggesting-enhancements)
* [Before submitting an Enhancement](#before-submitting-an-enhancement-suggestion)
* [How do I submit a (good) Enhancement?](#how-do-i-submit-a-good-enhancement-suggestion)
* [Your first Code Contribution](#your-first-code-contribution)
* [Pull Requests (PR)](#pull-requests)
* [PR Templates](#pr-templates)
* [Requirements for contributing a Bugfix](#requirements-for-contributing-a-bug-fix)
* [Bug Template](#bug-template)
* [Requirements for contributing Documentation](#requirements-for-contributing-documentation)
* [Documentation Template](#documentation-template)
* [Requirements for contributing adding, changing, or removing a Feature](#requirements-for-adding-changing-or-removing-a-feature)
* [Enhancement Template](#enhancement-template)
* [Style Guides](#style-guides)
* [Git Commit Messages](#git-commit-messages)
* [Python Styleguide](#python-styleguide)
## Code of Conduct
This project and everyone participating in it is governed by the [Pylenium Code of Conduct](CODE_OF_CONDUCT.md).
By participating, you are expected to uphold this code. Please report unacceptable behavior to Carlos Kidman (@ElSnoMan).
## What should I know before getting started?
Pylenium is a wrapper of Selenium, not Cypress.
However, like Cypress, we want Pylenium to be an all-in-one solution that comes with everything you need and expect from a robust Test Automation Solution.
Most features should be "plug and play" for the user to make it easy for them, but we also need to keep it extendable
so they can customize or swap components, like a reporting tool, if they want to. However, some components cannot be changed.
For example:
* Selenium (for obvious reasons)
* pytest as the Testing Framework
## How can I Contribute?
### Setup the Project to Start
This [Guide in our Documentation][clone and setup]
gives you the steps to clone and setup the project.
From there, open the [Issues Tab][Issues Tab] and check out the [Pull Requests Guide](#pull-requests)!
### Report a Bug
This section guides you through submitting a bug report for Pylenium. Following these guidelines helps maintainers
and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:.
When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report).
Following this and providing the information it asks for helps us resolve issues faster.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
#### Before Submitting A Bug Report
* **Perform a cursory search** to see if the problem has already been reported.
If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Bug Report?
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/).
Create an issue on this repository and explain the problem and include additional details to help maintainers reproduce the problem:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started Pylenium or ran a test(s), e.g. which command exactly you used in the terminal. When listing steps, **don't just say what you did, but explain how you did it**. For example, running tests via the IDE's UI behaves differently than the Terminal.
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps, **record the GIF with the [Keybinding Resolver](https://github.com/atom/keybinding-resolver) shown**. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
* **If you're reporting that Pylenium crashed, quits, or doesn't run tests**, include a stack trace and the Project Structure in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist.
Provide more context by answering these questions:
* **Can you reproduce the problem in the Terminal?** Sometimes Test issues are IDE-specific and not necessarily Selenium or Pytest.
* **Did the problem start happening recently** (e.g. after updating to a new version of Pylenium) or was this always a problem?
* If the problem started happening recently, **can you reproduce the problem in an older version of Pylenium?** What's the most recent version in which the problem doesn't happen?
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of Pylenium are you using?** You can get the exact version by running `pylenium --version` in your terminal.
* **What's the name and version of the OS you're using**?
* **Are you running Tests in locally vs the Cloud?** If so, which tools or software are you using and which operating systems and versions are used for the host and the guest?
* **Have you checked the Browser, Drivers, or Selenium about WebDriver issues?** Remember, Pylenium is a wrapper of Selenium, so something like Drag and Drop not working may be a WebDriver or Browser issue.
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for Pylenium, including completely new features and minor improvements to existing functionality or documentation.
Following these guidelines helps maintainers and the community understand your suggestion :pencil: and find related suggestions :mag_right:.
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one.
When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion)
and the steps that you imagine you would take if the feature you're requesting existed.
#### Before Submitting An Enhancement Suggestion
* **Check the [Pylenium Docs](https://elsnoman.gitbook.io/pylenium/)** for tips and commands — you might discover that the enhancement is already available. Most importantly, check if you're using the latest version of Pylenium.
* **Perform a cursory search in the Issues Tab** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Enhancement Suggestion?
Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue on this repository and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of Pylenium which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
* **Explain why this enhancement would be useful** to most Pylenium users and isn't something that can or should be implemented on your own or on another package, like a pytest plugin.
* **List some other automation tools or applications where this enhancement exists.**
* **Specify which version of Pylenium you're using.** You can get the exact version by running `pylenium --version` in your terminal.
* **Specify the name and version of the OS you're using.**
### Your First Code Contribution
Unsure where to begin contributing to Pylenium? You can start by looking through `beginner` and `help-wanted` issues:
* Beginner issues - Labeled with `beginner`, these issues should only require a few lines of code, and a test or two.
* Help wanted issues - Labeled with `help wanted`, these issues should be a bit more involved than `beginner` issues.
## Pull Requests
The process described here has several goals:
- Maintain Pylenium's quality or improve it
- Fix problems that are important to users
- Engage the community in working toward the best possible Pylenium
- Enable a sustainable system for Pylenium's maintainers to review contributions
Please follow these steps to have your contribution considered by the maintainers:
1. Follow all instructions in [the template](#pr-templates)
2. Follow the [style guides](#style-guides)
3. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing What if the status checks are failing?If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.
While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted.
## PR Templates
### Requirements for Contributing a Bug Fix
* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
* The pull request must only fix an existing bug. To contribute other changes, you must use a different template.
* The pull request must update the test suite to demonstrate the changed functionality.
* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution.
#### Bug Template
1. Identify the Bug
> Link to the issue describing the bug that you're fixing.
If there is not yet an issue for your bug, please open a new issue and then link to that issue in your pull request.
Note: In some cases, one person's "bug" is another person's "feature." If the pull request does not address an existing issue with the "bug" label, the maintainers have the final say on whether the current behavior is a bug.
2. Description of the Change
> We must be able to understand the design of your change from this description.
If we can't get a good idea of what the code will be doing from the description here, the pull request may be closed at the maintainers' discretion.
Keep in mind that the maintainer reviewing this PR may not be familiar with or have worked with the code here recently, so please walk us through the concepts.
3. Alternate Designs
> Explain what other alternates were considered and why the proposed version was selected
4. Possible Drawbacks
> What are the possible side-effects or negative impacts of the code change?
5. Verification Process
> What process did you follow to verify that the change has not introduced any regressions?
Describe the actions you performed (including buttons you clicked, text you typed, commands you ran, etc.),
and describe the results you observed.
6. Release Notes
> Please describe the changes in a single line that explains this improvement in
terms that a user can understand. This text will be used in Pylenium's release notes.
If this change is not user-facing or notable enough to be included in release notes you may use the strings "Not applicable" or "N/A" here.
Examples:
- The GitHub package now allows you to add co-authors to commits.
- Fixed an issue where multiple cursors did not work in a file with a single line.
- Increased the performance of searching and replacing across a whole project.
### Requirements for Contributing Documentation
* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
* The pull request must only contribute documentation (for example, markdown files or API docs). To contribute other changes, you must use a different template.
#### Documentation Template
1. Description of the Change
> We must be able to understand the purpose of your change from this description.
If we can't get a good idea of the benefits of the change from the description here, the pull request may be closed at the maintainers' discretion.
2. Release Notes
> Please describe the changes in a single line that explains this improvement in
terms that a user can understand. This text will be used in Pylenium's release notes.
If this change is not user-facing or notable enough to be included in release notes
you may use the strings "Not applicable" or "N/A" here.
Examples:
- The GitHub package now allows you to add co-authors to commits.
- Fixed an issue where multiple cursors did not work in a file with a single line.
- Increased the performance of searching and replacing across a whole project.
### Requirements for Adding, Changing, or Removing a Feature
* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
* The pull request must contribute a change that has been endorsed by the maintainer team. See details in the template below.
* The pull request must update the test suite to exercise the updated functionality.
* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution.
#### Enhancement Template
1. Issue or RFC Endorsed by Pylenium's Maintainers
> Link to the issue or RFC that your change relates to.
To contribute an enhancement that isn't endorsed, please follow our guide for Suggesting Enhancements.
To contribute other changes, you must use a different template.
2. Description of the Change
> We must be able to understand the design of your change from this description.
If we can't get a good idea of what the code will be doing from the description here, the pull request may be closed at the maintainers' discretion.
Keep in mind that the maintainer reviewing this PR may not be familiar with or have worked with the code here recently, so please walk us through the concepts.
3. Alternate Designs
> Explain what other alternates were considered and why the proposed version was selected.
4. Possible Drawbacks
> What are the possible side-effects or negative impacts of the code change?
5. Verification Process
> What process did you follow to verify that your change has the desired effects?
- How did you verify that all new functionality works as expected?
- How did you verify that all changed functionality works as expected?
- How did you verify that the change has not introduced any regressions?
Describe the actions you performed (including buttons you clicked, text you typed, commands you ran, etc.), and describe the results you observed.
6. Release Notes
> Please describe the changes in a single line that explains this improvement in
terms that a user can understand. This text will be used in Atom's release notes.
If this change is not user-facing or notable enough to be included in release notes
you may use the strings "Not applicable" or "N/A" here.
Examples:
- The GitHub package now allows you to add co-authors to commits.
- Fixed an issue where multiple cursors did not work in a file with a single line.
- Increased the performance of searching and replacing across a whole project.
### Style Guides
#### Git Commit Messages
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
* Limit the first line to 72 characters or less
* Reference issues and pull requests liberally after the first line
Consider starting the commit message with an applicable emoji:
* 🎨 :art: when improving the format/structure of the code
* 🐎 :racehorse: when improving performance
* 🚱 :non-potable_water: when plugging memory leaks
* 📝 :memo: when writing docs
* 🐧 :penguin: when fixing something on Linux
* 🍎 :apple: when fixing something on macOS
* 🏁 :checkered_flag: when fixing something on Windows
* 🐛 :bug: when fixing a bug
* 🔥 :fire: when removing code or files
* 💚 :green_heart: when fixing the CI build
* ✅ :white_check_mark: when adding tests
* 🔒 :lock: when dealing with security
* ⬆ :arrow_up: when upgrading dependencies
* ⬇ :arrow_down: when downgrading dependencies
* 👕 :shirt: when removing linter warnings
#### Python Styleguide
`pep8` is the styleguide and `flake8` is the linter in CI.
* Indents are 4 spaces (turn your tabs to spaces!)
* Type Hint as much as possible, but it should make sense
* Docstrings are _valuable_ because Pylenium is a library. Help the user stay in code rather than looking up docs all the time
* Docstrings follow the Google format
```python
def first() -> Element:
""" Get the first element from this list. """
return _elements[0]
# --- over ---
def first():
return _elements[0]
```
[clone and setup]: https://elsnoman.gitbook.io/pylenium/contribute/clone-and-setup-the-project
[Issues Tab]: https://github.com/ElSnoMan/pyleniumio/issues
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Carlos Kidman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Pylenium: Easy Python Web Test Automation
- [The Mission](#the-mission-is-simple)
- [Test Example](#test-example)
- [Purpose](#purpose)
- [Quickstart](#quick-start)
- [1. Install](#1-install-pyleniumio)
- [2. Initialize](#2-initialize-pylenium)
- [3. Write a Test](#3-write-a-test)
- [4. Run the Test](#4-run-the-test)
- [Contribute](#contribute)
## The mission is simple
> Bring the best of Selenium, Cypress and Python into one package.
This means:
* Automatic waiting and synchronization
* Quick setup to start writing tests
* Easy to use and clean syntax for amazing readability and maintainability
* Automatic driver installation so you don't need to manage drivers
* Leverage the awesome Python language
* and more!
### Test Example
Although Pylenium is a thin wrapper of Selenium, let's use this simple scenario to show the difference between using `Selenium` and `Pylenium`:
1. **Visit** the QA at the Point website: [https://qap.dev](https://qap.dev/)
2. **Hover** the About link to reveal a menu
3. **Click** the Leadership link in that menu
4. **Assert** Carlos Kidman is on the Leadership page
```python
def test_carlos_is_on_leadership(py):
py.visit('https://qap.dev')
py.get('a[href="/about"]').hover()
py.get('a[href="/leadership"][class^="Header-nav"]').click()
assert py.contains('Carlos Kidman')
```
```python
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
# define your setup and teardown fixture
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_carlos_is_on_leadership_page_with_selenium(driver):
wait = WebDriverWait(driver, timeout=10)
driver.get('https://qap.dev')
# hover About link
about_link = driver.find_element(By.CSS_SELECTOR, "a[href='/about']")
actions = ActionChains(driver)
actions.move_to_element(about_link).perform()
# click Leadership link in About menu
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "a[href='/leadership'][class^='Header-nav']"))).click()
# check if 'Carlos Kidman' is on the page
assert wait.until(lambda _: driver.find_element(By.XPATH, "//*[contains(text(), 'Carlos Kidman')]"))
```
### Purpose
I teach courses and do trainings for both **Selenium** and **Cypress**, but Selenium, out of the box, _feels_ clunky. When you start at a new place, you almost always need to "setup" the framework from scratch all over again. Instead of getting right to creating meaningful tests, you end up spending most of your time building a custom framework, maintaining it, and having to teach others to use it.
Also, many people blame Selenium for bad or flaky tests. This usually tells me that they have yet to experience someone that truly knows how to make Selenium amazing! This also tells me that they are not aware of the usual root causes that make Test Automation fail:
* Poor programming skills, test design and practices
* Flaky applications
* Complex frameworks
What if we tried to get the best from both worlds and combine it with an amazing language?
**Selenium** has done an amazing job of providing W3C bindings to many languages and makes scaling a breeze.
**Cypress** has done an amazing job of making the testing experience more enjoyable - especially for beginners.
**Pylenium** looks to bring more Cypress-like bindings and techniques to Selenium \(like automatic waits\) and still leverage Selenium's power along with the ease-of-use and power of **Python**.
## Quick Start
The [Official Pylenium Docs](https://elsnoman.gitbook.io/pylenium) are the best place to start, but you can quickly get going with the following steps:
### 1. Install **pyleniumio**
```python
pip install pyleniumio
---or---
pipenv install pyleniumio
---or---
poetry add pyleniumio
```
### 2. Initialize Pylenium
```bash
# execute at your Project Root
pylenium init
```
This creates three files:
* `conftest.py` - This has the fixtures needed for Pylenium
* `pylenium.json` - This is the config file for Pylenium
* `pytest.ini` - This is the config file for pytest
By default, Pylenium uses the Chrome browser. You have to install Chrome or update the `pylenium.json` file to use the browser of your choice.
### 3. Write a test
Create a directory called `tests` and then a test file called `test_google.py`
Define a new test called `test_google_search`
```python
def test_google_search(py)
```
Pylenium uses **pytest** as the Test Framework. You only need to pass in `py`to the function!
Now we can use **Pylenium Commands** to interact with the browser.
```python
def test_google_search(py):
py.visit('https://google.com')
py.get("[name='q']").type('puppies')
py.get("[name='btnK']").submit()
assert py.should().contain_title('puppies')
```
### 4. Run the Test
This will depend on your IDE, but you can always run tests from the CLI:
```bash
python -m pytest tests/test_google.py
```
You're all set! You should see the browser open and complete the commands we had in the test :\)
## Contribute
Pylenium uses [Gitpod](https://gitpod.io/) to make it easy to work on it from the _desktop or browser_ without having to worry about the setup like having the correct Python version installed after cloning the repo.
> 💡 With a single click, you can open the repo in your browser, make your changes, then submit a pull request!
0. If you're new to Gitpod, check out their [Getting Started](https://www.gitpod.io/docs/introduction/getting-started) docs to see how to use it
1. Visit [Pylenium's repo](https://github.com/ElSnoMan/pyleniumio) and click the `Gitpod` button to open the repo in a VS Code browser window
2. Wait for Gitpod to set up the project. You'll see things get setup from the various `.gitpod*` files at the Project Root
3. Once complete, create a new branch and start making your changes
4. When ready, submit a Pull Request!
5. Reviewers will see your CI pipeline and even be able to open your Gitpod instance if needed - making collaboration much easier
6. Gitpod instances are ephemeral, so you can create, share, and delete them as needed
> 🧪 By default, UI tests executed in Gitpod are headless. If you'd like to see UI tests run, open port `6080` from the bottom right corner of VS Code.
For more details and other ways to contribute to Pylenium, visit the [CONTRIBUTING.md](/CONTRIBUTING.md) doc 👀
================================================
FILE: conftest.py
================================================
"""
The `conftest.py` and `pylenium.json` files generated by Pylenium should stay at your Workspace Root (aka Project Root).
conftest.py
Although this file is editable, you should only change its contents if you know what you are doing.
Instead, you can create your own conftest.py file in the folder where you store your tests.
pylenium.json
You can change the values, but DO NOT touch the keys or you will break the schema.
py
The main fixture you need from this is `py`. This is the instance of Pylenium for each test.
Just pass py into your test and you're ready to go!
Examples:
def test_go_to_google(py):
py.visit('https://google.com')
assert 'Google' in py.title()
"""
import copy
import json
import logging
import shutil
from pathlib import Path
import allure
import pytest
import requests
from faker import Faker
from pylenium.a11y import PyleniumAxe
from pylenium.config import PyleniumConfig, TestCase
from pylenium.driver import Pylenium
@pytest.fixture(scope="function")
def fake() -> Faker:
"""A basic instance of Faker to make test data."""
return Faker()
@pytest.fixture(scope="function")
def api():
"""A basic instance of Requests to make HTTP API calls."""
return requests
@pytest.fixture(scope="session", autouse=True)
def project_root() -> Path:
"""The Project (or Workspace) root as a filepath.
* This conftest.py file should be in the Project Root if not already.
"""
return Path(__file__).absolute().parent
@pytest.fixture(scope="session", autouse=True)
def test_results_dir(project_root: Path, request) -> Path:
"""Creates the `/test_results` directory to store the results of the Test Run.
Returns:
The `/test_results` directory as a filepath (str).
"""
session = request.node
test_results_dir = project_root.joinpath("test_results")
if test_results_dir.exists():
# delete /test_results from previous Test Run
shutil.rmtree(test_results_dir, ignore_errors=True)
try:
# race condition can occur between checking file existence and
# creating the file when using pytest with multiple workers
test_results_dir.mkdir(parents=True, exist_ok=True)
except FileExistsError:
pass
for test in session.items:
try:
# make the test_result directory for each test
test_results_dir.joinpath(test.name).mkdir(parents=True, exist_ok=True)
except FileExistsError:
pass
return test_results_dir
@pytest.fixture(scope="session")
def _load_pylenium_json(project_root, request) -> PyleniumConfig:
"""Load the default pylenium.json file or the given pylenium.json config file (if specified).
* Pylenium looks for these files from the Project Root!
I may have multiple pylenium.json files with different presets. For example:
- stage-pylenium.json
- dev-testing.json
- firefox-pylenium.json
Examples
--------
$ pytest
>>> Loads the default file: PROJECT_ROOT/pylenium.json
$ pytest pylenium_json=dev-pylenium.json
>>> Loads the config file: PROJECT_ROOT/dev-pylenium.json
$ pytest pylenium_json="configs/stage-pylenium.json"
>>> Loads the config file: PROJECT_ROOT/configs/stage-pylenium.json
"""
custom_config_filepath = request.config.getoption("pylenium_json")
config_filepath = project_root.joinpath(custom_config_filepath or "pylenium.json")
try:
with config_filepath.open() as file:
_json = json.load(file)
config = PyleniumConfig(**_json)
except FileNotFoundError:
logging.warning(f"The config_filepath was not found, so PyleniumConfig will load with default values. File not found: {config_filepath.absolute()}")
config = PyleniumConfig()
return config
@pytest.fixture(scope="session")
def _override_pylenium_config_values(_load_pylenium_json: PyleniumConfig, request) -> PyleniumConfig:
"""Override any PyleniumConfig values after loading the initial pylenium.json config file.
After a pylenium.json config file is loaded and converted to a PyleniumConfig object,
then any CLI arguments override their respective key/values.
"""
config = _load_pylenium_json
# Driver Settings
cli_remote_url = request.config.getoption("--remote_url")
if cli_remote_url:
config.driver.remote_url = cli_remote_url
cli_browser_options = request.config.getoption("--options")
if cli_browser_options:
config.driver.options = [option.strip() for option in cli_browser_options.split(",")]
cli_browser = request.config.getoption("--browser")
if cli_browser:
config.driver.browser = cli_browser
cli_local_path = request.config.getoption("--local_path")
if cli_local_path:
config.driver.local_path = cli_local_path
cli_capabilities = request.config.getoption("--caps")
if cli_capabilities:
# --caps must be in '{"name": "value", "boolean": true}' format
# with double quotes around each key. booleans are lowercase.
config.driver.capabilities = json.loads(cli_capabilities)
cli_page_wait_time = request.config.getoption("--page_load_wait_time")
if cli_page_wait_time and cli_page_wait_time.isdigit():
config.driver.page_load_wait_time = int(cli_page_wait_time)
# Logging Settings
cli_screenshots_on = request.config.getoption("--screenshots_on")
if cli_screenshots_on:
shots_on = cli_screenshots_on.lower() == "true"
config.logging.screenshots_on = shots_on
cli_extensions = request.config.getoption("--extensions")
if cli_extensions:
config.driver.extension_paths = [ext.strip() for ext in cli_extensions.split(",")]
cli_log_level = request.config.getoption("--pylog_level")
if cli_log_level:
level = cli_log_level.upper()
config.logging.pylog_level = level if level in ["DEBUG", "COMMAND", "INFO", "USER", "WARNING", "ERROR", "CRITICAL"] else "INFO"
return config
@pytest.fixture(scope="function")
def py_config(_override_pylenium_config_values) -> PyleniumConfig:
"""Get a fresh copy of the PyleniumConfig for each test
See _load_pylenium_json and _override_pylenium_config_values for how the initial configuration is read.
"""
return copy.deepcopy(_override_pylenium_config_values)
@pytest.fixture(scope="class")
def pyc_config(_override_pylenium_config_values) -> PyleniumConfig:
"""Get a fresh copy of the PyleniumConfig for each test class"""
return copy.deepcopy(_override_pylenium_config_values)
@pytest.fixture(scope="session")
def pys_config(_override_pylenium_config_values) -> PyleniumConfig:
"""Get a fresh copy of the PyleniumConfig for each test session"""
return copy.deepcopy(_override_pylenium_config_values)
@pytest.fixture(scope="function")
def test_case(test_results_dir: Path, request) -> TestCase:
"""Manages data pertaining to the currently running Test Function or Case.
* Creates the test-specific logger.
Args:
test_results_dir: The ./test_results directory this Test Run (aka Session) is writing to
Returns:
An instance of TestCase.
"""
test_name = request.node.name
test_result_path = test_results_dir.joinpath(test_name)
return TestCase(name=test_name, file_path=test_result_path)
@pytest.fixture(scope="function")
def py(test_case: TestCase, py_config: PyleniumConfig, request):
"""Initialize a Pylenium driver for each test.
Pass in this `py` fixture into the test function.
Examples:
def test_go_to_google(py):
py.visit('https://google.com')
assert 'Google' in py.title()
"""
py = Pylenium(py_config)
yield py
try:
if request.node.report.failed:
# if the test failed, execute code in this block
if py_config.logging.screenshots_on:
screenshot = py.screenshot(str(test_case.file_path.joinpath("test_failed.png")))
allure.attach(screenshot, "test_failed.png", allure.attachment_type.PNG)
elif request.node.report.passed:
# if the test passed, execute code in this block
pass
else:
# if the test has another result (ie skipped, inconclusive), execute code in this block
pass
except Exception:
logging.error("Failed to take screenshot on test failure.")
py.quit()
@pytest.fixture(scope="class")
def pyc(pyc_config: PyleniumConfig, request):
"""Initialize a Pylenium driver for an entire test class."""
py = Pylenium(pyc_config)
yield py
try:
if request.node.report.failed:
# if the test failed, execute code in this block
if pyc_config.logging.screenshots_on:
allure.attach(py.webdriver.get_screenshot_as_png(), "test_failed.png", allure.attachment_type.PNG)
elif request.node.report.passed:
# if the test passed, execute code in this block
pass
else:
# if the test has another result (ie skipped, inconclusive), execute code in this block
pass
except Exception:
logging.error("Failed to take screenshot on test failure.")
py.quit()
@pytest.fixture(scope="session")
def pys(pys_config: PyleniumConfig, request):
"""Initialize a Pylenium driver for an entire test session."""
py = Pylenium(pys_config)
yield py
try:
if request.node.report.failed:
# if the test failed, execute code in this block
if pys_config.logging.screenshots_on:
allure.attach(py.webdriver.get_screenshot_as_png(), "test_failed.png", allure.attachment_type.PNG)
elif request.node.report.passed:
# if the test passed, execute code in this block
pass
else:
# if the test has another result (ie skipped, inconclusive), execute code in this block
pass
except Exception:
logging.error("Failed to take screenshot on test failure.")
py.quit()
@pytest.fixture(scope="function")
def axe(py) -> PyleniumAxe:
"""The aXe A11y audit tool as a fixture."""
return PyleniumAxe(py.webdriver)
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""Yield each test's outcome so we can handle it in other fixtures."""
outcome = yield
report = outcome.get_result()
if report.when == "call":
setattr(item, "report", report)
return report
def pytest_addoption(parser):
parser.addoption("--browser", action="store", default="", help="The lowercase browser name: chrome | firefox")
parser.addoption("--local_path", action="store", default="", help="The filepath to the local driver")
parser.addoption("--remote_url", action="store", default="", help="Grid URL to connect tests to.")
parser.addoption("--screenshots_on", action="store", default="", help="Should screenshots be saved? true | false")
parser.addoption(
"--pylenium_json",
action="store",
default="",
help="The filepath of the pylenium.json file to use (ie dev-pylenium.json)",
)
parser.addoption(
"--pylog_level", action="store", default="INFO", help="Set the logging level: 'DEBUG' | 'COMMAND' | 'INFO' | 'USER' | 'WARNING' | 'ERROR' | 'CRITICAL'"
)
parser.addoption(
"--options",
action="store",
default="",
help='Comma-separated list of Browser Options. Ex. "headless, incognito"',
)
parser.addoption(
"--caps",
action="store",
default="",
help='List of key-value pairs. Ex. \'{"name": "value", "boolean": true}\'',
)
parser.addoption(
"--page_load_wait_time",
action="store",
default="",
help="The amount of time to wait for a page load before raising an error. Default is 0.",
)
parser.addoption("--extensions", action="store", default="", help='Comma-separated list of extension paths. Ex. "*.crx, *.crx"')
================================================
FILE: docker-compose.yml
================================================
# To execute this docker-compose yml file use `docker-compose -f docker-compose-v3.yml up`
# Add the `-d` flag at the end for detached execution
# To stop the execution, hit Ctrl+C, and then `docker-compose -f docker-compose-v3.yml down`
version: "3"
services:
chrome:
image: selenium/node-chrome:4.1.3-20220327
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
edge:
image: selenium/node-edge:4.1.3-20220327
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
firefox:
image: selenium/node-firefox:4.1.3-20220327
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
selenium-hub:
image: selenium/hub:4.1.3-20220327
container_name: selenium-hub
ports:
- "4442:4442"
- "4443:4443"
- "4444:4444"
================================================
FILE: docs/README.md
================================================
---
description: Web Test Automation made easy
---
# Welcome to the Pylenium.io Docs
[](https://github.com/ElSnoMan/pyleniumio/tree/0bd684d227127daf2eccd2f284b849d4a91e3cb5/docs/code_of_conduct.md)
## Welcome to the Pylenium.io Docs
### The mission is simple
> Bring the best of Selenium, Cypress and Python into one package.
This means:
* Automatic waiting and synchronization
* Quick setup to start writing tests
* Easy to use and clean syntax for amazing readability and maintainability
* Automatic driver installation so you don't need to manage drivers
* Leverage the awesome Python language
* and more!
#### Test Example
Let's use this simple scenario to show the difference between using `Selenium` and `Pylenium`:
1. **Visit** the QA at the Point website: [https://qap.dev](https://qap.dev/)
2. **Hover** the About link to reveal a menu
3. **Click** the Leadership link in that menu
4. **Assert** Carlos Kidman is on the Leadership page
{% code title="Using Pylenium" %}
```python
def test_carlos_is_on_leadership(py):
py.visit('https://qap.dev')
py.get('a[href="/about"]').hover()
py.get('a[href="/leadership"][class^="Header-nav"]').click()
assert py.contains('Carlos Kidman')
```
{% endcode %}
{% code title="The same test using Selenium" %}
```python
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
# define your setup and teardown fixture
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_carlos_is_on_leadership_page_with_selenium(driver):
wait = WebDriverWait(driver, timeout=10)
driver.get('https://qap.dev')
# hover About link
about_link = driver.find_element(By.CSS_SELECTOR, "a[href='/about']")
actions = ActionChains(driver)
actions.move_to_element(about_link).perform()
# click Leadership link in About menu
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "a[href='/leadership'][class^='Header-nav']"))).click()
# check if 'Carlos Kidman' is on the page
assert wait.until(lambda _: driver.find_element(By.XPATH, "//*[contains(text(), 'Carlos Kidman')]"))
```
{% endcode %}
#### Purpose
I teach courses and do trainings for both **Selenium** and **Cypress**, but Selenium, out of the box, _feels_ clunky. When you start at a new place, you almost always need to "setup" the framework from scratch all over again. Instead of getting right to creating meaningful tests, you end up spending most of your time building a custom framework, maintaining it, and having to teach others to use it.
Also, many people blame Selenium for bad or flaky tests. This usually tells me that they have yet to experience someone that truly knows how to make Selenium amazing! This also tells me that they are not aware of the usual root causes that make Test Automation fail:
* Poor programming skills, test design and practices
* Flaky applications
* Complex frameworks
What if we tried to get the best from both worlds and combine it with an amazing language?
**Selenium** has done an amazing job of providing W3C bindings to many languages and makes scaling a breeze.
**Cypress** has done an amazing job of making the testing experience more enjoyable - especially for beginners.
**Pylenium** looks to bring more Cypress-like bindings and techniques to Selenium \(like automatic waits\) and still leverage Selenium's power along with the ease-of-use and power of **Python**.
### Quick Start
{% hint style="success" %}
If you are new to Selenium or Python, do the [Getting Started steps 1-4](getting-started/virtual-environments.md)
{% endhint %}
You can also watch the Getting Started video with Pylenium's creator, Carlos Kidman!
{% embed url="https://www.youtube.com/watch?v=li1nc4SUojo" caption="Getting Started with v1.7.7+" %}
{% hint style="success" %}
You don't need to worry about installing any driver binaries like `chromedriver`. **Pylenium** does this all for you automatically :\)
{% endhint %}
#### 1. Install **pyleniumio**
{% code title="Terminal $" %}
```python
pip install pyleniumio
---or---
pipenv install pyleniumio
```
{% endcode %}
#### 2. Initialize Pylenium
{% code title="Terminal $ " %}
```text
pylenium init
```
{% endcode %}
{% hint style="success" %}
Execute this command at your Project Root
{% endhint %}
This creates three files:
* `conftest.py` - This has the fixtures needed for Pylenium.
* `pylenium.json` - This is the [configuration ](https://github.com/ElSnoMan/pyleniumio/blob/master/docs/configuration/pylenium.json.md)file for Pylenium.
* `pytest.ini` - This is the configuration file for pytest and is used to connect to [ReportPortal](https://github.com/ElSnoMan/pyleniumio/blob/master/docs/configuration/report-portal.md)
By default, Pylenium uses Chrome browser. You have to install Chrome or update the `pylenium.json` file to use the browser of your choice.
#### 3. Write a test
Create a directory called `tests` and then a test file called `test_google.py`
Define a new test called `test_google_search`
{% code title="test\_google.py" %}
```python
def test_google_search(py)
```
{% endcode %}
{% hint style="info" %}
Pylenium uses **pytest** as the Test Framework. You only need to pass in `py`to the function!
{% endhint %}
Now we can use **Pylenium Commands** to interact with the browser.
{% code title="test\_google.py" %}
```python
def test_google_search(py):
py.visit('https://google.com')
py.get("[name='q']").type('puppies')
py.get("[name='btnK']").submit()
assert py.should().contain_title('puppies')
```
{% endcode %}
#### 4. Run the Test
This will depend on your IDE, but you can always run tests from the CLI:
{% code title="Terminal $ \(venv\)" %}
```bash
python -m pytest tests/test_google.py
```
{% endcode %}
You're all set! You should see the browser open and complete the commands we had in the test :\)
================================================
FILE: docs/SUMMARY.md
================================================
# Table of contents
* [Welcome to the Pylenium.io Docs](README.md)
* [Changelog](changelog.md)
## Getting Started
* [1. Virtual Environments](getting-started/virtual-environments.md)
* [2. Setup pytest](getting-started/setup-pytest.md)
* [3. Project Structure with pytest](getting-started/project-structure-with-pytest.md)
* [4. Writing Tests with Pylenium](getting-started/writing-tests-with-pylenium.md)
## Guides
* [Run Tests in Parallel](guides/run-tests-in-parallel.md)
* [Run Tests in Containers](guides/run-tests-in-containers.md)
## CLI
* [Pylenium CLI](cli/pylenium-cli.md)
* [Report Portal](cli/report-portal.md)
## Configuration
* [pylenium.json](configuration/pylenium.json.md)
* [Driver](configuration/driver.md)
* [Logging](configuration/logging.md)
* [Report Portal](configuration/report-portal.md)
## CONTRIBUTE
* [Clone and Setup the Project](contribute/clone-and-setup-the-project.md)
## Fixtures
* [axe](fixtures/axe.md)
* [fake](fixtures/fake.md)
* [api \(aka requests\)](fixtures/requests.md)
## Pylenium Commands
* [Commands](pylenium-commands/commands.md)
* [contains](pylenium-commands/contains.md)
* [delete\_all\_cookies](pylenium-commands/delete_all_cookies.md)
* [delete\_cookie](pylenium-commands/delete_cookie.md)
* [execute\_script](pylenium-commands/execute_script.md)
* [fake](pylenium-commands/fake.md)
* [find](pylenium-commands/find.md)
* [findx](pylenium-commands/find_xpath.md)
* [get](pylenium-commands/get.md)
* [get\_cookies](pylenium-commands/get_cookies.md)
* [get\_cookie](pylenium-commands/get_cookie.md)
* [getx](pylenium-commands/get_xpath.md)
* [go](pylenium-commands/go.md)
* [maximize\_window](pylenium-commands/maximize_window.md)
* [quit](pylenium-commands/quit.md)
* [request](pylenium-commands/request.md)
* [screenshot](pylenium-commands/screenshot.md)
* [scroll\_to](pylenium-commands/scroll_to.md)
* [set\_cookie](pylenium-commands/set_cookie.md)
* [should](pylenium-commands/should.md)
* [switch\_to.default\_content](pylenium-commands/switch_to.default_content.md)
* [switch\_to.frame](pylenium-commands/switch_to.frame.md)
* [switch\_to.parent\_frame](pylenium-commands/switch_to.parent_frame.md)
* [switch\_to.window](pylenium-commands/switch_to.window.md)
* [title](pylenium-commands/title.md)
* [url](pylenium-commands/url.md)
* [wait](pylenium-commands/wait.md)
* [webdriver](pylenium-commands/webdriver.md)
* [window\_handles](pylenium-commands/window_handles.md)
* [window\_size](pylenium-commands/window_size.md)
* [viewport](pylenium-commands/viewport.md)
* [visit](pylenium-commands/visit.md)
* [\(deprecated\) xpath](pylenium-commands/xpath.md)
## Element Commands
* [check](element-commands/check.md)
* [children](element-commands/children.md)
* [click](element-commands/click.md)
* [css\_value](element-commands/css_value.md)
* [deselect](element-commands/deselect.md)
* [double\_click](element-commands/double_click.md)
* [drag\_to](element-commands/drag_to.md)
* [drag\_to\_element](element-commands/drag_to_element.md)
* [first](element-commands/first.md)
* [get\_attribute](element-commands/get_attribute.md)
* [get\_property](element-commands/get_property.md)
* [hover](element-commands/hover.md)
* [is\_checked](element-commands/is_checked.md)
* [is\_displayed](element-commands/is_displayed.md)
* [is\_enabled](element-commands/is_enabled.md)
* [is\_empty](element-commands/is_empty.md)
* [is\_selected](element-commands/is_selected.md)
* [last](element-commands/last.md)
* [length](element-commands/length.md)
* [open\_shadow\_dom](element-commands/open_shadow_dom.md)
* [parent](element-commands/parent.md)
* [right\_click](element-commands/right_click.md)
* [screenshot](element-commands/screenshot.md)
* [scroll\_into\_view](element-commands/scroll_into_view.md)
* [select](element-commands/select.md)
* [select\_many](element-commands/select_many.md)
* [should](element-commands/should.md)
* [siblings](element-commands/siblings.md)
* [submit](element-commands/submit.md)
* [tag\_name](element-commands/tag_name.md)
* [text](element-commands/text.md)
* [type](element-commands/type.md)
* [uncheck](element-commands/uncheck.md)
* [webelement](element-commands/webelement.md)
## Misc
* [Install chromedriver](misc/install-chromedriver.md)
================================================
FILE: docs/changelog.md
================================================
---
description: Summary of notable changes and fixes.
---
# Changelog
## 1.12.3 - 2021-14-04
### Overview
`.select()` and `.select_many()` worked pretty well as expected. However, they wouldn't fail as expected! Because we were trying to combine all the "select strategies" in a single function, it made it harder to test and debug _and_ hid some exceptions... like when an `