diff --git a/karate-core/README.md b/karate-core/README.md index 778c98a22..f51183737 100644 --- a/karate-core/README.md +++ b/karate-core/README.md @@ -117,10 +117,10 @@ retry() | waitFor() | waitForAny() - | waitForUrl() - | waitUntil() - | waitUntilText() - | waitUntilEnabled() + | waitForUrl() + | waitForText() + | waitForEnabled() + | waitUntil() | delay() | script() | scripts() @@ -814,6 +814,30 @@ Very handy for waiting for an expected URL change *and* asserting if it happened Also see [waits](#wait-api). +## `waitForText()` +This is just a convenience short-cut for `waitUntil(locator, "_.textContent.includes('" + expected + "')")` since it is so frequently needed. Note the use of the JavaScript [`String.includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) function to do a *text contains* match for convenience. The need to "wait until some text appears" is so common, and with this - you don't need to worry about dealing with white-space such as line-feeds and invisible tab characters. + +Of course, try not to use single-quotes within the string to be matched, or escape them using a back-slash (`\`) character. + +```cucumber +* waitForText('#eg01WaitId', 'APPEARED') +``` + +And if you really need to scan the whole page for some text, you can use this, but it is better to be more specific for better performance: + +```cucumber +* waitForText('body', 'APPEARED') +``` + +## `waitForEnabled()` +This is just a convenience short-cut for `waitUntil(locator, '!_.disabled')` since it is so frequently needed: + +```cucumber +And waitForEnabled('#someId').click() +``` + +Also see [waits](#wait-api). + ## `waitFor()` This is typically used for the *first* element you need to interact with on a freshly loaded page. Use this in case a [`submit()`](#submit) for the previous action is un-reliable, see the section on [`waitFor()` instead of `submit()`](#waitfor-instead-of-submit) @@ -876,7 +900,7 @@ Wait for the JS expression to evaluate to `true`. Will poll using the [retry()]( * waitUntil("document.readyState == 'complete'") ``` -## `waitUntil(locator,js)` +### `waitUntil(locator,js)` A very useful variant that takes a [locator](#locators) parameter is where you supply a JavaScript "predicate" function that will be evaluated *on* the element returned by the locator in the HTML DOM. Most of the time you will prefer the short-cut boolean-expression form that begins with an underscore (or "`!`"), and Karate will inject the JavaScript DOM element reference into a variable named "`_`". Here is a real-life example: @@ -887,12 +911,12 @@ Here is a real-life example: And waitUntil('.alert-message', "_.innerHTML.includes('Some Text')") ``` -## Karate vs the Browser +### Karate vs the Browser One thing you need to get used to is the "separation" between the code that is evaluated by Karate and the JavaScript that is sent to the *browser* (as a raw string) and evaluated. Pay attention to the fact that the [`includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) function you see in the above example - is pure JavaScript. The use of `includes()` is needed in this real-life example, because [`innerHTML()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) can return leading and trailing white-space (such as line-feeds and tabs) - which would cause an exact "`==`" comparison in JavaScript to fail. -But guess what - this example is baked into a Karate API, see [`waitUntilText()`](#waituntiltext). +But guess what - this example is baked into a Karate API, see [`waitForText()`](#waitfortext). For an example of how JavaScript looks like on the "Karate side" see [Function Composition](#function-composition). @@ -906,31 +930,7 @@ And waitUntil('#eg01WaitId', "_.innerHTML == 'APPEARED!'") And waitUntil('#eg01WaitId', '!_.disabled') ``` -Also see [`waitUtntilEnabled`](#waituntilenabled) which is the preferred short-cut for the last example above, also look at the examples for [chaining](#chaining) and then the section on [waits](#wait-api). - -## `waitUntilText()` -This is just a convenience short-cut for `waitUntil(locator, "_.textContent.includes('" + expected + "')")` since it is so frequently needed. Note the use of [`includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) for a "string contains" match for convenience. Because the need to "wait until some text appears" is so common, and you don't need to worry about dealing with white-space such as line-feeds and invisible tab characters. - -Of course, try not to use single-quotes within the string to be matched, or escape them using a back-slash (`\`) character. - -```cucumber -* waitUntilText('#eg01WaitId', 'APPEARED') -``` - -And if you really need to scan the whole page for some text, you can use this: - -```cucumber -* waitUntilText('body', 'APPEARED') -``` - -## `waitUntilEnabled()` -This is just a convenience short-cut for `waitUntil(locator, '!_.disabled')` since it is so frequently needed: - -```cucumber -And waitUntilEnabled('#someId').click() -``` - -Also see [waits](#wait-api). +Also see [`waitForEnabled()`](#waitforenabled) which is the preferred short-cut for the last example above, also look at the examples for [chaining](#chaining) and then the section on [waits](#wait-api). ### `waitUntil(function)` A *very* powerful variation of `waitUntil()` takes a full-fledged JavaScript function as the argument. This can loop until *any* user-defined condition and can use any variable (or Karate or [Driver JS API](#syntax)) in scope. The signal to stop the loop is to return any not-null object. And as a convenience, whatever object is returned, can be re-used in future steps. @@ -958,7 +958,7 @@ Then match searchResults contains 'karate-core/src/main/resources/karate-logo.pn Also see [waits](#wait-api). -### Function Composition +## Function Composition The above example can be re-factored in a very elegant way as follows, using Karate's [native support for JavaScript](https://github.com/intuit/karate#javascript-functions): ```cucumber @@ -1017,11 +1017,11 @@ Script | Description [`waitFor('#myId')`](#waitfor) | waits for an element as described above `retry(10).waitFor('#myId')` | like the above, but temporarily over-rides the settings to wait for a [longer time](#retry-actions), and this can be done for *all* the below examples as well [`waitForUrl('google.com')`](#waitforurl) | for convenience, this uses a string *contains* match - so for example you can omit the `http` or `https` prefix +[`waitForText('#myId', 'appeared')`](#waitfortext) | frequently needed short-cut for waiting until a string appears - and this uses a "string contains" match for convenience +[`waitForEnabled('#mySubmit')`](#waitforenabled) | frequently needed short-cut for `waitUntil(locator, '!_disabled')` [`waitForAny('#myId', '#maybe')`](#waitforany) | handle if an element may or *may not* appear, and if it does, handle it - for e.g. to get rid of an ad popup or dialog [`waitUntil(expression)`](#waituntil) | wait until *any* user defined JavaScript statement to evaluate to `true` in the browser [`waitUntil(function)`](#waituntilfunction) | use custom logic to handle *any* kind of situation where you need to wait, *and* use other API calls if needed -[`waitUntilText()`](#waituntiltext) | frequently needed short-cut for waiting until a string appears - and this uses a "string contains" match for convenience -[`waitUntilEnabled()`](#waituntilenabled) | frequently needed short-cut for `waitUntil(locator, '!_disabled')` Also see the examples for [chaining](#chaining). diff --git a/karate-core/src/main/java/com/intuit/karate/driver/Driver.java b/karate-core/src/main/java/com/intuit/karate/driver/Driver.java index 038cd3120..44f916a95 100644 --- a/karate-core/src/main/java/com/intuit/karate/driver/Driver.java +++ b/karate-core/src/main/java/com/intuit/karate/driver/Driver.java @@ -149,6 +149,14 @@ default String waitForUrl(String expected) { return getOptions().waitForUrl(this, expected); } + default Element waitForText(String locator, String expected) { + return waitUntil(locator, "_.textContent.includes('" + expected + "')"); + } + + default Element waitForEnabled(String locator) { + return waitUntil(locator, "!_.disabled"); + } + default Element waitForAny(String locator1, String locator2) { return getOptions().waitForAny(this, new String[]{locator1, locator2}); } @@ -161,18 +169,6 @@ default Element waitUntil(String locator, String expression) { return getOptions().waitUntil(this, locator, expression); } - default Element waitUntilEnabled(String locator) { - return waitUntil(locator, "!_.disabled"); - } - - default Element waitUntilText(String locator, String expected) { - return waitUntil(locator, "_.textContent.includes('" + expected + "')"); - } - - default Element waitUntilText(String expected) { - return waitUntil("document", "_.textContent.includes('" + expected + "')"); - } - default Object waitUntil(Supplier condition) { return getOptions().retry(() -> condition.get(), o -> o != null, "waitUntil (function)"); } diff --git a/karate-core/src/main/java/com/intuit/karate/driver/DriverElement.java b/karate-core/src/main/java/com/intuit/karate/driver/DriverElement.java index 6ba2f608f..cd758a475 100644 --- a/karate-core/src/main/java/com/intuit/karate/driver/DriverElement.java +++ b/karate-core/src/main/java/com/intuit/karate/driver/DriverElement.java @@ -104,7 +104,7 @@ public Element input(String value) { @Override public Element input(String[] values) { return driver.input(locator, values); - } + } @Override public Element select(String text) { @@ -135,14 +135,14 @@ public Element waitFor() { } @Override - public Element waitUntil(String expression) { - return driver.waitUntil(locator, expression); // will throw exception if not found + public Element waitForText(String text) { + return driver.waitForText(locator, text); } @Override - public Element waitUntilText(String text) { - return driver.waitUntilText(locator, text); - } + public Element waitUntil(String expression) { + return driver.waitUntil(locator, expression); // will throw exception if not found + } @Override public Object script(String expression) { diff --git a/karate-core/src/main/java/com/intuit/karate/driver/Element.java b/karate-core/src/main/java/com/intuit/karate/driver/Element.java index dcc83e603..9a56e9d57 100644 --- a/karate-core/src/main/java/com/intuit/karate/driver/Element.java +++ b/karate-core/src/main/java/com/intuit/karate/driver/Element.java @@ -61,7 +61,7 @@ public interface Element { Element waitUntil(String expression); - Element waitUntilText(String text); + Element waitForText(String text); Object script(String expression); diff --git a/karate-core/src/main/java/com/intuit/karate/driver/MissingElement.java b/karate-core/src/main/java/com/intuit/karate/driver/MissingElement.java index 53bd67feb..6d00b63b4 100644 --- a/karate-core/src/main/java/com/intuit/karate/driver/MissingElement.java +++ b/karate-core/src/main/java/com/intuit/karate/driver/MissingElement.java @@ -50,7 +50,7 @@ public boolean isExists() { @Override public boolean isEnabled() { return true; // hmm - } + } @Override public Element focus() { @@ -70,12 +70,12 @@ public Element click() { @Override public Element submit() { return this; - } + } @Override public Mouse mouse() { return null; - } + } @Override public Element input(String text) { @@ -86,7 +86,7 @@ public Element input(String text) { public Element input(String[] values) { return this; } - + @Override public Element select(String text) { return this; @@ -100,13 +100,13 @@ public Element select(int index) { @Override public Element switchFrame() { return this; - } + } @Override public Element delay(int millis) { driver.delay(millis); return this; - } + } @Override public Element waitFor() { @@ -114,14 +114,14 @@ public Element waitFor() { } @Override - public Element waitUntil(String expression) { + public Element waitForText(String text) { return this; } @Override - public Element waitUntilText(String text) { + public Element waitUntil(String expression) { return this; - } + } @Override public Object script(String expression) { diff --git a/karate-demo/src/test/java/driver/core/test-01.feature b/karate-demo/src/test/java/driver/core/test-01.feature index 22ff46661..1891b9c98 100644 --- a/karate-demo/src/test/java/driver/core/test-01.feature +++ b/karate-demo/src/test/java/driver/core/test-01.feature @@ -13,13 +13,16 @@ Scenario Outline: using # wait for very slow loading element And waitFor('#eg01WaitId') + # wait for text (is a string "contains" match for convenience) + And waitForText('#eg01WaitId', 'APPEARED') + And waitForText('body', 'APPEARED') + And waitForEnabled('#eg01WaitId') + # powerful variants of the above, call any js on the element And waitUntil('#eg01WaitId', "function(e){ return e.innerHTML == 'APPEARED!' }") And waitUntil('#eg01WaitId', "_.innerHTML == 'APPEARED!'") And waitUntil('#eg01WaitId', '!_.disabled') - And waitUntilText('#eg01WaitId', 'APPEARED') - And waitUntilText('body', 'APPEARED') - And waitUntilEnabled('#eg01WaitId') + And match script('#eg01WaitId', "function(e){ return e.innerHTML }") == 'APPEARED!' And match script('#eg01WaitId', '_.innerHTML') == 'APPEARED!' And match script('#eg01WaitId', '!_.disabled') == true