Code Monkey home page Code Monkey logo

selene's Introduction

Selene - User-oriented Web UI browser tests in Python (Selenide port)

tests codecov Free MIT License Project Template Code style: black Downloads

GitHub stats in GitHub views GitHub views per week GitHub clones GitHub clones per week

Join telegram chat https://t.me/selene_py Присоединяйся к чату https://t.me/selene_py_ru

Sign up for a course http://autotest.how/selene Запишись на курс http://autotest.how/selene-ru Учи Selene https://leanpub.com/selene-automation-ru Реєструйся на курс http://autotest.how/selene-uk

Main features:

  • User-oriented API for Selenium Webdriver (code like speak common English)
  • Ajax support (Smart implicit waiting and retry mechanism)
  • PageObjects support (all elements are lazy-evaluated objects)
  • Automatic driver management (no need to install and setup driver for quick local execution)

Selene was inspired by Selenide from Java world.

Tests with Selene can be built either in a simple straightforward "selenide' style or with PageObjects composed from Widgets i.e. reusable element components.

Table of content

Versions

  • Latest recommended version to use is >= 2.0.0a39

    • it's a completely new version of selene, with improved API and speed
    • supports python >= 3.7
    • it's incompatible with 1.x
    • current master branch is pointed to 2.x
    • yet in pre-alpha stage, refining API, improving "migratability", and testing
    • it looks pretty stable, but not fully proven yet
      • mainly tested on production code base of a few users who successfully migrated from 1.x to 2.x
  • Latest version marked as stable is: 1.0.2

    • it is main version used by most selene users during last 2 years
    • it was proven to be stable for production use
    • its sources and corresponding README version can be found at 1.x branch.
    • supports python 2.7, 3.5, 3.6, 3.7

THIS README DESCRIBES THE USAGE OF THE PRE-RELEASE version of Selene. For older docs look at 1.x branch.

Migration guide

From 1.0.1 to 2.0.0aLATEST:

  • upgrade to python 3.7
  • update selene to 2.0.0aLATEST
    • find&replace the collection.first() method from .first() to .first
    • ensure all conditions like text('foo') are used via be.* or have.* syntax
      • example:
        • find&replace all
          • (text('foo')) to (have.text('foo'))
          • (visible) to (be.visible)
        • smarter find&replace (with some manual refactoring)
          • .should(x, timeout=y) to .with_(timeout=y).should(x)
        • and add corresponding imports: from selene import be, have
    • fix another broken imports if available
    • run tests, read deprecation warnings, and refactor to new style recommended in warning messages

Prerequisites

Python >= 3.7

Given pyenv installed, installing needed version of Python is pretty simple:

$ pyenv install 3.7.3
$ pyenv global 3.7.3
$ python -V
Python 3.7.3

Installation

via poetry + pyenv (recommended)

GIVEN poetry and pyenv installed ...

AND

$ poetry new my-tests-with-selene
$ cd my-tests-with-selene
$ pyenv local 3.7.3

WHEN latest pre-release recommended version:

$ poetry add selene --allow-prereleases

WHEN latest stable version:

$ poetry add selene

THEN

$ poetry install

via pip

Latest recommended pre-release alpha version:

$ pip install selene --pre

Latest stable version:

$ pip install selene

from sources

GIVEN webdriver and webdriver_manager are already installed

THEN

$ git clone https://github.com/yashaka/selene.git
$ python setup.py install

or using pip:

$ pip install git+https://github.com/yashaka/selene.git

Usage

Quick Start

Simply...

from selene.support.shared import browser
from selene import by, be, have

browser.open('https://google.com/ncr')
browser.element(by.name('q')).should(be.blank)\
    .type('selenium').press_enter()
browser.all('.srg .g').should(have.size(10))\
    .first.should(have.text('Selenium automates browsers'))

OR with custom setup

from selene.support.shared import browser
from selene import by, be, have

browser.config.browser_name = 'firefox'
browser.config.base_url = 'https://google.com'
browser.config.timeout = 2
# browser.config.* = ...

browser.open('/ncr')
browser.element(by.name('q')).should(be.blank)\
    .type('selenium').press_enter()
browser.all('.srg .g').should(have.size(10))\
    .first.should(have.text('Selenium automates browsers'))

OR more Selenide from java style:

from selene.support.shared import config, browser
from selene import by, be, have
from selene.support.shared.jquery_style import s, ss


config.browser_name = 'firefox'
config.base_url = 'https://google.com'
config.timeout = 2
# config.* = ...

browser.open('/ncr')
s(by.name('q')).should(be.blank)\
    .type('selenium').press_enter()
ss('.srg .g').should(have.size(10))\
    .first.should(have.text('Selenium automates browsers'))

