Giter Club home page Giter Club logo

playwright-fluent's Introduction

playwright-fluent

Fluent API around Playwright

TestsPipeline Build status npm version Mentioned in Awesome

Installation

npm i --save playwright-fluent

If not already installed, the playwright package should also be installed with a version >= 1.12.0

Usage

import { PlaywrightFluent, userDownloadsDirectory } from 'playwright-fluent';

const p = new PlaywrightFluent();

await p
  .withBrowser('chromium')
  .withOptions({ headless: false })
  .withCursor()
  .withDialogs()
  .recordPageErrors()
  .recordFailedRequests()
  .recordDownloadsTo(userDownloadsDirectory)
  .emulateDevice('iPhone 6 landscape')
  .navigateTo('https://reactstrap.github.io/components/form/')
  .click('#exampleEmail')
  .typeText('[email protected]')
  .pressKey('Tab')
  .expectThatSelector('#examplePassword')
  .hasFocus()
  .typeText("don't tell!")
  .pressKey('Tab')
  .expectThatSelector('#examplePassword')
  .hasClass('is-valid')
  .hover('#exampleCustomSelect')
  .select('Value 3')
  .in('#exampleCustomSelect')
  .close();

This package provides also a Selector API that enables to find and target a DOM element or a collection of DOM elements embedded in complex DOM Hierarchy:

const selector = p
  .selector('[role="row"]') // will get all dom elements, within the current page, with the attribute role="row"
  .withText('foobar') // will filter only those that contain the text 'foobar'
  .find('td') // from previous result(s), find all embedded <td> elements
  .nth(2); // take only the second cell

await p.expectThat(selector).hasText('foobar-2');

Usage with Iframes

This fluent API enables to seamlessly navigate inside an iframe and switch back to the page:

const p = new PlaywrightFluent();
const selector = 'iframe';
const inputInIframe = '#input-inside-iframe';
const inputInMainPage = '#input-in-main-page';
await p
  .withBrowser('chromium')
  .withOptions({ headless: false })
  .withCursor()
  .navigateTo(url)
  .hover(selector)
  .switchToIframe(selector)
  .click(inputInIframe)
  .typeText('hey I am in the iframe')
  .switchBackToPage()
  .click(inputInMainPage)
  .typeText('hey I am back in the page!');

Usage with Dialogs

This fluent API enables to handle alert, prompt and confirm dialogs:

const p = new PlaywrightFluent();

await p
  .withBrowser(browser)
  .withOptions({ headless: true })
  .WithDialogs()
  .navigateTo(url)
  // do some stuff that will open a dialog
  .waitForDialog()
  .expectThatDialog()
  .isOfType('prompt')
  .expectThatDialog()
  .hasMessage('Please say yes or no')
  .expectThatDialog()
  .hasValue('yes')
  .typeTextInDialogAndSubmit('foobar');

Usage with the tracing API

This fluent API enables to handle the playwright tracing API in the following way:

const p = new PlaywrightFluent();

await p
  .withBrowser(browser)
  .withOptions({ headless: true })
  .withTracing()
  .withCursor()
  .startTracing({ title: 'my first trace' })
  .navigateTo(url)
  // do some stuff on the opened page
  .stopTracingAndSaveTrace({ path: path.join(__dirname, 'trace1.zip') })
  // do other stuff
  .startTracing({ title: 'my second trace' })
  // do other stuff
  .stopTracingAndSaveTrace({ path: path.join(__dirname, 'trace2.zip') });

Usage with collection of elements

This fluent API enables to perform actions and assertions on a collection of DOM elements with a forEach() operator.

See it below in action on ag-grid where all athletes with Julia in their name must be selected:

demo-for-each

const p = new PlaywrightFluent();

const url = `https://www.ag-grid.com/javascript-data-grid/keyboard-navigation/`;
const cookiesConsentButton = p
  .selector('#onetrust-button-group')
  .find('button')
  .withText('Accept All Cookies');

const gridContainer = 'div#myGrid';
const rowsContainer = 'div.ag-body-viewport div.ag-center-cols-container';
const rows = p.selector(gridContainer).find(rowsContainer).find('div[role="row"]');
const filter = p.selector(gridContainer).find('input[aria-label="Athlete Filter Input"]').parent();

await p
  .withBrowser('chromium')
  .withOptions({ headless: false })
  .withCursor()
  .navigateTo(url)
  .click(cookiesConsentButton)
  .switchToIframe('iframe[title="grid-keyboard-navigation"]')
  .hover(gridContainer)
  .click(filter)
  .typeText('Julia')
  .pressKey('Enter')
  .expectThat(rows.nth(1))
  .hasText('Julia');

await rows.forEach(async (row) => {
  const checkbox = row
    .find('input')
    .withAriaLabel('Press Space to toggle row selection (unchecked)')
    .parent();
  await p.click(checkbox);
});

Usage with Stories

This package provides a way to write tests as functional components called Story:

stories.ts

import { Story, StoryWithProps } from 'playwright-fluent';

