From 29893429abe1a4b7e01228b6effbfe7b6f103c06 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 4 Apr 2024 22:09:55 +0200 Subject: [PATCH 1/2] Add test to check goml examples are valid --- package.json | 1 + src/commands.js | 15 ++++ src/index.js | 58 ++++++--------- src/utils.js | 10 +++ tools/api.js | 1 + tools/check-examples.js | 151 ++++++++++++++++++++++++++++++++++++++++ tools/utils.js | 4 ++ 7 files changed, 202 insertions(+), 38 deletions(-) create mode 100644 tools/check-examples.js diff --git a/package.json b/package.json index fbf8018d..487ca5c0 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "parser-test": "node tools/parser.js", "exported-test": "node tools/exported_items.js", "generated-test": "node tools/generated.js", + "examples-test": "node tools/check-examples.js", "lint": "eslint src tools", "lint-fix": "eslint --fix src tools" }, diff --git a/src/commands.js b/src/commands.js index 3fa26bda..a961d043 100644 --- a/src/commands.js +++ b/src/commands.js @@ -409,10 +409,25 @@ class ParserWithContext { } } +function parseTest(testName, testPath, logs, options, content) { + try { + const parser = new ParserWithContext(testPath, options, content); + return { + 'file': testName, + 'parser': parser, + }; + } catch (err) { + logs.append(testName + '... FAILED (exception occured)'); + logs.append(`${err.message}\n${err.stack}`); + } + return null; +} + const EXPORTS = { 'ParserWithContext': ParserWithContext, 'COLOR_CHECK_ERROR': consts.COLOR_CHECK_ERROR, 'ORDERS': ORDERS, + 'parseTest': parseTest, }; for (const func of Object.values(ORDERS)) { diff --git a/src/index.js b/src/index.js index b3c866a4..d4fce9fe 100755 --- a/src/index.js +++ b/src/index.js @@ -1,16 +1,21 @@ #!/usr/bin/env node const fs = require('fs'); -const PNG = require('pngjs').PNG; +const { PNG } = require('pngjs'); const parser = require('./parser.js'); -const commands_parser = require('./commands.js'); +const { COLOR_CHECK_ERROR, parseTest } = require('./commands.js'); const { innerParseScreenshot } = require('./commands/general.js'); -const utils = require('./utils.js'); -const Options = require('./options.js').Options; +const { + print, + add_warning, + loadPuppeteer, + getFileInfo, + escapeBackslahes, + extractFileNameWithoutExtension, +} = require('./utils.js'); +const { Options } = require('./options.js'); const {Debug, Logs} = require('./logs.js'); -const add_warn = utils.add_warning; const process = require('process'); -const print = utils.print; const path = require('path'); const consts = require('./consts.js'); const Module = require('module'); @@ -68,7 +73,7 @@ function save_failure(folderIn, failuresFolder, newImage) { try { fs.renameSync(path.join(folderIn, newImage), path.join(failuresFolder, newImage)); } catch (err) { - add_warn(`Error while trying to move files: "${err.message}"`); + add_warning(`Error while trying to move files: "${err.message}"`); // failed to move files... return false; } @@ -88,20 +93,6 @@ function getGlobalStyle(showText) { return css; } -function parseTest(testName, testPath, logs, options, content) { - try { - const parser = new commands_parser.ParserWithContext(testPath, options, content); - return { - 'file': testName, - 'parser': parser, - }; - } catch (err) { - logs.append(testName + '... FAILED (exception occured)'); - logs.append(`${err.message}\n${err.stack}`); - } - return null; -} - function createFolderIfNeeded(path) { if (path !== '' && fs.existsSync(path) === false) { try { @@ -176,15 +167,6 @@ async function runInstruction(loadedInstruction, page, extras) { await loadedInstruction(page, extras); } -function getFileInfo(context_parser, line, isExact = true) { - const file = utils.stripCommonPathsPrefix(context_parser.getCurrentFile()); - const file_s = file.length > 0 ? `\`${file}\` ` : ''; - if (isExact) { - return `${file_s}line ${line}`; - } - return `${file_s}around line ${line}`; -} - async function runAllCommands(loaded, logs, options, browser) { const commandLogs = new Logs(false); logs.append(loaded['file'] + '... '); @@ -217,7 +199,7 @@ async function runAllCommands(loaded, logs, options, browser) { 'variables': options.variables, 'setVariable': (varName, value) => { if (typeof value === 'string') { - value = utils.escapeBackslahes(value); + value = escapeBackslahes(value); } extras.variables[varName] = value; }, @@ -344,7 +326,7 @@ ${error.message}\n`; try { await runInstruction(loadedInstruction, page, extras); } catch (err) { // execution error - if (err === commands_parser.COLOR_CHECK_ERROR) { + if (err === COLOR_CHECK_ERROR) { error_log += `[ERROR] ${getFileInfo(context_parser, line_number)}: \ ${err}\n`; stopLoop = true; @@ -563,13 +545,13 @@ async function innerRunTests(logs, options, browser) { try { const needCloseBrowser = browser === null; if (browser === null) { - browser = await utils.loadPuppeteer(options); + browser = await loadPuppeteer(options); } const testsQueue = []; for (const file of allFiles) { total += 1; - const testName = utils.extractFileNameWithoutExtension(file); + const testName = extractFileNameWithoutExtension(file); const optionsCopy = options.clone(); // To make the Options type validation happy. @@ -652,7 +634,7 @@ async function innerRunTestCode( const needCloseBrowser = browser === null; if (browser === null) { - browser = await utils.loadPuppeteer(options); + browser = await loadPuppeteer(options); } const ret = await runAllCommands(loaded, logs, options, browser); @@ -754,7 +736,7 @@ async function runTest(testPath, extras = null) { } return innerRunTestCode( - utils.extractFileNameWithoutExtension(testPath), + extractFileNameWithoutExtension(testPath), testPath, optionsCopy, browser, @@ -811,7 +793,7 @@ if (require.main === module) { } process.exit(1); } - utils.loadPuppeteer(options).then(browser => { + loadPuppeteer(options).then(browser => { runTests({'options': options, 'browser': browser, 'showLogs': true}).then(x => { const [_output, nb_failures] = x; process.exit(nb_failures); @@ -835,6 +817,6 @@ if (require.main === module) { 'runTestCode': runTestCode, 'runTests': runTests, 'Options': Options, - 'loadBrowser': utils.loadPuppeteer, + 'loadBrowser': loadPuppeteer, }; } diff --git a/src/utils.js b/src/utils.js index c1b66274..e65ad51d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -177,6 +177,15 @@ function hasError(x) { return x.error !== undefined && x.error !== null; } +function getFileInfo(context_parser, line, isExact = true) { + const file = stripCommonPathsPrefix(context_parser.getCurrentFile()); + const file_s = file.length > 0 ? `\`${file}\` ` : ''; + if (isExact) { + return `${file_s}line ${line}`; + } + return `${file_s}around line ${line}`; +} + module.exports = { 'addSlash': addSlash, 'getCurrentDir': getCurrentDir, @@ -198,4 +207,5 @@ module.exports = { 'stripCommonPathsPrefix': stripCommonPathsPrefix, 'plural': plural, 'hasError': hasError, + 'getFileInfo': getFileInfo, }; diff --git a/tools/api.js b/tools/api.js index 9c2ea13e..e9f81604 100644 --- a/tools/api.js +++ b/tools/api.js @@ -1,5 +1,6 @@ const process = require('process'); process.env.debug_tests = '1'; // We enable this to get all items from `commands.js`. + const parserFuncs = require('../src/commands.js'); const Options = require('../src/options.js').Options; const { RESERVED_VARIABLE_NAME } = require('../src/utils.js'); diff --git a/tools/check-examples.js b/tools/check-examples.js new file mode 100644 index 00000000..71cb0208 --- /dev/null +++ b/tools/check-examples.js @@ -0,0 +1,151 @@ +const process = require('process'); +process.env.debug_tests = '1'; // We enable this to get all items from `commands.js`. + +const {Assert, plural, print} = require('./utils.js'); +const { getFileInfo } = require('../src/utils.js'); +const { parseTest, BEFORE_GOTO } = require('../src/commands.js'); +const { Options } = require('../src/options.js'); +const { Logs } = require('../src/logs.js'); +const fs = require('fs'); +const path = require('path'); + +function getAllFiles(folderPath, level) { + const files = []; + + if (level > 1) { + return files; + } + fs.readdirSync(folderPath).forEach(function(file) { + const fullPath = path.join(folderPath, file); + const stat = fs.lstatSync(fullPath); + if (stat.isFile()) { + if (file.endsWith('.md')) { + files.push(fullPath); + } + } else if (stat.isDirectory()) { + files.push(...getAllFiles(fullPath, level + 1)); + } + }); + return files; +} + +function checkContent(x, example, file, line, firstCommand) { + const logs = new Logs(false); + const fileAndLine = `${file}:${line}`; + const extra = BEFORE_GOTO.includes(firstCommand) ? '' : 'go-to: "/a"\n'; + + try { + const loaded = parseTest(fileAndLine, file, logs, new Options(), extra + example); + // FIXME: it's the same code as inside `src/index.js`. Better make a common function! + if (loaded === null) { + x.addError(`${fileAndLine}: Failed to parse example: ${logs.logs}`); + return; + } else if (loaded.parser.get_parser_errors().length !== 0) { + const errors = loaded.parser.get_parser_errors() + .map(e => `${getFileInfo(loaded.parser, e.line)}: ${e.message}`) + .join('\n'); + x.addError(`${fileAndLine}: Syntax errors: ${errors}`); + return; + } + const context_parser = loaded['parser']; + // Variables used in some examples. + context_parser.variables['variable'] = 1; + context_parser.variables['class_var'] = 1; + context_parser.variables['window_height'] = 1; + context_parser.variables['width'] = 1; + context_parser.variables['height'] = 1; + context_parser.variables['A_VARIABLE'] = 'a'; + context_parser.variables['DOC_PATH'] = './a'; + + for (let nb_commands = 0;; ++nb_commands) { + const command = context_parser.get_next_command(); + if (command === null) { + if (nb_commands === 0) { + x.addError(`${fileAndLine}: FAILED (No command to execute)`); + return; + } + break; + } + // FIXME: it's the same code as inside `src/index.js`. Better make a common function! + if (command['error'] !== undefined) { + x.addError( + `${fileAndLine}: [ERROR] ${getFileInfo(context_parser, command['line'])}: \ +${command['error']}`); + return; + } else if (command['errors'] !== undefined && command['errors'].length > 0) { + const error = command['errors'].map(e => { + return `[ERROR] ${getFileInfo(context_parser, e['line'])}: ${e['message']}`; + }).join('\n'); + x.addError(`${fileAndLine}: ${error}`); + return; + } + } + } catch (err) { + x.addError(`${fileAndLine}: ${err}`); + return; + } + x.addSuccess(); +} + +function checkFileExamples(x, filePath) { + const lines = fs.readFileSync(filePath, 'utf8').split('\n'); + + for (let i = 0, len = lines.length; i < len; ++i) { + const line = lines[i]; + + if (line.startsWith('```')) { + const lineStart = i + 1; + const block = []; + i += 1; + let command = null; + for (; i < len; ++i) { + if (lines[i].startsWith('```')) { + break; + } else if (command === null && lines[i].trim().length > 0) { + const c = lines[i].trim()[0]; + if (c >= 'a' && c <= 'z') { + command = lines[i].split(':')[0]; + } + } + block.push(lines[i]); + } + if (line === '```' || line === '```goml') { + checkContent(x, block.join('\n'), filePath, lineStart, command); + } + } + } +} + +async function checkExamples(x) { + x.startTestSuite('examples', false); + print('=> Starting code examples checks...'); + print(''); + + const files = getAllFiles(path.join(__dirname, '..'), 0); + for (const file of files) { + x.startTestSuite(file); + try { + checkFileExamples(x, file); + x.endTestSuite(); + } catch (err) { + x.endTestSuite(false, true); + } + } + + print(''); + print(`<= Ending ${x.getTotalRanTests()} ${plural('test', x.getTotalRanTests())} with ` + + `${x.getTotalErrors()} ${plural('error', x.getTotalErrors())}`); + + return x.getTotalErrors(); +} + +if (require.main === module) { + const x = new Assert(); + checkExamples(x).then(nbErrors => { + process.exit(nbErrors !== 0 ? 1 : 0); + }); +} else { + module.exports = { + 'check': checkExamples, + }; +} diff --git a/tools/utils.js b/tools/utils.js index d3a340a4..daccfca0 100644 --- a/tools/utils.js +++ b/tools/utils.js @@ -131,6 +131,10 @@ class Assert { this._incrError(); } + addSuccess() { + this._addTest(); + } + _addTest() { if (this.testSuite.length > 0) { this.testSuite[this.testSuite.length - 1].ranTests += 1; From 72578976ee35befd1c6b69da8555adc022b52191 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 4 Apr 2024 22:10:11 +0200 Subject: [PATCH 2/2] Fix invalid goml examples --- CONTRIBUTING.md | 2 +- README.md | 6 +-- goml-script.md | 136 ++++++++++++++++++++++++------------------------ 3 files changed, 71 insertions(+), 73 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8127314..59f6d75a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ The other tests are checks for the framework's code itself: For `ui` tests, you can filter which ones you want to run by passing the test name as argument: -``` +```console $ npm run ui-test debug.goml debug2.goml ``` diff --git a/README.md b/README.md index 5ba2e6f6..05198f5d 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ npm install browser-ui-test In case you can't install puppeteer "normally", you can give a try to `--unsafe-perm=true`: -```bash +```console $ npm install puppeteer --unsafe-perm=true ``` @@ -41,9 +41,9 @@ $ npm install puppeteer --unsafe-perm=true You can either use this framework by using it as dependency or running it directly. In both cases you'll need to write some `.goml` scripts. It looks like this: -``` +```goml go-to: "https://somewhere.com" // go to this url -text: ("#button", "hello") // set text of element #button +set-text: ("#button", "hello") // set text of element #button assert-text: ("#button", "hello") // check if #button element's text has been set to "hello" ``` diff --git a/goml-script.md b/goml-script.md index bf5dbb3a..00dbea40 100644 --- a/goml-script.md +++ b/goml-script.md @@ -38,14 +38,17 @@ text" In this framework, you can use variables defined through the `--variable` option or through your environment. For example, if you start your script with: -``` -A_VARIABLE=12 node src/index.js --test-folder tests/scripts/ --variable DOC_PATH tests/html_files --variable ANOTHER_ONE 42 +```bash +A_VARIABLE=12 node src/index.js \ + --test-folder tests/scripts/ \ + --variable DOC_PATH tests/html_files \ + --variable ANOTHER_ONE 42 ``` You will have three variables that you'll be able to use (since `A_VARIABLE` is available through your environment). Then, to use them in your scripts, you need to put their name between `|` characters: ``` -text: ("#an-element", |A_VARIABLE|) +set-text: ("#an-element", |A_VARIABLE|) ``` In here, it'll set "#an-element" element's text to "12". @@ -60,12 +63,12 @@ You can concatenate numbers and strings using the `+` sign: ``` store-value: (effect, ":hover") -text: ("element" + |effect|, "something " + 2 + " something else") +set-text: ("element" + |variable|, "something " + 2 + " something else") ``` Rules of concatenation are simple: if any of the element is a string, then it'll concatenate as a string. Examples: -``` +```ignore 1 + 2 + "a" // gives string "12a" 1 + 2 // gives number 3 ``` @@ -78,12 +81,12 @@ You can do more advanced things like: ``` store-value: (variable, 12) -12 == |variable| && variable + 1 < 14 +assert: 12 == |variable| && |variable| + 1 < 14 ``` The expression type will be evaluated depending on its components. If comparisons are being performed, it will be evaluated as booleans. Comparisons can only be performed between elements of the same type, except for numbers and strings which can be compared despite not being the same type. -``` +```ignore true != false // ok 1 != false // not ok "a" == "a" // ok @@ -93,7 +96,7 @@ true != false // ok You can also compare arrays, tuples and JSON dictionaries: -``` +```ignore ["a", "b"] == [1, 2] // computed as false ["a", "b"] != [1, 2] // computed as true (1, "a") == (1, "a", "e") // computed as false @@ -104,7 +107,7 @@ You can also compare arrays, tuples and JSON dictionaries: Some commands allow "object path" argument. In case you want to access children or deeper properties of an object, you will need to use an object path. For example, if you have something like: -``` +```json { "a": { "b": { @@ -116,13 +119,13 @@ Some commands allow "object path" argument. In case you want to access children You can access the lower levels using: -``` +```ignore "a"."b"."c" ``` Object paths also work with concatenation and variables: -``` +```ignore |var|."a" + "b"."c" ``` @@ -248,8 +251,8 @@ assert: ("#id > .class") // strictly equivalent // Will check that the boolean is true: store-attribute: ("#id > .class", {"class": class_var}) // We assume that the window's height is superior than 1000. -store-window-property: (window_height, "innerHeight") -assert: |class_var| == "class" && window_height > 1000 +store-window-property: {"innerHeight": window_height} +assert: |class_var| == "class" && |window_height| > 1000 // Or more simply: assert: true ``` @@ -268,8 +271,8 @@ assert-false: ("#id > .class") // strictly equivalent // Will check that the boolean is false: store-attribute: ("#id > .class", {"class": class_var}) // We assume that the window's height is superior than 1000. -store-window-property: (window_height, "innerHeight") -assert-false: |class_var| != "class" && window_height < 1000 +store-window-property: {"innerHeight": window_height} +assert-false: |class_var| != "class" && |window_height| < 1000 // Or more simply: assert-false: false ``` @@ -911,12 +914,12 @@ click: (10, 12) ``` // Clicking on `.element` but adding an offset of 10 on `X`. -click-with-offset: (".element", {"x": 10} +click-with-offset: (".element", {"x": 10}) // Same but with an XPath: -click-with-offset: ("//*[@class='element']", {"x": 10} +click-with-offset: ("//*[@class='element']", {"x": 10}) -click-with-offset: (".element", {"x": 10, "y": 3} -click-with-offset: (".element", {"y": 3} +click-with-offset: (".element", {"x": 10, "y": 3}) +click-with-offset: (".element", {"y": 3}) ``` #### compare-elements-attribute @@ -924,12 +927,12 @@ click-with-offset: (".element", {"y": 3} **compare-elements-attribute** command allows you to compare two DOM elements' attributes. By default, it's checking if they're equal Examples: ``` -compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX", ...]) -compare-elements-attribute: ("//element1", "element2", ["attribute1", "attributeX", ...]) +compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX"]) +compare-elements-attribute: ("//element1", "element2", ["attribute1", "attributeX"]) // The two previous commands can be written like this too: -compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX", ...], "=") -compare-elements-attribute: ("//element1", "element2", ["attribute1", "attributeX", ...], "=") +compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX"], "=") +compare-elements-attribute: ("//element1", "element2", ["attribute1", "attributeX"], "=") ``` Here is the list of the supported operators: `=`, `<`, `>`, `<=`, `>=`. @@ -937,8 +940,8 @@ Here is the list of the supported operators: `=`, `<`, `>`, `<=`, `>=`. Examples: ``` -compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX", ...], "<") -compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX", ...], ">=") +compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX"], "<") +compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX"], ">=") ``` #### compare-elements-attribute-false @@ -946,12 +949,12 @@ compare-elements-attribute: ("element1", "element2", ["attribute1", "attributeX" **compare-elements-attribute-false** command allows you to check that two DOM elements' attributes (and assuming the comparison will fail). If one of the elements doesn't exist, the command will fail. Examples: ``` -compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX", ...]) -compare-elements-attribute-false: ("//element1", "element2", ["attribute1", "attributeX", ...]) +compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX"]) +compare-elements-attribute-false: ("//element1", "element2", ["attribute1", "attributeX"]) // The two previous commands can be written like this too: -compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX", ...], "=") -compare-elements-attribute-false: ("//element1", "element2", ["attribute1", "attributeX", ...], "=") +compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX"], "=") +compare-elements-attribute-false: ("//element1", "element2", ["attribute1", "attributeX"], "=") ``` Here is the list of the supported operators: `=`, `<`, `>`, `<=`, `>=`. @@ -959,8 +962,8 @@ Here is the list of the supported operators: `=`, `<`, `>`, `<=`, `>=`. Examples: ``` -compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX", ...], "<") -compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX", ...], ">=") +compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX"], "<") +compare-elements-attribute-false: ("element1", "element2", ["attribute1", "attributeX"], ">=") ``` Another thing to be noted: if you don't care whether the selector exists or not either, take a look at the [`expect-failure`](#expect-failure) command too. @@ -970,8 +973,8 @@ Another thing to be noted: if you don't care whether the selector exists or not **compare-elements-css** command allows you to check that two DOM elements' CSS properties are equal. Examples: ``` -compare-elements-css: ("element1", "//element2", ["CSS property1", "CSS property2", ...]) -compare-elements-css: ("//element1", "element2", ["CSS property1", "CSS property2", ...]) +compare-elements-css: ("element1", "//element2", ["CSS property1", "CSS property2"]) +compare-elements-css: ("//element1", "element2", ["CSS property1", "CSS property2"]) ``` #### compare-elements-css-false @@ -979,8 +982,8 @@ compare-elements-css: ("//element1", "element2", ["CSS property1", "CSS property **compare-elements-css-false** command allows you to check that two DOM elements' CSS properties are different. If one of the elements doesn't exist, the command will fail. Examples: ``` -compare-elements-css-false: ("element1", "//element2", ["CSS property1", "CSS property2", ...]) -compare-elements-css-false: ("//element1", "element2", ["CSS property1", "CSS property2", ...]) +compare-elements-css-false: ("element1", "//element2", ["CSS property1", "CSS property2"]) +compare-elements-css-false: ("//element1", "element2", ["CSS property1", "CSS property2"]) ``` Another thing to be noted: if you don't care whether the selector exists or not either, take a look at the [`expect-failure`](#expect-failure) command too. @@ -1021,7 +1024,7 @@ compare-elements-position-false: ("element1", "element2", ["y", "x"]) ``` // Compare the X position. -compare-elements-position-near: ("//element1", "element2", {"x": 1})) +compare-elements-position-near: ("//element1", "element2", {"x": 1}) // Compare the Y position. compare-elements-position-near: ("element1", "//element2", {"y": 1}) // Compare both X and Y positions. @@ -1036,7 +1039,7 @@ compare-elements-position-near: ("element1", "element2", {"y": 3, "x": 1}) ``` // Compare the X position. -compare-elements-position-near-false: ("//element1", "element2", {"x": 1})) +compare-elements-position-near-false: ("//element1", "element2", {"x": 1}) // Compare the Y position. compare-elements-position-near-false: ("element1", "//element2", {"y": 1}) // Compare both X and Y positions. @@ -1052,11 +1055,11 @@ Another thing to be noted: if you don't care whether the selector exists or not **compare-elements-property** command allows you to check that two DOM elements' properties are equal. Examples: ``` -compare-elements-property: ("element1", "//element2", ["property1", "property2", ...]) -compare-elements-property: ("//element1", "element2", ["property1", "property2", ...]) +compare-elements-property: ("element1", "//element2", ["property1", "property2"]) +compare-elements-property: ("//element1", "element2", ["property1", "property2"]) // Object-paths are also supported: -compare-elements-property: ("//element1", "element2", ["property1"."sub", ...]) +compare-elements-property: ("//element1", "element2", ["property1"."sub"]) ``` #### compare-elements-property-false @@ -1064,11 +1067,11 @@ compare-elements-property: ("//element1", "element2", ["property1"."sub", ...]) **compare-elements-property-false** command allows you to check that two DOM elements' properties are different. If one of the elements doesn't exist, the command will fail. Examples: ``` -compare-elements-property-false: ("element1", "//element2", ["property1", "property2", ...]) -compare-elements-property-false: ("//element1", "element2", ["property1", "property2", ...]) +compare-elements-property-false: ("element1", "//element2", ["property1", "property2"]) +compare-elements-property-false: ("//element1", "element2", ["property1", "property2"]) // Object-paths are also supported: -compare-elements-property-false: ("//element1", "element2", ["property1"."sub", ...]) +compare-elements-property-false: ("//element1", "element2", ["property1"."sub"]) ``` Another thing to be noted: if you don't care whether the selector exists or not either, take a look at the [`expect-failure`](#expect-failure) command too. @@ -1194,7 +1197,7 @@ If you call `define-function` inside the function to overwrite it, it will not o ``` define-function: ( "fn1", - (), + [], block { // We overwrite "fn1". define-function: ( @@ -1256,14 +1259,14 @@ You can use it as follows too: ``` // text of "#elem" is "hello" -assert: ("#elem", "hello") -text: ("#elem", "not hello") +assert-text: ("#elem", "hello") +set-text: ("#elem", "not hello") // we want to check if the text changed (strangely but whatever) expect-failure: true -assert: ("#elem", "hello") +assert-text: ("#elem", "hello") // now we set it back to false to check the new text expect-failure: false -assert: ("#elem", "not hello") +assert-text: ("#elem", "not hello") ``` #### fail-on-js-error @@ -1271,7 +1274,7 @@ assert: ("#elem", "not hello") **fail-on-js-error** command sets changes the behaviour of `browser-ui-test` when a JS error occurs on the web page. By default, they are ignored. It overloads the value from the `--enable-on-js-error-fail` option. Example: ``` -# To enable the check: +// To enable the check: fail-on-js-error: true ``` @@ -1280,7 +1283,7 @@ fail-on-js-error: true **fail-on-request-error** command sets changes the behaviour of `browser-ui-test` when a request fails on the web page. By default, this option is enabled. It overloads the value from the `--disable-on-request-error-fail` option. Example: ``` -# To disable the check: +// To disable the check: fail-on-request-error: false ``` @@ -1366,7 +1369,7 @@ Please note that if no `timeout` is specified, the one from the [`timeout`](#tim **include** command loads the given path then runs it. If the path is not absolute, it'll be relative to the current file. The file is not loaded until the `include` command is run. Example: -``` +```ignore // current file: /bar/foo.goml include: "bar.goml" // It will load `/bar/bar.goml` include: "/babar/foo.goml" // It will load `/babar/foo.goml` @@ -1377,7 +1380,7 @@ include: "/babar/foo.goml" // It will load `/babar/foo.goml` **javascript** command enables/disables the javascript. If you want to render the page without javascript, don't forget to disable it before the call to `goto`! Examples: ``` -javascript: false // we disable it before using goto to have a page rendered without javascript +javascript: false // we disable it before using go-to to have a page rendered without javascript go-to: "https://somewhere.com" // rendering without javascript ``` @@ -1506,11 +1509,6 @@ scroll-to: (10, 12) **set-attribute** command allows to update an element's attribute. Example: ``` -set-attribute: ("#button", "attribute-name", "attribute-value") -// Same but with a XPath: -set-attribute: ("//*[@id='button']", "attribute-name", "attribute-value") - -// To set multiple attributes at once: set-attribute: ("#button", {"attribute name": "attribute value", "another": "x"}) // Same but with a XPath: set-attribute: ("//*[@id='button']", {"attribute name": "attribute value", "another": "x"}) @@ -1660,7 +1658,7 @@ show-text: true // text won't be invisible anymore ``` store-attribute: ("#button", {"id": variable_name}) -assert: |variable_name| == "button") +assert: |variable| == "button" // You can set multiple variables at once too: store-attribute: ("#button", {"id": variable_name, "class": another_variable}) @@ -1702,7 +1700,7 @@ For more information about variables, read the [variables section](#variables). **store-local-storage** command stores a value from the local storage into a variable. Examples: ``` -local-storage: {"key": "value"} +set-local-storage: {"key": "value"} store-local-storage: {"key": variable_name} assert-variable: (variable_name, "value") @@ -1718,8 +1716,8 @@ For more information about variables, read the [variables section](#variables). ``` store-position: ("#button", {"x": x}) -store-position: ("#button", {"y": var2}) -assert: |width| == 30 && |var2| == 10 +store-position: ("#button", {"y": variable}) +assert: |width| == 30 && |variable| == 10 // It could be written like this too: store-position: ("#button", {"x": x, "y": var2}) @@ -1752,11 +1750,11 @@ For more information about variables, read the [variables section](#variables). ``` store-size: ("#button", {"width": width}) -store-size: ("#button", {"height": var2}) -assert: |width| == 30 && |var2| == 10 +store-size: ("#button", {"height": height}) +assert: |width| == 30 && |height| == 10 // It could be written like this too: -store-size: ("#button", {"width": width, "height": var2}) +store-size: ("#button", {"width": width, "height": height}) // It also works for pseudo elements: store-size: ("#button::after", {"width": width, "height": var2}) @@ -1772,7 +1770,7 @@ For more information about variables, read the [variables section](#variables). **store-text** command stores an element's text into a variable. Examples: ``` -store-property: (variable_name, "#button") +store-property: ("#button", {"innerText": variable_name}) assert-variable: (variable_name, "click me") ``` @@ -2086,8 +2084,8 @@ instead. ``` // It'll write into the given element if it exists: -write: (".element", "text") -write: ("//*[@class='element']", "text") -write: ("#element", 13) // this is the keycode for "enter" -write: ("//*[@id='element']", 13) // this is the keycode for "enter" +write-into: (".element", "text") +write-into: ("//*[@class='element']", "text") +write-into: ("#element", 13) // this is the keycode for "enter" +write-into: ("//*[@id='element']", 13) // this is the keycode for "enter" ```