- think on refactoring
await browser.element('#new-todo').type('do something').then(perform.pressEnter)
- to
await browser.element('#new-todo').type('do something').then(command.pressEnter)
- consider returning ElementPromise from all Element.* commands to allow:
await browser.element('#new-todo').type('do something').pressEnter()
- same like in raw Selenium WebDriver
- fix
command.js.type
working when when value isnull
- fix
element.perform(command)
incorrect typing issues - update selenium dependency to >=4.10.0, i.e. will install 4.15.0 as of 2023.11.20
Flatten inner lists inside have.texts(HERE)
. Now you can pass nested arrays to have.texts
:
// Like:
await table.all('tr').all('td').should(
have.texts(
['Bach', 'Frank', '[email protected]'],
['Conway', 'Tim', '[email protected]'],
)
)
// Instead of:
await table.all('tr').all('td').should(
have.texts(
'Bach', 'Frank', '[email protected]',
'Conway', 'Tim', '[email protected]',
)
)
You might need this if you use formatter like prettier, that will format your code more like:
await table.all('tr').all('td').should(
have.texts(
'Bach',
'Frank',
'[email protected]',
'Conway',
'Tim',
'[email protected]',
)
)
– that is pretty not readable in context of table data...
new commands:
- collection.even
- collection.odd
- collection.sliced(start, end, STEP)
- collection.all(selector)
- collection.allFirst(selector)
new import alias:
import { command } from 'selenidejs'
- as alias to
import { perform } from 'selenidejs'
- as alias to
fix bugs:
- once
collection.should(have.texts(...))
fails, sometimes returns unclear reason in error message: "Cannot read properties of undefined (reading 'includes')"
upgrade selenium to 4.10.0
fix bugs:
- have.exactText does not accept numbers
- have.exactTexts does not type hint for expected texts as numbers
- accept Locator in browser.element, browser.element.element, browser.element.all, and browser.all
- update Selenium to 4.9.2
- add prebuilt browser object to selenidejs imports
- added
collection.second
as alias tocollection.elementAt(1)
- added
collection.by(condition)
as alias tocollection.filteredBy(condition)
- filteredBy will be deprecated in future releases
- updated selenium to 4.7.0
-
now you can pass a function that returns driver to provide smarter driver management:
let globalDriver: WebDriver; // to be initialized later Browser.configuredWith() .driver(() => globalDriver) ._locationStrategy(mobile.selectorToBy) .timeout(10000) .build();
- update selenium from 4.1.0 to 4.3.1
-
added Configuration._locationStrategy to customize the conversion from string selector to By in element builders like browser.element(selector), browser.all(selector), element.element(selector), element.all(selector) By default equals to built in Extensions.cssOrXpathToBy function that is as simple as:
export function cssOrXPathToBy(selector: string): By { const cssOrXPath = selector.trim(); const isXpath = (str: string) => ( str.startsWith('/') || str.startsWith('./') || str.startsWith('..') || str.startsWith('(') || str.startsWith('*/') ); return isXpath(cssOrXPath) ? by.xpath(cssOrXPath) : by.css(cssOrXPath); }
Hence you can provide any other custom conversion fn;) For example adapt selenidejs for appium with corresponding mobile selectors, see an example for android at github.com/automician/selenidejs-mobile-test-appium-ts-template with usage in test by link
- !NOTE! The option starts with underscore dangle.
In selenidejs the underscore dangle is used as a mark of "experimental" featues
that might change in future releases.
Speaking about
_locationStrategy
either its name of signature of fn value can be changed
- !NOTE! The option starts with underscore dangle.
In selenidejs the underscore dangle is used as a mark of "experimental" featues
that might change in future releases.
Speaking about
- update selenium to 4.1.0
- fixed
byXpath
function, so it trims input now to allow passing values such as.//div
- fixed finding by xpath when using
element
/all
methods
- fixed
have.text
condition - it was using alwaysmatch
instead of usingmatch
orincludes
depending on passed argument
-
executeScript
improvements:browser.executeScript(...)
andelement.executeScript(...)
- now accepts not plainstring
/Function
- but brand new ones:browser.executeScript
- now accepts(document, args, window) => ...
function, where:element.executeScript
- now accepts(element, args, window) => ...
function, where:-
element
- is HTMLElement which corresponds toelement
's actualWebElement
-
args
- is an array of passed additional arguments, likeelement.executeScript((element, args) => args[0] === 'foo' && args[1] === 'bar', 'foo', 'bar')
-
window
- is Window``` // assume dom looks like // <body> // <span>first</span> // <div> // <span>second</span> // <span>third</span> // </div> // </body> const text = await browser.executeScript(document => document.getElementsByTagName('span')[0].innerHTML); console.log(text); // 'first item' const texts = await browser.element('div').executeScript( (element, args) => { var spans = element.getElementsByTagName('span'); var textOne = spans[0].innerHTML; var textTwo = spans[1].innerHTML; return [args[0], textOne, textTwo]; }, 'first' ); console.log(texts); // ['first', 'second', 'third'] ```
-
- all new arguments for
executeScript
function are typed, so if you use Typescript - you will be able to use full completion inside passed function right in your IDE
-
you can now find elements not only with
string
(which is xpath or css) orBy
, but using js function also:``` // assume dom looks like // <body> // <span>first</span> // <div> // <span>second</span> // <span>third</span> // </div> // </body> const body = browser.element({ script: document => document.body }); const div = body.element({ script: element => element.getElementsByTagName('div')[0] }); const spans = div.all({ script: element => element.getElementsByTagName('span') }); console.log(await spans.get(their.texts)); // ['second', 'third'] ```
-
Shadow DOM support:
``` // assume dom looks like // <body> // ...shadowRoot... // <span>first</span> // ...shadowRoot... // </body> const span = browser.element('body').shadow.element('span'); console.log(await span.get(its.text)); // 'first' ```
-
mapping collection elements to inner (relative) elements:
``` // assume dom looks like // <body> // <div> // <span>first</span> // <span>second</span> // </div> // <div> // <span>third</span> // <span>fourth</span> // </div> // </body> const firstSpans = browser.all('div').collected(it => it.element('span')); console.log(await firstSpans.get(their.texts)); // ['first', 'third'] const allSpans = browser.all('div').collected(it => it.all('span')); console.log(await allSpans.get(their.texts)); // ['first', 'second', 'third', 'fourth'] ```
- fixed
setSize(...)
tosetRect({height: ..., width:...})
inresizeWindow
according to latest upgrade ofselenium-webdriver
version up to4.0.0-alpha.4
- updated
selenium-webdriver
version up to4.0.0-alpha.4
- removed
- changed
- (see #92)
- refactored conditions to be object-based over function-based (should not break anything actually, if you have not created your own conditions...)
wait.until
now accepts only one argument (no varargs anymore)- changed
have.jsReturnedTrue
tohave.jsReturned
which accepts expected result now ( f.e.have.jsReturned(10, 'return 10;')
)
- (see #92)
- added
condition.or
andcondition.and
(see #92)- example:
elements.filteredBy(have.cssClass('green').or(have.cssClass('red')))
- example:
- added
be.not.*
andhave.no.*
- example:
element.should(be.not.visible)
element.should(have.no.text('foo'))
elements.filteredBy(have.no.cssClass('green'))
- example:
- changed Collection#first
browser.all('#alphabet *').first().should(have.text('a'))
- to
browser.all('#alphabet *').first.should(have.text('a'))
- changed Element#followingSibling
browser.element('#alphabet *').followingSibling('[@class="vowel"]').should(have.text('e'))
- to
browser.element('#alphabet *').followingSibling.should(have.text('b'))
- to achieve the same, you have to be more verbose now:
browser.element('#alphabet *').element('./followingSibling::*[@class="vowel"]').should(have.text('e'))
- to achieve the same, you have to be more verbose now:
- renamed according to correct english grammar (read more-than-VS-greater-than)
condition.collection.hasSizeMoreThan
- to
condition.collection.hasGreaterThan
-
added a few collection conditions
- have.sizeGreaterThan
- have.sizeLessThan
- have.sizeGreaterThanOrEqual
- have.sizeLessThanOrEqual
-
added collection.sliced(start, endExclusive):
browser.all('#alphabet *').sliced(0, 2).should(have.texts('a', 'b'))
The all new API. Let's do our best to keep it stable:)