<commit_before>[![Build Status](https://travis-ci.org/testingrequired/vom.svg?branch=master)](https://travis-ci.org/testingrequired/vom)
[![PyPI version](https://badge.fury.io/py/vom.svg)](https://badge.fury.io/py/vom)

# vom

`vom` (View Object Modal) is an opinionated framework for writing page objects for selenium tests/scripts

## View Object vs Page Object

The term "page" is outdated in the context of modern web applications. The term "view" more aligned with the usage of self contained components (e.g. React, Angular, Vue). They represent the same concept however.

## Installation

Tested on Python 2.7.x & 3.6.x

```bash
$ pip install vom
```

## Getting Started

```python
from vom import View

class Login(View):
    @property
    def username(self):
        return self.find_element_by_name("username")

    @property
    def password(self):
        return self.find_element_by_name("password")

    @property
    def login_button(self):
        return self.find_element_by_id("loginBtn")
```

A `View` is then initialized by passing a `Callable[[], WebElement]` where the `WebElement` is the View's [root element](#root-element).

```python
login = Login(lambda: driver.find_element_by_id("loginForm"))
```

### Applications

Applications will have multiple view objects. Each one will need to be initialized which can become cumbersome. A pattern to help with this is an App class. It holds the `WebDriver` instance reference and exposes view objects as properties.

```python
class App(object):
    def __init__(self, driver):
        self.driver = driver
    
    @property
    def login(self):
        return Login(lambda: self.driver.find_element_by_id("loginForm"))
```

## Element Properties

Elements should be properties on the `View` to ensure they never throw a `StaleElementReference`. These element properties are a `View` themselves all the way down.

### Root Element

The `root` element of the `View` is the underlying `WebElement`. All other element properties are within the `root` element scope.

## API

`View` mirrors most of the `WebElement` [API](http://selenium-python.readthedocs.io/api.html) with additional utility methods:

### Finding Elements

The `find_element`/`find_elements` family of methods all return `View` instead of `WebElement` scoped within the `root` `WebElement`.

#### Custom View Class

Element/s are wrapped in a `View` class before being returned from `find_element`/`find_elements` family of methods. A custom subclass of `View` can be passed using the `view_cls` argument.

```python
class CustomView(View): pass

view.find_element(By.CSS_SELECTOR, "input[placeholder='username']", view_cls=CustomView)<commit_msg>Removing application section from readme<commit_after>[![Build Status](https://travis-ci.org/testingrequired/vom.svg?branch=master)](https://travis-ci.org/testingrequired/vom)
[![PyPI version](https://badge.fury.io/py/vom.svg)](https://badge.fury.io/py/vom)

# vom

`vom` (View Object Modal) is an opinionated framework for writing page objects for selenium tests/scripts

## View Object vs Page Object

The term "page" is outdated in the context of modern web applications. The term "view" more aligned with the usage of self contained components (e.g. React, Angular, Vue). They represent the same concept however.

## Installation

Tested on Python 2.7.x & 3.6.x

```bash
$ pip install vom
```

## Getting Started

```python
from vom import View

class Login(View):
    @property
    def username(self):
        return self.find_element_by_name("username")

    @property
    def password(self):
        return self.find_element_by_name("password")

    @property
    def login_button(self):
        return self.find_element_by_id("loginBtn")
```

A `View` is then initialized by passing a `Callable[[], WebElement]` where the `WebElement` is the View's [root element](#root-element).

```python
login = Login(lambda: driver.find_element_by_id("loginForm"))
```

## Element Properties

Elements should be properties on the `View` to ensure they never throw a `StaleElementReference`. These element properties are a `View` themselves all the way down.

### Root Element

The `root` element of the `View` is the underlying `WebElement`. All other element properties are within the `root` element scope.

## API

`View` mirrors most of the `WebElement` [API](http://selenium-python.readthedocs.io/api.html) with additional utility methods:

### Finding Elements

The `find_element`/`find_elements` family of methods all return `View` instead of `WebElement` scoped within the `root` `WebElement`.

#### Custom View Class

Element/s are wrapped in a `View` class before being returned from `find_element`/`find_elements` family of methods. A custom subclass of `View` can be passed using the `view_cls` argument.

```python
class CustomView(View): pass

view.find_element(By.CSS_SELECTOR, "input[placeholder='username']", view_cls=CustomView)
{'fraction_non_alphanumeric': 0.1119008935824533, 'fraction_numerical': 0.0016246953696181965, 'mean_word_length': 4.69364161849711, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '999906', 'n_tokens_mistral': 1470, 'n_tokens_neox': 1411, 'n_words': 519}