export interface StartAppProps {
  browser: BrowserName;
  isHeadless: boolean;
  url: string;
}

// first story: start the App
export const startApp: StoryWithProps<StartAppProps> = async (p, props) => {
  await p
    .withBrowser(props.browser)
    .withOptions({ headless: props.isHeadless })
    .withCursor()
    .navigateTo(props.url);
}

// second story: fill in the form
export const fillForm: Story = async (p) => {
  await p
    .click(selector)
    .select(option)
    .in(customSelect)
    ...;
};

// threrd story: submit form
export const submitForm: Story = async (p) => {
  await p
    .click(selector);
};

// fourth story: assert
export const elementIsVisible: Story = async (p) => {
  await p
    .expectThatSelector(selector)
    .isVisible();
};

test.ts

import { startApp, fillForm } from 'stories';
import { PlaywrightFluent } from 'playwright-fluent';
const p = new PlaywrightFluent();

await p
  .runStory(startApp, { browser: 'chrome', isHeadless: false, url: 'http://example.com' })
  .runStory(fillForm)
  .close();

// Also methods synonyms are available to achieve better readability
const user = new PlaywrightFluent();
await user
  .do(startApp, { browser: 'chrome', isHeadless: false, url: 'http://example.com' })
  .and(fillForm)
  .attemptsTo(submitForm)
  .verifyIf(elementIsVisible)
  .close();

Usage with mocks

This fluent API provides a generic and simple infrastructure for massive request interception and response mocking.

This Mock API leverages the Playwright request interception infrastructure and will enable you to mock all HTTP requests in order to test the front in complete isolation from the backend.

Read more about the Fluent Mock API

This API is still a draft and is in early development, but stay tuned!

Contributing

Check out our contributing guide.

Resources

FAQ

Q: How does playwright-fluent relate to Playwright?

playwright-fluent is just a wrapper around the Playwright API. It leverages the power of Playwright by giving a Fluent API, that enables to consume the Playwright API with chainable actions and assertions. The purpose of playwright-fluent is to be able to write e2e tests in a way that makes tests more readable, reusable and maintainable.

Q: Can I start using playwright-fluent in my existing code base?

Yes you can.

import { PlaywrightFluent } from 'playwright-fluent';

// just create a new instance with playwright's browser and page instances
const p = new PlaywrightFluent(browser, page);

// you can also create a new instance with playwright's browser and frame instances
const p = new PlaywrightFluent(browser, frame);

// now you can use the fluent API

Q: Can I use Playwright together with the playwright-fluent?

Yes you can. To use the Playwright API, call the currentBrowser() and/or currentPage() methods exposed by the fluent API:

const browser = 'chromium';
const p = new PlaywrightFluent();
await p
  .withBrowser(browser)
  .emulateDevice('iPhone 6 landscape')
  .withCursor()
  .navigateTo('https://reactstrap.github.io/components/form/')
  ...;

// now if you want to use the playwright API from this point:
const browser = p.currentBrowser();
const page = p.currentPage();

// the browser and page objects are standard playwright objects
// so now you are ready to go by using the playwright API

Q: What can I do with the currently published npm package playwright-fluent?

The documentations:

reflect the current status of the development and are inline with the published package.

Q: Do you have some samples on how to use this library?

Yes, have a look to this demo project with jest, this demo project with cucumber-js v6 or this demo project with cucumber-js v7.

playwright-fluent's People

Contributors

dependabot[bot] avatar hdorgeval avatar osolomin90 avatar val1984 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

playwright-fluent's Issues

Check node version

**Is your feature request related to a problem?
We've just lost time to understand why our tests could not execute on some agents. It appears that node on those agents was still old 12 version, while playwright-fluent needs node 14 or more.

Describe the solution you'd like
Add an engine constraints in package.json to get a relevant message when an old node version is used.
engines check description

Api to handle dialogs and alerts.

Is your feature request related to a problem? Please describe.
Yes to check dialog or alert I have to switch on pure Playwright.
Describe the solution you'd like
a few more api functions to expect dialog or alert with expected text and a few functions to accept and dismiss them.
Describe alternatives you've considered
Currently I have to call currentPage() and use page.on('dialog', ...) but with TS it make me handle optional chaining) due to return type of currentPage().
Additional context
I am trying to wright tests without pure Playwright because it allows me to create very readable code plus Playwright-Fluent acts more like real user. I really like use this wrapper.

New assert methods.

Is your feature request related to a problem? Please describe.
I found that next assert: await playwrightFluent.expectThatSelector(SELECTOR).isVisible();
Will fail if element is out of viewport. But in most cases I need to check is element is visible any were and then I can scroll to it,
also this behaviour with viewport is not mentioned in method description.

Describe the solution you'd like
I would like to have two methods: isVisible() - checks if element visible in general and isVisibleInViewport() - which works as existing one.
Describe alternatives you've considered
isVisible() - works as it is. isDisplayed() - checks if element visible in general.

Read Playwright configuration file from fluent wrapper

Discussed in #27

Originally posted by rubyrocks-code October 31, 2022
Hello @hdorgeval

