diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e6ccde47..02d181fb4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,191 @@
+## 3.5.12
+
+â¤ī¸ Thanks all to those who contributed to make this release! â¤ī¸
+
+đŠī¸ *Features*
+* feat: upgrade wdio (#4123) - by @KobeNguyenT
+
+ đŠī¸ With the release of WebdriverIO version `v8.14.0`, and onwards, all driver management hassles are now a thing of the past đ. Read more [here](https://webdriver.io/blog/2023/07/31/driver-management/).
+ One of the significant advantages of this update is that you can now get rid of any driver services you previously had to manage, such as
+ `wdio-chromedriver-service`, `wdio-geckodriver-service`, `wdio-edgedriver-service`, `wdio-safaridriver-service`, and even `@wdio/selenium-standalone-service`.
+
+For those who require custom driver options, fear not; WebDriver Helper allows you to pass in driver options through custom WebDriver configuration.
+If you have a custom grid, use a cloud service, or prefer to run your own driver, there's no need to worry since WebDriver Helper will only start a driver when there are no other connection information settings like hostname or port specified.
+
+Example:
+
+```js
+{
+ helpers: {
+ WebDriver : {
+ smartWait: 5000,
+ browser: "chrome",
+ restart: false,
+ windowSize: "maximize",
+ timeouts: {
+ "script": 60000,
+ "page load": 10000
+ }
+ }
+ }
+}
+```
+
+Testing Chrome locally is now more convenient than ever. You can define a browser channel, and WebDriver Helper will take care of downloading the specified browser version for you.
+For example:
+
+```js
+{
+ helpers: {
+ WebDriver : {
+ smartWait: 5000,
+ browser: "chrome",
+ browserVersion: '116.0.5793.0', // or 'stable', 'beta', 'dev' or 'canary'
+ restart: false,
+ windowSize: "maximize",
+ timeouts: {
+ "script": 60000,
+ "page load": 10000
+ }
+ }
+ }
+}
+```
+* feat: wdio with devtools protocol (#4105) - by @KobeNguyenT
+
+Running with devtools protocol
+
+```js
+{
+ helpers: {
+ WebDriver : {
+ url: "http://localhost",
+ browser: "chrome",
+ devtoolsProtocol: true,
+ desiredCapabilities: {
+ chromeOptions: {
+ args: [ "--headless", "--disable-gpu", "--no-sandbox" ]
+ }
+ }
+ }
+ }
+}
+```
+* feat: add a locator builder method withTextEquals() (#4100) - by @mirao
+
+Find an element with exact text
+```js
+locate('button').withTextEquals('Add');
+```
+* feat: waitForNumberOfTabs (#4124) - by @KobeNguyenT
+
+Waits for number of tabs.
+
+```js
+I.waitForNumberOfTabs(2);
+```
+* feat: I.say would be added to Test.steps array (#4145) - by @KobeNguyenT
+
+Currently `I.say` is not added into the `Test.steps` array. This PR aims to add this to steps array so that we could use it to print steps in ReportPortal for instance.
+
+
+
+đ *Bug Fixes*
+* fix: reduce the package size to 2MB (#4138) - by @KobeNguyenT
+* fix(webapi): see attributes on elements (#4147) - by @KobeNguyenT
+* fix: some assertion methods (#4144) - by @KobeNguyenT
+
+Improve the error message for `seeElement`, `dontSeeElement`, `seeElementInDOM`, `dontSeeElementInDOM`
+
+The current error message doesn't really help when debugging issue also causes some problem described in #4140
+
+Actual
+
+```
+ expected visible elements '[ELEMENT]' to be empty
+ + expected - actual
+
+ -[
+ - "ELEMENT"
+ -]
+ +[]
+```
+
+Updated
+
+```
+ Error: Element "h1" is still visible
+ at seeElementError (lib/helper/errors/ElementAssertion.js:9:9)
+ at Playwright.dontSeeElement (lib/helper/Playwright.js:1472:7)
+```
+
+* fix: css to xpath backward compatibility (#4141) - by @KobeNguyenT
+
+- [css-to-xpath](https://www.npmjs.com/package/css-to-xpath): old lib, which works perfectly unless you have hyphen in locator. (https://github.com/codeceptjs/CodeceptJS/issues/3563)
+- [csstoxpath](https://www.npmjs.com/package/csstoxpath): new lib, to solve the issue locator with hyphen but also have some [limitations](https://www.npmjs.com/package/csstoxpath#limitations)
+
+* fix: grabRecordedNetworkTraffics throws error when being called twice (#4143) - by @KobeNguyenT
+* fix: missing steps of test when running with workers (#4127) - by @KobeNguyenT
+
+```js
+Scenario('Verify getting list of users', async () => {
+let res = await I.getUserPerPage(2);
+res.data = []; // this line causes the issue
+await I.expectEqual(res.data.data[0].id, 7);
+});
+```
+at this time, res.data.data[0].id would throw undefined error and somehow the test is missing all its steps.
+
+* fix: process.env.profile when --profile isn't set in run-multiple mode (#4131) - by @mirao
+
+`process.env.profile` is the string "undefined" instead of type undefined when no --profile is specified in the mode "run-multiple"
+
+
+* fix: session doesn't respect the context options (#4111) - by @KobeNguyenT
+
+```js
+Helpers: Playwright
+Plugins: screenshotOnFail, tryTo, retryFailedStep, retryTo, eachElement
+
+Repro --
+[1] Starting recording promises
+Timeouts:
+âē [Session] Starting singleton browser session
+Reproduce issue
+I am on page "https://example.com"
+âē [Browser:Error] Failed to load resource: the server responded with a status of 404 ()
+âē [New Context] {}
+user1: I am on page "https://example.com"
+user1: I execute script () => {
+return { width: window.screen.width, height: window.screen.height };
+}
+sessionScreen is {"width":375,"height":667}
+â OK in 1890ms
+
+
+OK | 1 passed // 4s
+```
+
+* fix(plugin): retryTo issue (#4117) - by @KobeNguyenT
+ 
+
+* fix(types): CustomLocator typing broken for custom strict locators (#4120) - by @KobeNguyenT
+* fix: wrong output for skipped tests - by @KobeNguyenT
+* fix: no retry failed step after tryto block (#4103) - by @KobeNguyenT
+* fix: deprecate some JSON Wire Protocol commands (#4104) - by @KobeNguyenT
+
+deprecate some JSON Wire Protocol commands: `grabGeoLocation`, `setGeoLocation`
+
+* fix: cannot locate complicated locator (#4101) - by @KobeNguyenT
+
+Locator issue due to the lib changes
+
+```
+The locator locate(".ps-menu-button").withText("Authoring").inside(".ps-submenu-root:nth-child(3)") is translated to
+3.5.8: //*[contains(concat(' ', normalize-space(./@class), ' '), ' ps-menu-button ')][contains(., 'Authoring')][ancestor::*[(contains(concat(' ', normalize-space(./@class), ' '), ' ps-submenu-root ') and count(preceding-sibling::*) = 2)]] and works well
+3.5.11: //*[contains(@class, "ps-menu-button")][contains(., 'Authoring')][ancestor::*[3][contains(@class, "ps-submenu-root")]] and doesn't work (no clickable element found). Even if you test it in browser inspector, it doesn't work.
+```
+
## 3.5.11
â¤ī¸ Thanks all to those who contributed to make this release! â¤ī¸
diff --git a/README.md b/README.md
index 68dab791c..4d7ed4050 100644
--- a/README.md
+++ b/README.md
@@ -313,10 +313,10 @@ Thanks all to those who are and will have contributing to this awesome project!
-
-
+
+
diff --git a/lib/actor.js b/lib/actor.js
index 5b4332b68..a53a03fe4 100644
--- a/lib/actor.js
+++ b/lib/actor.js
@@ -13,15 +13,18 @@ const output = require('./output');
*/
class Actor {
/**
- * add print comment method`
+ * Print the comment on log. Also, adding a step in the `Test.steps` object
* @param {string} msg
* @param {string} color
* @inner
*
* â ī¸ returns a promise which is synchronized internally by recorder
*/
- say(msg, color = 'cyan') {
- return recorder.add(`say ${msg}`, () => {
+ async say(msg, color = 'cyan') {
+ const step = new Step('say', 'say');
+ step.status = 'passed';
+ return recordStep(step, [msg]).then(() => {
+ // this is backward compatibility as this event may be used somewhere
event.emit(event.step.comment, msg);
output.say(msg, `${color}`);
});
diff --git a/lib/helper/Appium.js b/lib/helper/Appium.js
index 2eae24a3d..81fcfa415 100644
--- a/lib/helper/Appium.js
+++ b/lib/helper/Appium.js
@@ -2,6 +2,7 @@ let webdriverio;
const fs = require('fs');
const axios = require('axios').default;
+const { v4: uuidv4 } = require('uuid');
const Webdriver = require('./WebDriver');
const AssertionFailedError = require('../assert/error');
@@ -1088,17 +1089,40 @@ class Appium extends Webdriver {
* Appium: support Android and iOS
*/
async performSwipe(from, to) {
- await this.browser.touchPerform([{
- action: 'press',
- options: from,
- }, {
- action: 'wait',
- options: { ms: 1000 },
- }, {
- action: 'moveTo',
- options: to,
- }, {
- action: 'release',
+ await this.browser.performActions([{
+ id: uuidv4(),
+ type: 'pointer',
+ parameters: {
+ pointerType: 'touch',
+ },
+ actions: [
+ {
+ duration: 0,
+ x: from.x,
+ y: from.y,
+ type: 'pointerMove',
+ origin: 'viewport',
+ },
+ {
+ button: 1,
+ type: 'pointerDown',
+ },
+ {
+ duration: 600,
+ type: 'pause',
+ },
+ {
+ duration: 600,
+ x: to.x,
+ y: to.y,
+ type: 'pointerMove',
+ origin: 'viewport',
+ },
+ {
+ button: 1,
+ type: 'pointerUp',
+ },
+ ],
}]);
await this.browser.pause(1000);
}
@@ -1128,7 +1152,7 @@ class Appium extends Webdriver {
yoffset = 100;
}
- return this.swipe(locator, 0, yoffset, speed);
+ return this.swipe(parseLocator.call(this, locator), 0, yoffset, speed);
}
/**
diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js
index 4c0575205..4f9816cbc 100644
--- a/lib/helper/Playwright.js
+++ b/lib/helper/Playwright.js
@@ -1805,7 +1805,7 @@ class Playwright extends Helper {
let optionToSelect = '';
try {
- optionToSelect = await el.locator('option', { hasText: option }).textContent();
+ optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim();
} catch (e) {
optionToSelect = option;
}
diff --git a/lib/step.js b/lib/step.js
index f57dc2dc8..c9184fad3 100644
--- a/lib/step.js
+++ b/lib/step.js
@@ -119,7 +119,9 @@ class Step {
}
let result;
try {
- result = this.helper[this.helperMethod].apply(this.helper, this.args);
+ if (this.helperMethod !== 'say') {
+ result = this.helper[this.helperMethod].apply(this.helper, this.args);
+ }
this.setStatus('success');
} catch (err) {
this.setStatus('failed');
diff --git a/package.json b/package.json
index cd2806746..8c2b144d8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codeceptjs",
- "version": "3.5.11",
+ "version": "3.5.12",
"description": "Supercharged End 2 End Testing Framework for NodeJS",
"keywords": [
"acceptance",
@@ -122,9 +122,9 @@
"@types/chai": "4.3.7",
"@types/inquirer": "9.0.3",
"@types/node": "20.10.7",
- "@wdio/sauce-service": "8.27.0",
+ "@wdio/sauce-service": "8.29.1",
"@wdio/selenium-standalone-service": "8.3.2",
- "@wdio/utils": "8.27.2",
+ "@wdio/utils": "8.28.8",
"@xmldom/xmldom": "0.8.10",
"apollo-server-express": "2.25.3",
"chai-as-promised": "7.1.1",
@@ -152,7 +152,7 @@
"runok": "0.9.3",
"sinon": "17.0.1",
"sinon-chai": "3.7.0",
- "testcafe": "3.3.0",
+ "testcafe": "3.5.0",
"ts-morph": "21.0.1",
"ts-node": "10.9.2",
"tsd-jsdoc": "2.5.0",
@@ -169,4 +169,4 @@
"npm": ">=5.6.0"
},
"es6": true
-}
+}
\ No newline at end of file
diff --git a/test/data/app/view/form/select_additional_spaces.php b/test/data/app/view/form/select_additional_spaces.php
new file mode 100755
index 000000000..6f202b7ac
--- /dev/null
+++ b/test/data/app/view/form/select_additional_spaces.php
@@ -0,0 +1,25 @@
+
+