Skip to content

Commit

Permalink
feat: timeout for grabNumberOfVisibleElements
Browse files Browse the repository at this point in the history
  • Loading branch information
kobenguyent committed Sep 4, 2024
1 parent 469d624 commit c307fa4
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 17 deletions.
2 changes: 2 additions & 0 deletions docs/helpers/Appium.md
Original file line number Diff line number Diff line change
Expand Up @@ -1075,11 +1075,13 @@ Resumes test execution, so **should be used inside async function with `await`**

```js
let numOfElements = await I.grabNumberOfVisibleElements('p');
let numOfElementsWithWait = await I.grabNumberOfVisibleElements('p', 2); // timeout applied
```

#### Parameters

- `locator` **([string][5] \| [object][11])** located by CSS|XPath|strict locator.
- `sec` **[number][10]?** (optional, `1` by default) time in seconds to wait

Returns **[Promise][6]<[number][10]>** number of visible elements

Expand Down
2 changes: 2 additions & 0 deletions docs/helpers/Playwright.md
Original file line number Diff line number Diff line change
Expand Up @@ -1276,11 +1276,13 @@ Resumes test execution, so **should be used inside async function with `await`**

```js
let numOfElements = await I.grabNumberOfVisibleElements('p');
let numOfElementsWithWait = await I.grabNumberOfVisibleElements('p', 2); // timeout applied
```

#### Parameters

- `locator` **([string][9] | [object][6])** located by CSS|XPath|strict locator.
- `sec` **[number][20]?** (optional, `1` by default) time in seconds to wait

Returns **[Promise][22]<[number][20]>** number of visible elements