Thanks for putting this wonderful wrapper to Playwright.

I am curious if there's any workaround to allow the fluent wrapper to read the config values from playwright.config.ts files? I see you have withOptions exposed but not sure how to use it to allow the fluent wrapper to automatically capture screenshots on test failures. The native Playwright has this config value of 'screenshot' which could be set to only-on-failure to do the same. Wondering how we can achieve something similar with fluent.

It would be great to have the fluent wrapper read in by default the playwright.config files or take a command line argument to read a custom config file.

Thanks!

Select item by value

Is your feature request related to a problem? Please describe.
I would like to select an item by value and not by label

Describe the solution you'd like
Having a selectByValue that works like select

Add tracing.

Is your feature request related to a problem? Please describe.
Currently to turn on tracing feature I need to do this via pure playwright. My code:
const story = playwrightFluent
.withBrowser(props.browser)
.withOptions({headless: props.isHeadless, slowMo: 50, args: getBrowserArgs(props.browser)})
.withDefaultWaitOptions({stabilityInMilliseconds: 50, timeoutInMilliseconds: PLAYWRIGHT_TIMEOUT})
.withDefaultAssertOptions({stabilityInMilliseconds: 100, timeoutInMilliseconds: 3000})
.recordNetworkActivity({path: props.harPath})
.WithDialogs();
await getPage(story).context().tracing.start({screenshots: true, snapshots: true});

Describe the solution you'd like
Please add .withTrace() and stopTracing() methods to api and let them use original parameters.

Iframe api.

Is your feature request related to a problem? Please describe.
I use raw playwright to test service with several iframes, I would like to use Playwright-fluent but when I get frame content I can not continue use fluent api.

Describe the solution you'd like

  1. Methods to switch to frame and back.
  2. PlaywrightFluent constructor can accept Page | Frame.

Reusing a browser instance rather than starting browser for each tests

Is your feature request related to a problem? Please describe.
Starting a browser for each test takes time. We could make a test campaigns faster by reusing single browser instance.

Describe the solution you'd like
I would like to use one and only one browser instance and to create a context for each test.
However this could have pitfalls for those who try to use parallelism. We should have the choice between using a single instance or not.

BR

Franck

Dialog hangs browser and it can not be closed.

Describe the bug
In case when expectThatDialog fail due to assert then dialog window blocks browser close
To Reproduce
Steps to reproduce the behavior:

Actual script:
await playwright
.waitForDialog()
.expectThatDialog()
.hasMessage(error) // This line throw assert as expected
.acceptDialog();

// first hook after scenario
After(async function (this: ScenarioContext) {
if(this.playwright.isDialogOpened()){
await this.playwright.cancelDialog(); // This line close dialog
}
})

// second hook
After(async function (this: ScenarioContext, test: ITestCaseHookParameter) {
await this.attach(await scenarioContext.playwright.takeFullPageScreenshotAsBase64(), 'base64:image/png'); // This line throws "page.screenshot: Timeout 20000ms exceeded. at Object.takeFullPageScreenshotAsBase64"
await this.playwright.close();
})

Expected behavior
This should work)

Desktop (please complete the following information):

  • OS: OSX
  • Browser chromium

Thank you in advance for your efforts.

Method to close context and get video and HAR path.

Is your feature request related to a problem? Please describe.
Playwright allows to record video and network logs (HAR), those files save when context closes.
Describe the solution you'd like
It would be great to have api which allows get path to saved video and HAR files after context is closed.

Can not start chrome in docker

Describe the bug
withBrowser('chrome') do not work in docker.
To Reproduce
Environment - Dockerfile:
FROM mcr.microsoft.com/playwright:focal

WORKDIR tests

COPY package.json /tests

RUN npm install
RUN npx playwright install chrome --with-deps

COPY cucumber.ts /tests
COPY tsconfig.json /tests
COPY src /tests/src

ENTRYPOINT ["npm", "run", "test:pipeline"]

Script:
playwright
.withBrowser('chrome')
.withOptions({headless: props.isHeadless, slowMo: 50})
.withDefaultWaitOptions({stabilityInMilliseconds: 50, timeoutInMilliseconds: PLAYWRIGHT_TIMEOUT})
.withDefaultAssertOptions({stabilityInMilliseconds: 100, timeoutInMilliseconds: 3000})
.WithDialogs();

ERROR: browserType.launch: Failed to launch chromium because executable doesn't exist at google-chrome-stable

But it works with next script:
async function main() {
const browser = await playwright['chromium'].launch({
channel: 'chrome'
});
const page = await browser.newPage();
await page.goto('https://google.com');
await browser.close();
console.log("DONE!")
};
main();

Expected behavior
Both scripts should work with chrome

Additional context
Both scripts works as expected on my mac book pro with Big Sur.
Also I found that I can not add 'channel' property to withOptions(...).

[Question] Understanding the name

Hi! This looks like a great project. I'm curious to know why the package is called playwright-controller. I'm new to the Fluent pattern, and I'm probably missing something about the role of "controller"

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.