Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update guides #505

Merged
merged 18 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 34 additions & 54 deletions docs/docs/add_assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,89 +7,69 @@ In this guide we show you how to add assertions to your tests. We assume that yo

## TL;DR

- [Use QA Wolf helpers](#use-qa-wolf-helpers) to create assertions:

```js
it("can click input", async () => {
await browser.click(selectors[2]);

// custom code starts
// verify that "Clear completed" text appears
const hasClearCompletedText = await browser.hasText("Clear completed");
expect(hasClearCompletedText).toBe(true);
// custom code ends
});
```

- [Use the Playwright API](#use-the-playwright-api) to create assertions:

```js
it('can click "Clear completed" button', async () => {
await browser.click(selectors[3]);

// custom code starts
// get current Playwright Page instance
const page = await browser.page();
// wait until no more todo items appear
await page.waitFor(() => !document.querySelector(".todo-list li"));
// custom code ends
await page.waitFor(() => {
jperl marked this conversation as resolved.
Show resolved Hide resolved
return document.body.innerText.includes('Clear completed');
});

await page.waitFor(() => !document.querySelector('.todo-list li'));
```

- The [interactive REPL](use_the_repl) lets you try out assertions while creating tests

## Use QA Wolf helpers
## Use the Playwright API

In this guide, we'll add assertions to a test on [TodoMVC](http://todomvc.com/examples/react).

You can add assertions as you create your test, since the [test code is generated](create_a_test#review-test-code) as you use your application. The [interactive REPL](use_the_repl) can be helpful in trying out code.

The first assertion we will add is to check that the text `"Clear completed"` appears after we complete our todo.

We'll use QA Wolf's [`browser.hasText` method](api/browser_context/has_text) to verify that the text appears. This method automatically waits for the given text to appear on the page. It returns `true` if the text is found, and `false` if the text does not appear before timing out.
The first assertion we will add is to check that the text `"Clear completed"` appears after we complete our todo. We'll do this using Playwright's [`page.waitFor` method](https://github.com/microsoft/playwright/blob/master/docs/api.md#framewaitforselectororfunctionortimeout-options-args), which waits for the specified function to return `true`. Specifically, we will pass it a function that waits until the text `"Clear completed"` appears.

In our test code, let's update the following step where we click to complete the todo. We'll call `browser.hasText("Clear completed")`, assign the result to a variable called `hasClearCompletedText`, and assert that `hasClearCompletedText` is `true`. See [Jest documentation](https://jestjs.io/docs/en/expect) to learn more about assertions.
In our test code, let's call `page.waitFor`, passing it a function that returns whether the [`document.body`](https://developer.mozilla.org/en-US/docs/Web/API/Document/body)'s [`innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText) includes `"Clear completed"`. Our test will wait until this funciton returns `true`. If the function never returns `true` before timing out, the test will fail.

```js
it("can click input", async () => {
await browser.click(selectors[2]);

test('myFirstTest', async () => {
await qawolf.repl({ selectors });
await page.goto('http://todomvc.com/examples/react');
await page.click(selectors['0_what_needs_to_b_input']);
await page.type(selectors['1_what_needs_to_b_input'], 'create test!');
await page.press(selectors['2_what_needs_to_b_input'], 'Enter');
await page.click(selectors['3_input']);
// custom code starts
// verify that "Clear completed" text appears
const hasClearCompletedText = await browser.hasText("Clear completed");
expect(hasClearCompletedText).toBe(true);
await page.waitFor(() => {
return document.body.innerText.includes('Clear completed');
});
// custom code ends
await page.click(selectors['4_button']);
});
```

If you run the test again (`npx qawolf test myTestName`), you'll see that it still passes.

See our [API documentation](api/table_of_contents) for a full list of QA Wolf helpers.

## Use the Playwright API

In addition to QA Wolf helpers, you also have full access to the [Playwright API](https://github.com/microsoft/playwright/blob/master/docs/api.md) and the [Jest API](https://jestjs.io/docs/en/expect) in your test code.

Next we'll add an assertion that our todo is no longer visible after we clear completed todos. In terms of the [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model), this means that there should be no todo `li` elements under the todo `ul` with the [class](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors) `"todo-list"`.

The Playwright API provides a [method called `page.waitFor`](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagewaitforselectororfunctionortimeout-options-args) that takes a predicate function and waits until the function returns `true`. We'll call this method, passing it a function to check that there are no todo items left on the page.

Putting it all together, after our test clicks the "Clear completed" button to clear completed todos, we will verify that the todos disappear from the page. We'll do this by:

1. Calling [`browser.page`](api/browser_context/page) to get the current Playwright `Page` instance
2. Calling [`page.waitFor`](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagewaitforselectororfunctionortimeout-options-args), passing it a function that checks to see that there are no elements that match the [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) `.todo-list li`.
We'll call `page.waitFor`, passing it a function that returns `true` when no elements match the [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) `".todo-list li"`. We use the [`document.querySelector` method](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) to check if an element matches our selector.

The last step of our test code now looks like this:
Our test now looks like this:

```js
it('can click "Clear completed" button', async () => {
await browser.click(selectors[3]);

test('myFirstTest', async () => {
await qawolf.repl({ selectors });
await page.goto('http://todomvc.com/examples/react');
await page.click(selectors['0_what_needs_to_b_input']);
await page.type(selectors['1_what_needs_to_b_input'], 'create test!');
await page.press(selectors['2_what_needs_to_b_input'], 'Enter');
await page.click(selectors['3_input']);
// custom code starts
await page.waitFor(() => {
return document.body.innerText.includes('Clear completed');
});
// custom code ends
await page.click(selectors['4_button']);
// custom code starts
// get current Playwright Page instance
const page = await browser.page();
// wait until no more todo items appear
await page.waitFor(() => !document.querySelector(".todo-list li"));
await page.waitFor(() => !document.querySelector('.todo-list li'));
// custom code ends
});
```
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/api/components/qawolf/ArgumentRepl.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ function ArgumentRepl() {
description={
<React.Fragment>
Pass variables you want to access in the REPL. Includes the{' '}
<a href="https://github.com/microsoft/playwright/blob/master/docs/api.md#class-browser">
Playwright <code>Browser</code>
<a href="https://github.com/microsoft/playwright/blob/master/docs/api.md#class-browsercontext">
Playwright <code>BrowserContext</code>
</a>{' '}
instance by default.
instance and <code>qawolf</code> module by default.
</React.Fragment>
}
name="context"
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/api/environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Click on this element:
The generated code will be:

```js
await browser.click({ css: "[my-attribute='search']" });
await page.click("[my-attribute='search']");
```

## QAW_BROWSER
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/api/qawolf/repl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,22 @@ Pause a running test or script and open the [interactive REPL](../../use_the_rep

### Examples

Call `repl` in your test code, passing whatever values you want to access in the REPL. The [Playwright `Browser`](https://github.com/microsoft/playwright/blob/master/docs/api.md#class-browser) instance (`browser`) is included by default, so you do not need to include it again:
Call `repl` in your test code, passing whatever values you want to access in the REPL. The [Playwright `BrowserContext`](https://github.com/microsoft/playwright/blob/master/docs/api.md#class-browsercontext) instance (`context`) and `qawolf` are included by default, so you do not need to include them again:

```js
const qawolf = require('qawolf');
const selectors = require('../selectors/myTestName.json');
// ...

test('myTestName', async () => {
await qawolf.repl({ selectors }); // already includes browser
await qawolf.repl({ selectors }); // already includes qawolf and context
});
```

Run your test:
Run your test with the `--repl` flag:

```bash
npx qawolf test myTestName
npx qawolf test --repl myTestName
```

Your test will run until `repl` is called, at which point the test will pause and the [interactive REPL](../../use_the_repl) will open. After you exit the REPL by calling `.exit`, the test will continue. You can call `repl` any number of times in your test.
92 changes: 26 additions & 66 deletions docs/docs/change_input_values.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,111 +5,71 @@ title: 📮 Change Input Values

## TL;DR

- [QA Wolf captures type actions](#keyboard-events-overview) when you create tests
- You can [change input values](#change-input-values) in your test code:

```js
it('can type into "What needs to be done?" input', async () => {
// change this
await browser.type(selectors[0], "create test!");
// to this
await browser.type(selectors[0], "update test!");
});
// change this
await page.type(selectors['1_what_needs_to_b_input'], 'create test!');
// to this
await page.type(selectors['1_what_needs_to_b_input'], ':)');
```

- **Always replace sensitive values like passwords [with environment variables](#use-environment-variables)**:

```js
it('can type into "What needs to be done?" input', async () => {
// change this
await browser.type(selectors[0], "secret value");
// to this
await browser.type(selectors[0], process.env.TODO_VALUE);
});
```

## Keyboard events overview

When you create a test, QA Wolf converts your `type` actions to test code. This code includes both the element that you typed into, as well as the value you typed. See [our selectors overview](use_custom_selectors#selectors-overview) to learn more about how element selectors work.

To capture the values you type, QA Wolf listens for `keydown` and `keyup` events so it can perfectly match the original keystrokes. This allows us to support special keys like `Tab`, `Enter`, and hotkeys.

These keystrokes are then included in your test code as the second argument to [`browser.type`](api/browser_context/type). This argument is the value that will be typed when your test is run. If no special characters are found, the keystrokes are simplified to a plain [`string`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) for human readability. Otherwise, they are included in the test code as is.

Below we show sample generated code after typing into an input:

```js
// original key events
await browser.type(
selectors[0],
"↓KeyL↑KeyL↓KeyA↓KeyU↑KeyA↓KeyR↑KeyU↑KeyR↓KeyA↑KeyA"
);

// simplified
await browser.type(selectors[0], "laura");
```

When a special key like `Enter` is pressed, all keyboard events are included in the generated test code. This allows the correct keystrokes to be used when running your test:

```js
await browser.type(selectors[1], "↓Enter↑Enter");
// change this
await page.type(selectors['1_what_needs_to_b_input'], 'secret value');
// to this
await page.type(selectors['1_what_needs_to_b_input'], process.env.TODO_VALUE);
```

## Change input values

When capturing `type` actions, whatever you originally typed will be included in the test by default. You may want to change your test code to use a different value than the one you originally typed.
When QA Wolf captures your `type` actions, whatever you originally typed is included in the test by default. You may want to change your test code to use a different value than the one you originally typed.

In an example test on [TodoMVC](http://todomvc.com/examples/react), we typed `"create test!"` as our todo item in the first step of our test. The following code was then generated:

```js
it('can type into "What needs to be done?" input', async () => {
await browser.type(selectors[0], "create test!");
});
await page.type(selectors['1_what_needs_to_b_input'], 'create test!');
```

However, you may want to change this input value to something else. The simplest way to do this is to change the second argument to [`browser.type`](api/browser_context/type) from the value you originally typed to your desired value:
However, you may want to change this input value to something else. The simplest way to do this is to change the second argument to [`page.type`](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagetypeselector-text-options) from the value you originally typed to your desired value:
jperl marked this conversation as resolved.
Show resolved Hide resolved

```js
it('can type into "What needs to be done?" input', async () => {
// change this
await browser.type(selectors[0], "create test!");
// to this
await browser.type(selectors[0], "update test!");
});
// change this
await page.type(selectors['1_what_needs_to_b_input'], 'create test!');
// to this
await page.type(selectors['1_what_needs_to_b_input'], ':)');
```

Note that you can edit your code [as your test is created](create_a_test#review-test-code), or after you've completed it.

If you run the test again, you'll notice that it now types the updated value (`"update test!"` in our example) in the edited step.
If you run the test again, you'll notice that it now types the updated value (`":)"` in our example) in the edited step.

You can also configure your tests to input dynamic values with [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) code. In the example below we type the current date as our todo item. To get the current date, we call the [`toString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString) method on a new [`Date` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date):

```js
it('can type into "What needs to be done?" input', async () => {
// change this
await browser.type(selectors[0], "create test!");
// to this
await browser.type(selectors[0], new Date().toString());
});
// change this
await page.type(selectors['1_what_needs_to_b_input'], 'create test!');
// to this
await page.type(selectors['1_what_needs_to_b_input'], new Date().toString());
```

## Use environment variables

Rather than pass an input value directly to [`browser.type`](api/browser_context/type), you can use an environment variable. Update the test code to use `process.env.YOUR_ENV_VARIABLE` (`TODO_VALUE` in the example below):
Rather than pass an input value directly to [`page.type`](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagetypeselector-text-options), you can use an environment variable. Update the test code to use `process.env.YOUR_ENV_VARIABLE` (`TODO_VALUE` in the example below):

```js
it('can type into "What needs to be done?" input', async () => {
// change this
await browser.type(selectors[0], "secret value");
// to this
await browser.type(selectors[0], process.env.TODO_VALUE);
});
// change this
await page.type(selectors['1_what_needs_to_b_input'], 'secret value');
// to this
await page.type(selectors['1_what_needs_to_b_input'], process.env.TODO_VALUE);
```

You can then run your test again, passing the appropriate environment variable. It will now type the value of your environment variable in the first step:

```bash
TODO_VALUE="create environment variable!" npx qawolf test myTestName
TODO_VALUE="hello env variable!" npx qawolf test myTestName
```

**You should always replace sensitive values like passwords with environment variables.** Use the above example as a reference for updating the test code and running it locally. When running tests in CI, be sure to [update your configuration to include the environment variable](run_tests_in_ci#use-environment-variables).
Expand Down
34 changes: 6 additions & 28 deletions docs/docs/contribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,12 @@ Once you've made your changes, make a pull request to the [`qawolf` repository](

If you have any questions please [chat with us on Gitter](https://gitter.im/qawolf/community)!

## Development environment

QAWolf is organized as a mono-repo of packages using [lerna](https://github.com/lerna/lerna).

Run bootstrap to install, build, and link the dependencies. You should re-run this everytime you change a package's dependencies.

```sh
npm run bootstrap
```

Run the the watchers to keep the `/lib` folders up to date.

```sh
cd ./qawolf
npm run watch

cd ./qawolf/packages/web
npm run watch
```

## What are all of these packages?!

[@qawolf/web](https://github.com/qawolf/qawolf/tree/master/packages/web) is a JS library that runs inside the browser. It has helpers to auto-wait for elements and assertions, and a Recorder to collect user interaction events.

[@qawolf/browser](https://github.com/qawolf/qawolf/tree/master/packages/browser) is a wrapper around Playwright that injects the `@qawolf/web` library and exposes helpers to use it. It switches to the page of a selector, waits for requests to finish, and manages keyboard interactions.

[@qawolf/build-workflow](https://github.com/qawolf/qawolf/tree/master/packages/build-workflow) converts user interaction events into a workflow of steps to take.

[@qawolf/build-code](https://github.com/qawolf/qawolf/tree/master/packages/build-code) builds a test or script from a workflow.
We maintain several packages under the [`qawolf` organzation](https://github.com/qawolf). Feel free to contribute to any of them!

[@qawolf/cli](https://github.com/qawolf/qawolf/tree/master/packages/cli) ties everything together into commands.
- [qawolf](https://github.com/qawolf/qawolf) is a Node.js library for creating Playwright/Jest tests and scripts
- [playwright-ci](https://github.com/qawolf/playwright-ci) provides a CLI to set up tests in CI
- [playwright-html-selector](https://github.com/qawolf/playwright-html-selector) is a custom selector engine for Playwright that finds elements by their HTML
- [playwright-utils](https://github.com/qawolf/playwright-utils) contains utility functions to help with Playwright (for example, save debug artifacts and scroll)
- [playwright-video](https://github.com/qawolf/playwright-video) saves a video of a Playwright page