Skip to content

Releases: ElSnoMan/pyleniumio

Web Performance API

08 Oct 05:02
Compare
Choose a tag to compare

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

Gather Web Performance data and log how long actions take

Fix: from pylenium import Pylenium

02 Oct 16:08
86e80e2
Compare
Choose a tag to compare

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

28 Sep 20:09
a2d097a
Compare
Choose a tag to compare

1.9.7 - 2020-09-28

Added

  • Github Actions CI to Pylenium repo
  • Pylenium CLI: pylenium version is now pylenium --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

24 Jun 23:33
Compare
Choose a tag to compare

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 default pytest.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 and find_xpath functions were not behaving as expected. This has been fixed, but we have also renamed them to

    • getx()
    • findx()
  • AttributeError was raised if there were more than one pytest_runtest_makereport fixtures in the project.

  • Logging now uses the built-in logging python package, but screenshots are still saved to the test_results directory.

should().disappear() and should().have_attr(name)

21 May 18:55
82e4a9f
Compare
Choose a tag to compare

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 the value 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

12 May 02:03
210b84e
Compare
Choose a tag to compare

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.

  • #95 .not_find()
  • #96 .not_find_xpath()
  • #97 .not_contain()
# 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 or Elements 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 default wait_time in pylenium.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 to Elements.length() function
  • Element.tag_name property changed to Element.tag_name() function
  • Element.text property changed to Element.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

08 May 20:11
Compare
Choose a tag to compare

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

08 May 17:50
447b5f7
Compare
Choose a tag to compare

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

07 May 23:44
dd9889d
Compare
Choose a tag to compare

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

01 May 21:02
Compare
Choose a tag to compare

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)