Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lariat-js/playwright

🪢 Page object framework for Playwright.
https://github.com/lariat-js/playwright

end-to-end-testing page-object-model playwright playwright-test testing

Last synced: 3 months ago
JSON representation

🪢 Page object framework for Playwright.

Awesome Lists containing this project

README

        

# Lariat for Playwright

[![Build](https://github.com/lariat-js/playwright/actions/workflows/build.yml/badge.svg)](https://github.com/lariat-js/playwright/actions/workflows/build.yml)
[![npm](https://img.shields.io/npm/v/@lariat/playwright)](https://www.npmjs.com/package/@lariat/playwright)

Page object framework for [Playwright](https://playwright.dev).

## Installation

npm

```sh
npm install @lariat/playwright
```

Yarn

```sh
yarn add @lariat/playwright
```

pnpm

```sh
pnpm add @lariat/playwright
```

Bun

```sh
bun add @lariat/playwright
```

## Basic Usage

At the core of Lariat is the `Collection` class. This class is used to represent
a collection of elements in a page or section of a page and can include
associated utility methods for interacting with those elements.

To create your own collections, simply create a class which extends the
`Collection` class. You can then define elements using the `Collection.el()`
method which we will explore more in a moment.

```ts
import Collection from '@lariat/playwright'

class TodoPage extends Collection {
input = this.el('#todo-input')
}
```

With your collection defined, you can instantiate it in your test to access the
elements.

```ts
test('create a todo', async ({ page }) => {
const todoPage = new TodoPage(page)
await todoPage.input.fill('Finish the website')
})
```

## Elements

Elements are defined in collections using the `Collection.el()` method.

```ts
class TodoPage extends Collection {
saveButton = this.el('#save-button')
}
```

Elements are represented using Playwright
[locators](https://playwright.dev/docs/api/class-locator) .

```ts
const todoPage = new TodoPage(page)
await todoPage.saveButton.click()
```

### `has` and `hasText`

The `has` and `hasText` locator options can be passed to `Collection.el()` to
match elements that contain a given locator or text.

```ts
class TodoPage extends Collection {
buttonWithIcon = this.el('button', { has: this.el('svg') })
saveButton = this.el('button', { hasText: 'Save' })
}
```

### Dynamic selectors

Because collections in Lariat are plain JavaScript classes, you can easily
create elements with dynamic selectors. Consider a todo list where we find an
item based on it's name. Our collection might look something like this:

```ts
class TodoPage extends Collection {
item = (name: string) => this.el(`#todo-item[data-name="${name}"]`)
}

const todoPage = new TodoPage(page)
const item = todoPage.item('Finish the website')
await item.click()
```

### Portals

Sometimes, the DOM structure of a page might not match the visual structure
exactly. For example, if you use React's `createPortal` function you can render
an element outside the main React tree. To support these use cases, Lariat
allows you to pass a `portal` option to `Collection.el()` to indicate that the
element should not be based off the `root` element.

```ts
class TodoPage extends Collection {
modal = this.el('#modal', { portal: true })
}
```

## Utility methods

Because collections are plain JavaScript classes, you can easily add utility
methods to your collections.

```ts
class TodoPage extends Collection {
input = this.el('#todo-input')
saveButton = this.el('#save-button')

async create(name: string) {
await this.input.fill(name)
await this.input.click()
}
}

const todoPage = new TodoPage(page)
await todoPage.create('Finish the website')
```

## Nested collections

So far, we've shown examples of simple collections, but Lariat also gives you
the ability to nest collections inside each other. With this approach, you can
create a page object structure that more closely resembles your page layout.

To nest a collection, use the `Collection.nest()` method and pass the nested
collection class and the root of the nested collection.

```ts
class TodoPage extends Collection {
field = this.nest(TextField, '#todo-field')
}

const todoPage = new TodoPage(page)
await todoPage.field.input.fill('Finish the website')
```

If your nested collection is used merely to group a set of related elements
together, you can use the parent's `root` property as the root of the child
collection.

```ts
class TodoPage extends Collection {
field = this.nest(TextField, this.root)
}
```

If your nested collection exists outside the DOM structure of the parent
collection, you can use the parent's `frame` property as the root of the child
collection. This behaves very similarly to the `portal` option for
`Collection.el()`.

```ts
class TodoPage extends Collection {
modal = this.nest(Modal, this.frame)
}
```

### `first`, `last`, and `nth`

In some cases, you may have a nested collection where multiple instances exist
on the page. For example, a todo list may contain multiple todo items each of
which are represented as a collection. To make these scenarios easier, Lariat
provides `first`, `last`, and `nth` methods which will return a new instance of
the nested collection scoped to that specific item.

```ts
class TodoPage extends Collection {
item = this.nest(TodoItem, '.todo-item')
}

const todoPage = new TodoPage(page)
const firstItem = todoPage.item.first()
const secondItem = todoPage.item.nth(1)
const lastItem = todoPage.item.last()
```

## Frames

Lariat has utilities for working with frames thanks to Playwright's
[`FrameLocator`](https://playwright.dev/docs/api/class-framelocator).

The simplest way to access an element in a frame is by using the `frame` option
of `Collection.el`. Simply pass a string as a selector for the frame and Lariat
will take care of the rest.

```ts
class FramePage extends Collection {
frameHeader = this.el('h1', { frame: '#my-frame' })
}
```

However, if you need to access multiple elements in a frame, you can use
`Collection.nest` with a `FrameLocator`.

```ts
class MyFrame extends Collection {
header = this.el('h1')
content = this.el('main')
}

class FramePage extends Collection {
myFrame = this.nest(MyFrame, this.page.frameLocator('#my-frame'))
}
```

You can use a similar method to instantiate collections with a `FrameLocator` as
the root.

```ts
class MyFrame extends Collection {
header = this.el('h1')
content = this.el('main')
}

new MyFrame(page.frameLocator('#my-frame'))
```

## Accessing the page or frame

Lariat makes it easy to access the page or frame that a collection is associated
with.

```ts
class TodoPage extends Collection {
input = this.el('#todo-input')
}

const todoPage = new TodoPage(page.locator('#my-page'))
await todoPage.frame.goto('https://google.com')
await todoPage.page.mouse.down()
```