Releases: ElSnoMan/pyleniumio
Web Performance API
1.10.0 - 2020-10-7
Overview
Pylenium can now gather Web Performance metrics right from the browser and into your hands! Through Pylenium's Performance API, you can leverage the different Performance and Timing objects that each browser tracks and generates. Also, we have created some custom data points, like Time to Interactive
, Page Load Time
, and Time to First Contentful Paint
!
Added
- Performance API
# Access the Performance API right from py
perf = py.performance.get()
- WindowPerformance Object
The main abstraction that holds all of these metrics and data points.
# get the TTI
tti = py.performance.get().time_to_interactive()
- Stopwatch Decorator
The performance.py
module includes a Stopwatch Decorator that you add to any function. This will log how long it takes for that function to complete!
# 1. How long does it take to add an item to the cart?
@stopwatch
def add_item_to_cart(py):
py.get('#add-item').click()
py.get('#added-notification').should().be_visible()
# 2. How long does it take to edit an item's available stock via the API and see it change in the UI?
@stopwatch
def update_available_stock(py, item, quantity):
payload = {'item': item, 'qty': quantity}
api.items.update(payload)
py.get(f'#available-stock-{item}').should().have_text(quantity)
- CONTRIBUTING.md
- CODE_OF_CONDUCT.md
Linked Issues
Fix: from pylenium import Pylenium
1.9.8 - 2020-10-2
Fixed
from pylenium import Pylenium
In the CLI, running pylenium init
creates files to be used by Pylenium. One of them is the conftest.py
file which was still using the old from pylenium import Pylenium
which no longer works (see v1.9.7).
This is a very small fix that only affects new Pylenium users.
Quickfix to use last stable Pytest version
1.9.7 - 2020-09-28
Added
- Github Actions CI to Pylenium repo
- Pylenium CLI:
pylenium version
is nowpylenium --version
If you want to import the Pylenium
class into a Module, you used to do this:
from pylenium import Pylenium
This has now been changed (for CI reasons) to:
from pylenium.driver import Pylenium
So make sure you update this import statement if you were using the previous version!
Fixes
- pytest version 6.1.0 (released in September 2020) causes issues within Pylenium's conftest.py. Changed Pipfile to use previous version until a solution is found
is_checked()
now works for Radio Buttons and Checkboxes
ReportPortal.io Integration
1.9.0 - 2020-06-24
Report Portal (RP)
RP is now natively supported by Pylenium! If you are not already familiar with Report Portal, I highly suggest you check it out. It gives you robust reporting and categorizing of your test runs and results and is backed with machine learning!
https://reportportal.io
We had very basic logging and reporting, but we wanted to provide a better and more robust reporting solution. After a lot of research, we landed on RP. They are not only free and Open Source, but they also have a great community, Slack group, and YouTube channel with different demos and presentations to help you take your reporting to the next level. This level of modern support was crucial in our decision and we hope you enjoy it!
Added
pylenium init
now also creates a defaultpytest.ini
file at your Project Root. This contains values to easily connect with RP.pylenium portal
CLI Commands to quickly setup your RP instance
# 1. Download the docker-compose file used to spin up RP
pylenium portal download
# 2. Configure your machine and this docker-compose.yml based on your OS and needs
# by going to https://reportportal.io/docs/Deploy-with-Docker
# 3. Spin up the RP instance
pylenium portal up
That's it! You'll get helpful hints as you execute each command so you know where to go and how to login. Happy reporting!
Fixes
-
get_xpath
andfind_xpath
functions were not behaving as expected. This has been fixed, but we have also renamed them togetx()
findx()
-
AttributeError
was raised if there were more than onepytest_runtest_makereport
fixtures in the project. -
Logging now uses the built-in
logging
python package, but screenshots are still saved to thetest_results
directory.
should().disappear() and should().have_attr(name)
1.8.1 - 2020-05-21
Changed
-
Element.should().not_exist()
-->Element.should().disappear()
If the intent is to check that the element is not on the page, then use:
py.should().not_find()
If the intent is to wait until an existing element does not exist anymore or "disappear", then you used to have to do
py.get().should().not_exist()
However, this is clearly confusing because the way should not exist
reads would suggest that both options are the same. This has now been changed to more clearly reflect that intent.
py.get().should().disappear()
-
Element.should().have_attr()
doesn't require thevalue
argument
There is a scenario when all you want to check on an element is that an attribute exists or not.
Example:
py.wait().until(lambda _: toggle.get_attribute('aria-checked'))
Unfortunately, ElementShould.have_attr()
requires an attribute name AND a value. Trying to use it in this scenario is difficult to use or straight up doesn't work.
# doesn't work
py.get(TOGGLE).should().have_attr('aria-checked', True)
# doesn't work either
py.get(TOGGLE).should().have_attr('aria-checked', '')
# this is just confusing...
py.get(TOGGLE).should().not_have_attr('aria-checked', None)
# this may work if it sees the custom `aria-checked` attribute as "checked"
py.get(TOGGLE).should().be_checked()
Solution
Make the existing expectations not require the value
argument.
should().have_attr(name, value=None)
should().not_have_attr(name, value=None)
Fixed
drag_and_drop.js
was not included in the pylenium installation. Now it is!- Some typos
ElementsShould and Immediate Poll
1.8.0 - 2020-05-11
Added
This is a bigger change that sets us up for things we want to do with better reporting and BDD functionality. There may be some breaking changes depending how you wrote your tests.
For example, the property of Element.text
is now a function Element.text()
and .find()
no longer has an at_least_one
parameter.
Make sure you run your tests after upgrading to catch errors like str is not invocable
. They should be easy to fix.
PyleniumShould
The use case of checking that an element is NOT on the page or DOM was much more common than anticipated. I have changed how the .find()
and .find_xpath()
functions behave to help with this, but there are now three easy to use "should" commands as well.
# example usage
py.should().not_find('#hidden-element')
Driver
Having these as properties was actually messing people up as they used Pylenium. Because almost all of the commands are functions, it was common that someone would try py.url()
or py.title()
only to see the test fail saying that str is not invocable
. Changing these to functions feels more natural.
.url
property changed to.url()
function.title
property changed to.title()
function
XPaths
Removed the .xpath()
function from Pylenium and Element and replaced with get
and find
options. The .xpath()
function could return an empty list, a single element, or a list of 2 or more elements. The flexibility was pretty "clever", but it was not intuitive to work with. Separating it into two distinct functions to match the CSS versions of get()
and .find()
made more sense.
.get_xpath()
find_xpath()
# single element with xpath
py.get_xpath('//input[@name="q"]')
# list of elements with xpath
py.find_xpath('//li')
Find Elements
The .find()
and .find_xpath()
functions on the Pylenium and Element objects will now return an empty list if none are found rather than throwing an exception. Dealing with an empty list is easier and cleaner than having to handle an exception.
However, this is not the case If the timeout is set to 0
(zero). The next section goes into more detail.
Immediate Poll with timeout=0
There are times when you don't want to use an awesome wait and a timeout of 1 second isn't good enough. For all of the Find Element commands, you can now set the timeout to 0
(zero) to poll the DOM immediately as if you were using Selenium with no wait.
This will still return an
Element
orElements
object if found, but no wait is used.
Let's take a look at the .get()
signature:
def get(self, css: str, timeout: int = None) -> Element
- If
timeout=None
(default), the function will use the defaultwait_time
inpylenium.json
which is 10 seconds.
# use `wait_time` in pylenium.json
py.get('#button').click()
- If
timeout > 0
, override the default wait_time.
# shorten it to 3 seconds instead
py.get('#button', timeout=3).click()
---or---
# give it even more time
py.get('#button', timeout=30).click()
- If timeout=0, poll the DOM immediately without any waiting.
# no waiting, immediately poll the DOM
py.get('#button', timeout=0).click()
Element and Elements
Changed some properties to functions for the same reasons as the props in Driver.
Elements.length
property changed toElements.length()
functionElement.tag_name
property changed toElement.tag_name()
functionElement.text
property changed toElement.text()
function
ElementsShould
Pylenium and Element have their own Should classes for expectations. Most of our assertions and checks are done against them, but there were enough use cases against the length of the Elements that I wanted to include them to make it easier. Now when you have a list of elements (Elements), you can use .should()
:
be_empty()
not_be_empty()
have_length()
be_greater_than()
be_less_than()
Pylenium CLI: Mac, Windows, and Linux
1.7.7
Added
Use Click-native commands to work with any OS
Fixed
pylenium.json
was not included in the packaging so copying it with $ pylenium init
was not working. This is fixed now.
Pylenium CLI
1.7.0 - 2020-05-08
Added
Pylenium CLI
Details
After a fresh install of pyleniumio, you now need to initialize pylenium using the Pylenium CLI:
$ pylenium init
You can also see the available options using the --help
argument.
$ pylenium init --help
This will create the conftest.py
file needed by Pylenium as well as the default pylenium.json
config file.
Purpose
Originally, Pylenium would copy a conftest.py file and overwrite any existing conftest.py files the user had at the Project Root. This was a necessary side effect with how setup.py
was working. With pylenium init
, you now have the option to create or overwrite these files rather than needing to start from scratch.
pylenium init
also creates a default pylenium.json
so the user knows what config values they can change globally. This makes for a much easier experience for users.
Add Extension and Shadow DOM
1.6.2 - 2020-05-07
Added
options.add_extension()
Element.open_shadow_dom()
Details
Add Extension
You can now easily add extensions to your browser sessions by either using the --extensions
CLI argument and passing in a list of file paths, or you can also do this in the pylenium.json
{
"driver": {
"extension_paths": ["path.crx", "other-path.crx"]
}
}
Shadow DOM
Shadow DOMs are a bit tricky, but you can now find elements within them by using the Element.open_shadow_dom()
command. Check out this example using chrome://extensions
:
def test_loading_extension_to_browser(py):
py.visit('chrome://extensions/')
shadow1 = py.get('extensions-manager').open_shadow_dom()
shadow2 = shadow1.get('extensions-item-list').open_shadow_dom()
extension_shadow_dom = shadow2.find('extensions-item')[1].open_shadow_dom()
assert extension_shadow_dom.get('#name-and-version').should().contain_text('Get CRX')
Drag and Drop
1.6.1 - 2020-05-01
Added
drag_to( css )
drag_to_element ( to_element )
# drag_to
py.get('locator').drag_to('to_locator')
# drag_to_element
to_element = py.get('to_locator')
py.get('locator').drag_to_element(to_element)