From f27ca988bc6941e5627eea704a588983887c17fe Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 27 Sep 2024 13:56:56 +0200 Subject: [PATCH 1/3] Add `wait-for-text-false` command --- src/commands.js | 2 ++ src/commands/all.js | 1 + src/commands/wait.js | 23 +++++++++++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/commands.js b/src/commands.js index 30b9e3b8a..0a6bd5af2 100644 --- a/src/commands.js +++ b/src/commands.js @@ -113,6 +113,7 @@ const ORDERS = { 'wait-for-property-false': commands.parseWaitForPropertyFalse, 'wait-for-size': commands.parseWaitForSize, 'wait-for-text': commands.parseWaitForText, + 'wait-for-text-false': commands.parseWaitForTextFalse, 'wait-for-window-property': commands.parseWaitForWindowProperty, 'wait-for-window-property-false': commands.parseWaitForWindowPropertyFalse, 'write': commands.parseWrite, @@ -161,6 +162,7 @@ const FATAL_ERROR_COMMANDS = [ 'wait-for-property', 'wait-for-property-false', 'wait-for-text', + 'wait-for-text-false', 'write', 'write-into', ]; diff --git a/src/commands/all.js b/src/commands/all.js index 30539711f..ed1cb39b3 100644 --- a/src/commands/all.js +++ b/src/commands/all.js @@ -121,6 +121,7 @@ module.exports = { 'parseWaitForPropertyFalse': wait.parseWaitForPropertyFalse, 'parseWaitForSize': wait.parseWaitForSize, 'parseWaitForText': wait.parseWaitForText, + 'parseWaitForTextFalse': wait.parseWaitForTextFalse, 'parseWaitForWindowProperty': wait.parseWaitForWindowProperty, 'parseWaitForWindowPropertyFalse': wait.parseWaitForWindowPropertyFalse, 'parseWrite': input.parseWrite, diff --git a/src/commands/wait.js b/src/commands/wait.js index 8731a9f3b..8619ff88d 100644 --- a/src/commands/wait.js +++ b/src/commands/wait.js @@ -976,6 +976,17 @@ ${indentString(incr, 1)} // // * ("selector", "text") function parseWaitForText(parser) { + return parseWaitForTextInner(parser, false); +} + +// Possible inputs: +// +// * ("selector", "text") +function parseWaitForTextFalse(parser) { + return parseWaitForTextInner(parser, true); +} + +function parseWaitForTextInner(parser, waitFalse) { const identifiers = ['ALL', 'CONTAINS', 'ENDS_WITH', 'STARTS_WITH']; const ret = validator(parser, { kind: 'tuple', @@ -1045,10 +1056,17 @@ for (const elem of ${varName}) { }`; } + let comp = '==='; + let errorMessage = '"The following checks still fail: [" + err + "]"'; + if (waitFalse) { + comp = '!=='; + errorMessage = '"All checks still pass"'; + } + const [init, looper] = waitForElement(selector, varName, {checkAll: enabledChecks.has('ALL')}); const incr = incrWait(`\ const err = errors.join(", "); -throw new Error("The following checks still fail: [" + err + "]");`); +throw new Error(${errorMessage});`); const instructions = `\ ${init} @@ -1065,7 +1083,7 @@ const value = "${value}"; while (true) { ${indentString(looper, 1)} ${indentString(checker, 1)} - if (errors.length === 0) { + if (errors.length ${comp} 0) { break; } @@ -1303,6 +1321,7 @@ module.exports = { 'parseWaitForProperty': parseWaitForProperty, 'parseWaitForPropertyFalse': parseWaitForPropertyFalse, 'parseWaitForText': parseWaitForText, + 'parseWaitForTextFalse': parseWaitForTextFalse, 'parseWaitForWindowProperty': parseWaitForWindowProperty, 'parseWaitForWindowPropertyFalse': parseWaitForWindowPropertyFalse, 'parseWaitForSize': parseWaitForSize, From 48791e6159c7b3598e8aef2da5f2dd96c1bac229 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 27 Sep 2024 13:57:13 +0200 Subject: [PATCH 2/3] Add tests for `wait-for-text-false` command --- .../parseWaitForTextFalse/basic-1.toml | 53 +++++++++++++++++ .../parseWaitForTextFalse/basic-2.toml | 54 +++++++++++++++++ .../parseWaitForTextFalse/err-1.toml | 1 + .../parseWaitForTextFalse/err-2.toml | 1 + .../parseWaitForTextFalse/err-3.toml | 1 + .../parseWaitForTextFalse/err-4.toml | 1 + .../parseWaitForTextFalse/err-5.toml | 1 + .../parseWaitForTextFalse/err-6.toml | 1 + .../parseWaitForTextFalse/extra-1.toml | 58 +++++++++++++++++++ .../parseWaitForTextFalse/extra-2.toml | 53 +++++++++++++++++ .../parseWaitForTextFalse/extra-3.toml | 58 +++++++++++++++++++ .../parseWaitForTextFalse/xpath-1.toml | 53 +++++++++++++++++ tests/ui/wait-for-text-false.goml | 15 +++++ tests/ui/wait-for-text-false.output | 7 +++ tools/api.js | 5 ++ 15 files changed, 362 insertions(+) create mode 100644 tests/api-output/parseWaitForTextFalse/basic-1.toml create mode 100644 tests/api-output/parseWaitForTextFalse/basic-2.toml create mode 100644 tests/api-output/parseWaitForTextFalse/err-1.toml create mode 100644 tests/api-output/parseWaitForTextFalse/err-2.toml create mode 100644 tests/api-output/parseWaitForTextFalse/err-3.toml create mode 100644 tests/api-output/parseWaitForTextFalse/err-4.toml create mode 100644 tests/api-output/parseWaitForTextFalse/err-5.toml create mode 100644 tests/api-output/parseWaitForTextFalse/err-6.toml create mode 100644 tests/api-output/parseWaitForTextFalse/extra-1.toml create mode 100644 tests/api-output/parseWaitForTextFalse/extra-2.toml create mode 100644 tests/api-output/parseWaitForTextFalse/extra-3.toml create mode 100644 tests/api-output/parseWaitForTextFalse/xpath-1.toml create mode 100644 tests/ui/wait-for-text-false.goml create mode 100644 tests/ui/wait-for-text-false.output diff --git a/tests/api-output/parseWaitForTextFalse/basic-1.toml b/tests/api-output/parseWaitForTextFalse/basic-1.toml new file mode 100644 index 000000000..44f47c04d --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/basic-1.toml @@ -0,0 +1,53 @@ +instructions = [ + """const timeLimit = page.getDefaultTimeout(); +const timeAdd = 50; +let allTime = 0; +let parseWaitForText = null; +async function checkTextForElem(elem) { + return await elem.evaluate(e => { + const value = \"b\"; + const elemText = browserUiTestHelpers.getElemText(e, value); + const errors = []; + if (elemText !== value) { + errors.push(\"`\" + elemText + \"` isn't equal to `\" + value + \"`\"); + } + return errors; + }); +} +const value = \"b\"; +while (true) { + while (true) { + parseWaitForText = await page.$$(\"a\"); + if (parseWaitForText.length !== 0) { + parseWaitForText = parseWaitForText[0]; + break; + } + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + throw new Error(\"The CSS selector \\\"a\\\" was not found\"); + } + } + const errors = await checkTextForElem(parseWaitForText); + if (errors.length !== 0) { + break; + } + + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + const err = errors.join(\", \"); + throw new Error(\"All checks still pass\"); + } +}""", +] +wait = false +warnings = [ +] +checkResult = true diff --git a/tests/api-output/parseWaitForTextFalse/basic-2.toml b/tests/api-output/parseWaitForTextFalse/basic-2.toml new file mode 100644 index 000000000..69010fa04 --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/basic-2.toml @@ -0,0 +1,54 @@ +instructions = [ + """const timeLimit = page.getDefaultTimeout(); +const timeAdd = 50; +let allTime = 0; +let parseWaitForText = null; +async function checkTextForElem(elem) { + return await elem.evaluate(e => { + const value = \"b\"; + const elemText = browserUiTestHelpers.getElemText(e, value); + const errors = []; + if (elemText !== value) { + errors.push(\"`\" + elemText + \"` isn't equal to `\" + value + \"`\"); + } + return errors; + }); +} +const value = \"b\"; +while (true) { + while (true) { + parseWaitForText = await page.$$(\"a\"); + if (parseWaitForText.length !== 0) { + parseWaitForText = parseWaitForText[0]; + break; + } + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + throw new Error(\"The CSS selector \\\"a\\\" was not found\"); + } + } + const errors = await checkTextForElem(parseWaitForText); + if (errors.length !== 0) { + break; + } + + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + const err = errors.join(\", \"); + throw new Error(\"All checks still pass\"); + } +}""", +] +wait = false +warnings = [ + """Pseudo-elements (`::after`) don't have inner text so the check will be performed on the element itself""", +] +checkResult = true diff --git a/tests/api-output/parseWaitForTextFalse/err-1.toml b/tests/api-output/parseWaitForTextFalse/err-1.toml new file mode 100644 index 000000000..7ebf18c8f --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/err-1.toml @@ -0,0 +1 @@ +error = """expected a tuple, found nothing""" diff --git a/tests/api-output/parseWaitForTextFalse/err-2.toml b/tests/api-output/parseWaitForTextFalse/err-2.toml new file mode 100644 index 000000000..e5b8f7752 --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/err-2.toml @@ -0,0 +1 @@ +error = """expected a tuple, found `hello` (an ident)""" diff --git a/tests/api-output/parseWaitForTextFalse/err-3.toml b/tests/api-output/parseWaitForTextFalse/err-3.toml new file mode 100644 index 000000000..ee91ecfef --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/err-3.toml @@ -0,0 +1 @@ +error = """expected a selector, found `1` (a number) (first element of the tuple)""" diff --git a/tests/api-output/parseWaitForTextFalse/err-4.toml b/tests/api-output/parseWaitForTextFalse/err-4.toml new file mode 100644 index 000000000..ee91ecfef --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/err-4.toml @@ -0,0 +1 @@ +error = """expected a selector, found `1` (a number) (first element of the tuple)""" diff --git a/tests/api-output/parseWaitForTextFalse/err-5.toml b/tests/api-output/parseWaitForTextFalse/err-5.toml new file mode 100644 index 000000000..ed00941d6 --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/err-5.toml @@ -0,0 +1 @@ +error = """expected a string, found `2` (a number) (second element of the tuple)""" diff --git a/tests/api-output/parseWaitForTextFalse/err-6.toml b/tests/api-output/parseWaitForTextFalse/err-6.toml new file mode 100644 index 000000000..587f83cd2 --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/err-6.toml @@ -0,0 +1 @@ +error = """unexpected ident `A` (third element of the tuple). Allowed idents are: [`ALL`, `CONTAINS`, `ENDS_WITH`, `STARTS_WITH`]""" diff --git a/tests/api-output/parseWaitForTextFalse/extra-1.toml b/tests/api-output/parseWaitForTextFalse/extra-1.toml new file mode 100644 index 000000000..3d21a3563 --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/extra-1.toml @@ -0,0 +1,58 @@ +instructions = [ + """const timeLimit = page.getDefaultTimeout(); +const timeAdd = 50; +let allTime = 0; +let parseWaitForText = null; +async function checkTextForElem(elem) { + return await elem.evaluate(e => { + const value = \"b\"; + const elemText = browserUiTestHelpers.getElemText(e, value); + const errors = []; + if (elemText !== value) { + errors.push(\"`\" + elemText + \"` isn't equal to `\" + value + \"`\"); + } + return errors; + }); +} +const value = \"b\"; +while (true) { + while (true) { + parseWaitForText = await page.$$(\"a\"); + if (parseWaitForText.length !== 0) { + break; + } + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + throw new Error(\"The CSS selector \\\"a\\\" was not found\"); + } + } + let errors = []; + for (const elem of parseWaitForText) { + errors = await checkTextForElem(elem); + if (errors.length !== 0) { + break; + } + } + if (errors.length !== 0) { + break; + } + + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + const err = errors.join(\", \"); + throw new Error(\"All checks still pass\"); + } +}""", +] +wait = false +warnings = [ +] +checkResult = true diff --git a/tests/api-output/parseWaitForTextFalse/extra-2.toml b/tests/api-output/parseWaitForTextFalse/extra-2.toml new file mode 100644 index 000000000..13a74c387 --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/extra-2.toml @@ -0,0 +1,53 @@ +instructions = [ + """const timeLimit = page.getDefaultTimeout(); +const timeAdd = 50; +let allTime = 0; +let parseWaitForText = null; +async function checkTextForElem(elem) { + return await elem.evaluate(e => { + const value = \"b\"; + const elemText = browserUiTestHelpers.getElemText(e, value); + const errors = []; + if (!elemText.includes(value)) { + errors.push(\"`\" + elemText + \"` doesn't contain `\" + value + \"` (for CONTAINS check)\"); + } + return errors; + }); +} +const value = \"b\"; +while (true) { + while (true) { + parseWaitForText = await page.$$(\"a\"); + if (parseWaitForText.length !== 0) { + parseWaitForText = parseWaitForText[0]; + break; + } + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + throw new Error(\"The CSS selector \\\"a\\\" was not found\"); + } + } + const errors = await checkTextForElem(parseWaitForText); + if (errors.length !== 0) { + break; + } + + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + const err = errors.join(\", \"); + throw new Error(\"All checks still pass\"); + } +}""", +] +wait = false +warnings = [ +] +checkResult = true diff --git a/tests/api-output/parseWaitForTextFalse/extra-3.toml b/tests/api-output/parseWaitForTextFalse/extra-3.toml new file mode 100644 index 000000000..ea5f24ffe --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/extra-3.toml @@ -0,0 +1,58 @@ +instructions = [ + """const timeLimit = page.getDefaultTimeout(); +const timeAdd = 50; +let allTime = 0; +let parseWaitForText = null; +async function checkTextForElem(elem) { + return await elem.evaluate(e => { + const value = \"b\"; + const elemText = browserUiTestHelpers.getElemText(e, value); + const errors = []; + if (!elemText.includes(value)) { + errors.push(\"`\" + elemText + \"` doesn't contain `\" + value + \"` (for CONTAINS check)\"); + } + return errors; + }); +} +const value = \"b\"; +while (true) { + while (true) { + parseWaitForText = await page.$$(\"a\"); + if (parseWaitForText.length !== 0) { + break; + } + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + throw new Error(\"The CSS selector \\\"a\\\" was not found\"); + } + } + let errors = []; + for (const elem of parseWaitForText) { + errors = await checkTextForElem(elem); + if (errors.length !== 0) { + break; + } + } + if (errors.length !== 0) { + break; + } + + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + const err = errors.join(\", \"); + throw new Error(\"All checks still pass\"); + } +}""", +] +wait = false +warnings = [ +] +checkResult = true diff --git a/tests/api-output/parseWaitForTextFalse/xpath-1.toml b/tests/api-output/parseWaitForTextFalse/xpath-1.toml new file mode 100644 index 000000000..065e9c379 --- /dev/null +++ b/tests/api-output/parseWaitForTextFalse/xpath-1.toml @@ -0,0 +1,53 @@ +instructions = [ + """const timeLimit = page.getDefaultTimeout(); +const timeAdd = 50; +let allTime = 0; +let parseWaitForText = null; +async function checkTextForElem(elem) { + return await elem.evaluate(e => { + const value = \"b\"; + const elemText = browserUiTestHelpers.getElemText(e, value); + const errors = []; + if (elemText !== value) { + errors.push(\"`\" + elemText + \"` isn't equal to `\" + value + \"`\"); + } + return errors; + }); +} +const value = \"b\"; +while (true) { + while (true) { + parseWaitForText = await page.$$(\"::-p-xpath(//a)\"); + if (parseWaitForText.length !== 0) { + parseWaitForText = parseWaitForText[0]; + break; + } + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + throw new Error(\"The XPath \\\"//a\\\" was not found\"); + } + } + const errors = await checkTextForElem(parseWaitForText); + if (errors.length !== 0) { + break; + } + + await new Promise(r => setTimeout(r, timeAdd)); + if (timeLimit === 0) { + continue; + } + allTime += timeAdd; + if (allTime >= timeLimit) { + const err = errors.join(\", \"); + throw new Error(\"All checks still pass\"); + } +}""", +] +wait = false +warnings = [ +] +checkResult = true diff --git a/tests/ui/wait-for-text-false.goml b/tests/ui/wait-for-text-false.goml new file mode 100644 index 000000000..ad4751377 --- /dev/null +++ b/tests/ui/wait-for-text-false.goml @@ -0,0 +1,15 @@ +// This test ensures that the `wait-for-text-false` command is behaving like expected. +go-to: "file://" + |CURRENT_DIR| + "/" + |DOC_PATH| + "/elements.html" +set-timeout: 400 +// Shouldn't wait here since it's already the case. +wait-for-text-false: ("#js-call-attr", "hoy") +click: "#js-call-attr" +// Should wait for 250ms here. +wait-for-text-false: ("#js-call-attr", "hey") +click: "#js-create-elem" +// Should wait for 100 here (the element is created first). +wait-for-text-false: ("#created-one", "ding ding") +// This next one should fail. +wait-for-text-false: ("#js-call-attr", "hello") +// Check that the script won't stop running anything after the previous command failed. +assert-text: ("#js-call-attr", "hello bis") diff --git a/tests/ui/wait-for-text-false.output b/tests/ui/wait-for-text-false.output new file mode 100644 index 000000000..dd803315f --- /dev/null +++ b/tests/ui/wait-for-text-false.output @@ -0,0 +1,7 @@ +=> Starting doc-ui tests... + +wait-for-text-false... FAILED +[ERROR] `tests/ui/wait-for-text-false.goml` line 13: Error: All checks still pass: for command `wait-for-text-false: ("#js-call-attr", "hello")` + + +<= doc-ui tests done: 0 succeeded, 1 failed \ No newline at end of file diff --git a/tools/api.js b/tools/api.js index 108b37e73..59c0b2d64 100644 --- a/tools/api.js +++ b/tools/api.js @@ -2928,6 +2928,11 @@ const TO_CHECK = [ 'func': checkWaitForText, 'toCall': (x, e, name, o) => wrapper(parserFuncs.parseWaitForText, x, e, name, o), }, + { + 'name': 'wait-for-text-false', + 'func': checkWaitForText, + 'toCall': (x, e, name, o) => wrapper(parserFuncs.parseWaitForTextFalse, x, e, name, o), + }, { 'name': 'wait-for-window-property', 'func': checkWaitForObjectProperty, From 17cf400d36fa9f853fe8098b58523ed15db9b0fe Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 27 Sep 2024 14:00:44 +0200 Subject: [PATCH 3/3] Add documentation for `wait-for-text-false` command --- goml-script.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/goml-script.md b/goml-script.md index 04bc0ee4b..6d60a97fc 100644 --- a/goml-script.md +++ b/goml-script.md @@ -241,6 +241,7 @@ Here's the command list: * [`wait-for-property`](#wait-for-property) * [`wait-for-property-false`](#wait-for-property-false) * [`wait-for-text`](#wait-for-text) + * [`wait-for-text-false`](#wait-for-text-false) * [`wait-for-size`](#wait-for-size) * [`wait-for-window-property`](#wait-for-window-property) * [`wait-for-window-property-false`](#wait-for-window-property-false) @@ -2297,6 +2298,36 @@ wait-for-text: (".class", "hello", [ALL, CONTAINS]) wait-for-text: (".class", "hello", [ALL, STARTS_WITH, ENDS_WITH]) ``` +If you want to wait for any of the given element(s) to not have the expected text, take a look at [`wait-for-text-false`](#wait-for-text-false). + +#### wait-for-text-false + +**wait-for-text-false** command waits for any of the given element(s) to not have the expected text. It'll wait up to 30 seconds by default before failing (can be changed with the [`timeout`](#timeout) command). + +Examples: + +``` +wait-for-text-false: ("#element", "text") +// Same with an XPath: +wait-for-text-false: ("//*[@id='element']", "text") + +// If you want to wait for all elements matching this selector/XPath, use `ALL`: +wait-for-text-false: ("#id > .class", "hello", ALL) +wait-for-text-false: ("//*[@id='id']/*[@class='class']", "hello", ALL) +``` + +Apart from "ALL", you can also use "CONTAINS", "ENDS_WITH" and "STARTS_WITH" and even combine them if you want. Example: + +``` +wait-for-text-false: (".class", "hello", CONTAINS) +// To wait for all ".class" elements to contain "hello": +wait-for-text-false: (".class", "hello", [ALL, CONTAINS]) +// To wait for all ".class" elements to start and end with "hello": +wait-for-text-false: (".class", "hello", [ALL, STARTS_WITH, ENDS_WITH]) +``` + +If you want to wait for the given element(s) to have the expected text, take a look at [`wait-for-text`](#wait-for-text). + #### wait-for-window-property **wait-for-window-property** command waits for the window objects properties to have the expected values. It'll wait up to 30 seconds by default before failing (can be changed with the [`timeout`](#timeout) command).