Expand Down
3 changes: 2 additions & 1 deletion docs/helpers/Puppeteer.md
Original file line number Diff line number Diff line change
Expand Up @@ -1125,16 +1125,17 @@ Resumes test execution, so **should be used inside async function with `await`**
```js
let numOfElements = await I.grabNumberOfVisibleElements('p');
let numOfElementsWithWait = await I.grabNumberOfVisibleElements('p', 2); // timeout applied
```
#### Parameters
- `locator` **([string][6] | [object][4])** located by CSS|XPath|strict locator.
- `sec` **[number][11]?** (optional, `1` by default) time in seconds to wait
Returns **[Promise][14]<[number][11]>** number of visible elements
This action supports [React locators](https://codecept.io/react#locators)
Expand Down
2 changes: 2 additions & 0 deletions docs/helpers/TestCafe.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,13 @@ Resumes test execution, so **should be used inside async function with `await`**

```js
let numOfElements = await I.grabNumberOfVisibleElements('p');
let numOfElementsWithWait = await I.grabNumberOfVisibleElements('p', 2); // timeout applied
```

#### Parameters

- `locator` **([string][4] | [object][5])** located by CSS|XPath|strict locator.
- `sec` **[number][11]?** (optional, `1` by default) time in seconds to wait

Returns **[Promise][9]<[number][11]>** number of visible elements

Expand Down
2 changes: 2 additions & 0 deletions docs/helpers/WebDriver.md
Original file line number Diff line number Diff line change
Expand Up @@ -1342,11 +1342,13 @@ Resumes test execution, so **should be used inside async function with `await`**
```js
let numOfElements = await I.grabNumberOfVisibleElements('p');
let numOfElementsWithWait = await I.grabNumberOfVisibleElements('p', 2); // timeout applied
```
#### Parameters
- `locator` **([string][18] | [object][17])** located by CSS|XPath|strict locator.
- `sec` **[number][24]?** (optional, `1` by default) time in seconds to wait
Returns **[Promise][27]<[number][24]>** number of visible elements
Expand Down
4 changes: 3 additions & 1 deletion docs/webapi/grabNumberOfVisibleElements.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ Resumes test execution, so **should be used inside async function with `await`**

```js
let numOfElements = await I.grabNumberOfVisibleElements('p');
let numOfElementsWithWait = await I.grabNumberOfVisibleElements('p', 2); // timeout applied
```

@param {CodeceptJS.LocatorOrString} locator located by CSS|XPath|strict locator.
@returns {Promise<number>} number of visible elements
@param {number} [sec] (optional, `1` by default) time in seconds to wait
@returns {Promise<number>} number of visible elements
16 changes: 13 additions & 3 deletions lib/helper/Playwright.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const {
clearString,
requireWithFallback,
normalizeSpacesInString,
promiseWithTimeout,
} = require('../utils')
const { isColorProperty, convertColorToRGBA } = require('../colorUtils')
const ElementNotFound = require('./errors/ElementNotFound')
Expand Down Expand Up @@ -1851,10 +1852,19 @@ class Playwright extends Helper {
* {{> grabNumberOfVisibleElements }}
*
*/
async grabNumberOfVisibleElements(locator) {
async grabNumberOfVisibleElements(locator, sec) {
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
let els = await this._locate(locator)
els = await Promise.all(els.map((el) => el.isVisible()))
return els.filter((v) => v).length

const visibilityChecks = els.map((el) => promiseWithTimeout(el.isVisible(), waitTimeout))

try {
els = await Promise.all(visibilityChecks)
return els.filter((v) => v).length
} catch (error) {
console.error(error)
return 0
}
}

/**
Expand Down
16 changes: 10 additions & 6 deletions lib/helper/Puppeteer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
isModifierKey,
requireWithFallback,
normalizeSpacesInString,
promiseWithTimeout,
} = require('../utils')
const { isColorProperty, convertColorToRGBA } = require('../colorUtils')
const ElementNotFound = require('./errors/ElementNotFound')
Expand Down Expand Up @@ -1514,17 +1515,20 @@ class Puppeteer extends Helper {
* {{> grabNumberOfVisibleElements }}
* {{ react }}
*/
async grabNumberOfVisibleElements(locator) {
async grabNumberOfVisibleElements(locator, sec) {
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
let els = await this._locate(locator)
els = (await Promise.all(els.map((el) => el.boundingBox() && el))).filter((v) => v)
els = (await Promise.all(els.map((el) => promiseWithTimeout(el.boundingBox() && el, waitTimeout)))).filter((v) => v)
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
els = await Promise.all(
els.map(
async (el) =>
(await el.evaluate(
els.map((el) =>
promiseWithTimeout(
el.evaluate(
(node) =>
window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none',
)) && el,
) && el,
waitTimeout,
),
),
)

Expand Down
10 changes: 7 additions & 3 deletions lib/helper/TestCafe.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const stringIncludes = require('../assert/include').includes
const { urlEquals } = require('../assert/equal')
const { empty } = require('../assert/empty')
const { truth } = require('../assert/truth')
const { xpathLocator, normalizeSpacesInString } = require('../utils')
const { xpathLocator, normalizeSpacesInString, promiseWithTimeout } = require('../utils')
const Locator = require('../locator')

/**
Expand Down Expand Up @@ -696,8 +696,12 @@ class TestCafe extends Helper {
/**
* {{> grabNumberOfVisibleElements }}
*/
async grabNumberOfVisibleElements(locator) {
const count = (await findElements.call(this, this.context, locator)).filterVisible().count
async grabNumberOfVisibleElements(locator, sec) {
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout

const elements = await promiseWithTimeout(findElements.call(this, this.context, locator), waitTimeout)
const visibleElements = await elements.filterVisible()
const count = visibleElements.count
return count
}

Expand Down
9 changes: 7 additions & 2 deletions lib/helper/WebDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const {
screenshotOutputFolder,
getNormalizedKeyAttributeValue,
modifierKeys,
promiseWithTimeout,
} = require('../utils')
const { isColorProperty, convertColorToRGBA } = require('../colorUtils')
const ElementNotFound = require('./errors/ElementNotFound')
Expand Down Expand Up @@ -1701,12 +1702,16 @@ class WebDriver extends Helper {
/**
* {{> grabNumberOfVisibleElements }}
*/
async grabNumberOfVisibleElements(locator) {
async grabNumberOfVisibleElements(locator, sec) {
const waitTimeout = sec || this.options.waitForTimeoutInSeconds
const res = await this._locate(locator)

let selected = await forEachAsync(res, async (el) => el.isDisplayed())
let selected = await forEachAsync(res, async (el) => promiseWithTimeout(el.isDisplayed(), waitTimeout))

if (!Array.isArray(selected)) selected = [selected]

selected = selected.filter((val) => val === true)

return selected.length
}

Expand Down
8 changes: 8 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,3 +476,11 @@ module.exports.printObjectProperties = (obj) => {
module.exports.normalizeSpacesInString = (string) => {
return string.replace(/\s+/g, ' ');
};

module.exports.promiseWithTimeout = (promise, timeout = 1000) => {
return Promise.race([
promise,
new Promise((_, reject) => { setTimeout(() => reject(new Error(`Set timeout: ${timeout / 1000} sec(s). Timeout exceeded`)), timeout) },
),
]);
};
2 changes: 1 addition & 1 deletion runok.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ Our community prepared some valuable recipes for setting up CI systems with Code
// generate documentation for helpers
const files = fs.readdirSync('lib/helper').filter((f) => path.extname(f) === '.js')

const ignoreList = ['Polly', 'MockRequest'] // WebDriverIO won't be documented and should be removed
const ignoreList = ['Polly', 'MockRequest', 'Nightmare', 'Protractor'] // WebDriverIO won't be documented and should be removed

const partials = fs.readdirSync('docs/webapi').filter((f) => path.extname(f) === '.mustache')
const placeholders = partials.map((file) => `{{> ${path.basename(file, '.mustache')} }}`)
Expand Down

0 comments on commit c307fa4

Please sign in to comment.