Core Api

Given:

from selenium.webdriver import Chrome

AND chromedriver executable available in $PATH

WHEN:

from selene import Browser, Config

browser = Browser(Config(
    driver=Chrome(),
    base_url='https://google.com',
    timeout=2))

AND (uncomment if needed):

# import atexit
# atexit.register(browser.quit)

AND:

browser.open('/ncr')

AND:

# browser.element('//*[@name="q"]')).type('selenium').press_enter()
# OR...

# browser.element('[name=q]')).type('selenium').press_enter()
# OR...

from selene import by

# browser.element(by.name('q')).type('selenium').press_enter()
# OR...for total readability

query = browser.element(by.name('q'))  # actual search doesn't start here, the element is "lazy"          
     # here the actual webelement is found
query.type('selenium').press_enter()       
                      # and here it's located again, i.e. the element is "dynamic"

AND (in case we need to filter collection of items by some condition like visibility):

from selene import be

results = browser.all('.srg .g').filtered_by(be.visible)

THEN:

from selene import have

# results.should(have.size(10))
# results.first.should(have.text('Selenium automates browsers'))
# OR...

results.should(have.size(10))\
    .first.should(have.text('Selenium automates browsers'))

FINALLY (if not registered "atexit" before):

browser.quit()

Automatic Driver and Browser Management

Instead of:

from selene import Browser, Config

browser = Browser(Config(
    driver=Chrome(),
    base_url='https://google.com',
    timeout=2))

You can simply use the browser and config instance predefined for you in selene.support.shared module:

from selene.support.shared import browser, config

# ... do the same with browser.*

So you don't need to create you driver instance manually. It will be created for you automatically.

Yet, if you need some special case, like working with remote driver, etc., you can still use shared browser object, while providing driver to it through:

config.driver = my_remote_driver_instance
# or
browser.config.driver = my_remote_driver_instance

Advanced API

Sometimes you might need some extra actions on elements, e.g. for workaround something through js:

from selene import command

browser.element('#not-in-view').perform(command.js.scroll_into_view)

Probably you think that will need something like:

from selene import query

product_text = browser.element('#to-assert-something-non-standard').get(query.text)
price = my_int_from(product_text)
assert price > 100

But usually it's either better to implement your custom condition:

browser.element('#to-assert-something-non-standard').should(have_in_text_the_int_number_more_than(100))

Where the have_in_text_the_int_number_more_than is your defined custom condition. Such condition-based alternative will be less fragile, because python's assert does not have "implicit waiting", like selene's should ;)

Furthermore, the good test is when you totally control your test data, and instead:

product = browser.element('#to-remember-for-future')

product_text_before = product.get(query.text)
price_before = my_int_from(product_text_before)

# ... do something

product_text_after = product.get(query.text)
price_after = my_int_from(product_text_after)

assert price_after > price_before

Normally, better would be to refactor to something like:

product = browser.element('#to-remember-for-future')

product.should(have.text('100$'))

# ... do something

product.should(have.text('125$'))

You might think you need something like:

from selene import query

if browser.element('#i-might-say-yes-or-no').get(query.text) == 'yes':
    # do something...

Or:

from selene import query

if browser.all('.option').get(query.size) >= 2:
    # do something...

Maybe one day, you really find a use case:) But for above cases, probably easier would be:

if browser.element('#i-might-say-yes-or-no').wait_until(have.text('yes')):
    # do something

# ...

if browser.all('.i-will-appear').wait_until(have.size_greater_than_or_equal(2)):
    # do something

Or, by using non-waiting versions, if "you are in a rush:)":

if browser.element('#i-might-say-yes-or-no').matching(have.text('yes')):
    # do something

# ...

if browser.all('.i-will-appear').matching(have.size_greater_than_or_equal(2)):
    # do something

Tutorials

TBD

More examples

TBD

Contributing

see CONTRIBUTING.md

Release Process

see CONTRIBUTING.md#release-process

Changelog

see CHANGELOG.md

selene's People

Contributors

ak40u avatar akioyuki avatar aleksandr-kotlyar avatar arnerpan avatar bbartok76 avatar codedsk avatar dunossauro avatar emaddeve20 avatar evgenynalivayko avatar gospodinkot avatar igorvlasovkram avatar initial1ze avatar kainjazz avatar kianku avatar kixiro avatar kylefix avatar maratori avatar pupsikpic avatar rashevskyv avatar rina-tenitska avatar rodinant avatar sergeypirogov avatar simonbaars avatar svetlanaponomarenko avatar vanderloos avatar vfalco02 avatar vikilib avatar yashaka avatar ysparrow avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.