Skip to content

Commit

Permalink
[no-ci] improve documentation for ui drop-downs
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrthomas committed May 23, 2023
1 parent e7f6444 commit ce5b7a7
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 0 deletions.
64 changes: 64 additions & 0 deletions karate-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
| <a href="#chaining">Chaining</a>
| <a href="#function-composition">Function Composition</a>
| <a href="#script">Browser JavaScript</a>
| <a href="#looping">Looping</a>
| <a href="#dropdowns">Drop Downs</a>
| <a href="#debugging">Debugging</a>
| <a href="#retry">Retries</a>
| <a href="#wait-api">Waits</a>
Expand Down Expand Up @@ -1015,6 +1017,8 @@ The rare need to "double-click" is supported as a `doubleClick()` method:
* mouse('#myBtn').doubleClick()
```

Note that you can chain `mouse()` off an [`Element`](#chaining) which can be really convenient. Refer to the section on handling [drop downs](#drop-downs) for an example.

## `close()`
Close the page / tab.

Expand Down Expand Up @@ -1674,6 +1678,66 @@ This can be convenient in some cases, for example as an alternative to [Friendly

Also note that [`locate()`](#locate) and [`locateAll()`](#locateall) can be called *on* an [`Element`](#chaining), so that the "search scope" is limited to that `Element` and it's children.

# Looping

Looping over data is easy in Karate because of the natural way in which you can loop over JS arrays. And the API for UI testing is designed to return arrays, for example [`scriptAll()`](#scriptall) and [`locateAll()`](#locateall) turn out to be very useful.

For example, if you had a list of rows shown on the screen, and you wanted to click on all of them, you could do this:

```cucumber
* def rows = locateAll('.my-table tr button')
* rows.forEach(row => row.click())
```

If you wanted to do multiple actions per iteration of the loop, refer the next example for drop-downs.

# Drop Downs

This section exists here in the documentation because it is a frequently asked question, and most drop-down "select" experiences in the wild are powered by JavaScript which makes it harder. These are cases where [`select`](#select) will not work. Instead, most JS-powered drop-down components can be handled by using [`mouse()`](#mouse).

For example, consider this HTML which is using [Bootstrap](https://getbootstrap.com):

```html
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown button
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">First</a></li>
<li><a class="dropdown-item" href="#">Second</a></li>
<li><a class="dropdown-item" href="#">Third</a></li>
</ul>
</div>
```

The way to handle this is in two steps, first to click on the button to show the list of items, and then to click on one of the items:

```cucumber
* mouse('button').click()
* mouse('a.dropdown-item').click()
```

## Looping Over Elements

In the above example, what if we wanted to loop over each drop-down item and select each one. Here's how we can do it. Note how we can have multiple actions within the loop.

```cucumber
* def list = locateAll('a.dropdown-item')
* def fun =
"""
function(e) {
mouse('button').click();
e.mouse().click();
delay(2000);
}
"""
* list.forEach(fun)
```

Note how we could chain the `mouse()` method off an [`Element`](#chaining) instance, which is really convenient.

For the full working expanded example that shows all the concepts you need for looping over elements and handling drop-downs, refer to [this example](../karate-e2e-tests/src/test/java/driver/99_bootstrap.feature) and the [corresponding HTML](../karate-e2e-tests/src/test/java/driver/html/99_bootstrap.html).

# Debugging
You can use the [Visual Studio Karate entension](https://github.com/karatelabs/karate/wiki/IDE-Support#vs-code-karate-plugin) for stepping through and debugging a test. You can see a [demo video here](https://twitter.com/KarateDSL/status/1167533484560142336). We recommend that you get comfortable with this because it is going to save you lots of time. And creating tests may actually turn out to be fun !

Expand Down
45 changes: 45 additions & 0 deletions karate-e2e-tests/src/test/java/driver/99_bootstrap.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
Feature:

Background:
* driver serverUrl + '/99_bootstrap'

Scenario: using the mouse to click and select something in a js-powered dropdown

# can help for some pages that take time to load
* waitUntil("document.readyState == 'complete'")

# click on button to show dropdown options
* mouse('button').click()

# click on first option
* mouse('a.dropdown-item').click()

# asserted expected result
* match text('#container') == 'First'


Scenario: looping over data to repeat an action

# can help for some pages that take time to load
* waitUntil("document.readyState == 'complete'")

# get all possible drop-down elements
* def list = locateAll('a.dropdown-item')
* def results = []
* def fun =
"""
function(e) {
mouse('button').click();
e.mouse().click();
let result = text('#container').trim();
results.push(result);
delay(2000);
}
"""

# perform the loop to click on all dropdown items
* list.forEach(fun)

# assert at the end for the data collected
* match results == ['First', 'Second', 'Third']

24 changes: 24 additions & 0 deletions karate-e2e-tests/src/test/java/driver/html/99_bootstrap.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<title>Bootstrap</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
<script src="00.js"></script>
<link rel="icon" href="00.ico">
</head>
<body>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown button
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="karate.setHtml('container', this.textContent)">First</a></li>
<li><a class="dropdown-item" href="#" onclick="karate.setHtml('container', this.textContent)">Second</a></li>
<li><a class="dropdown-item" href="#" onclick="karate.setHtml('container', this.textContent)">Third</a></li>
</ul>
</div>
<div id="container"></div>
</body>
</html>

0 comments on commit ce5b7a7

Please sign in to comment.