1 | # Query and Manipulation Library
|
2 |
|
3 | [![Build Status](https://travis-ci.org/tenbits/selenium-query.png?branch=master)](https://travis-ci.org/tenbits/selenium-query)
|
4 | [![NPM version](https://badge.fury.io/js/selenium-query.svg)](http://badge.fury.io/js/selenium-query)
|
5 | [![TypeScript](https://badges.frapsoft.com/typescript/code/typescript.svg?v=101)](https://github.com/ellerbrock/typescript-badges/)
|
6 |
|
7 | #### jQuery-alike API for [Selenium WebDriver](https://seleniumhq.github.io/selenium/docs/api/javascript/index.html), [JSDom](https://github.com/jsdom/jsdom) and [Cheerio](https://github.com/cheeriojs/cheerio)
|
8 |
|
9 |
|
10 | Single API to query web-pages or html blocks with supported providers: `Selenium WebDriver`, `JSDom`, `Cheerio` _(`puppeteer` on roadmap)_.
|
11 |
|
12 | > Use for tests or crawlers.
|
13 |
|
14 | ---
|
15 |
|
16 | ### Asynchronous nature
|
17 |
|
18 | As the WebDriver methods are **async**, `Selenium Query` instance implements `Promise` and you can chain the function calls or use `async/await`. A very basic example
|
19 |
|
20 | ```javascript
|
21 | let $ = require('selenium-query');
|
22 | $(driver)
|
23 | .find('.foo')
|
24 | .filter('input')
|
25 | .attr('placeholder', 'Baz')
|
26 | .val()
|
27 | .then(value => console.log(value));
|
28 |
|
29 | // or via await
|
30 | let value = await $(driver).find('input.foo').val();
|
31 | console.log(value);
|
32 | ```
|
33 |
|
34 | ### Extension methods
|
35 |
|
36 | As with jQuery you can define an extension method and call it in your tests
|
37 |
|
38 | ```javascript
|
39 | let $ = require('selenium-query');
|
40 | $.fn.doBaz = function(){
|
41 | return this.each(el => {
|
42 | // do some usefull things with WebElement/JsDomElement/CherioElement
|
43 | });
|
44 | };
|
45 | $(driver)
|
46 | .find('input')
|
47 | .doBaz();
|
48 | ```
|
49 |
|
50 | ## API
|
51 |
|
52 | ##### ☰
|
53 | - [`constructor`](#constructor)
|
54 | - [Collection](#collection)
|
55 | - [`length`](#length)
|
56 | - [`eq`](#eq)
|
57 | - [`slice`](#slice)
|
58 | - [`each`](#each)
|
59 | - [`map`](#map)
|
60 | - [`toArray`](#toArray)
|
61 | - [Traverse](#traverse)
|
62 | - [`find`](#find)
|
63 | - [`filter`](#filter)
|
64 | - [`children`](#children)
|
65 | - [`parent`](#parent)
|
66 | - [`closest`](#closest)
|
67 | - [Attributes](#attributes)
|
68 | - [`attr`](#attr)
|
69 | - [`removeAttr`](#removeAttr)
|
70 | - [`prop`](#prop)
|
71 | - [`removeProp`](#removeProp)
|
72 | - [`val`](#val)
|
73 | - [`css`](#css)
|
74 | - [Class](#class)
|
75 | - [`hasClass`](#hasClass)
|
76 | - [`addClass`](#addClass)
|
77 | - [`removeClass`](#removeAttr)
|
78 | - [`toggleClass`](#toggleClass)
|
79 | - [Manipulate](#manipulate)
|
80 | - [`remove`](#remove)
|
81 | - [Dimension and Position](#dimensions)
|
82 | - [`height`](#height)
|
83 | - [`width`](#width)
|
84 | - [`innerHeight`](#innerHeight)
|
85 | - [`innerWidth`](#innerWidth)
|
86 | - [`offset`](#offset)
|
87 | - [`position`](#position)
|
88 | - [`scrollTop`](#scrollTop)
|
89 | - [`scrollLeft`](#scrollLeft)
|
90 | - [Content](#content)
|
91 | - [`html`](#html)
|
92 | - [`text`](#text)
|
93 | - [`append`](#append)
|
94 | - [`prepend`](#prepend)
|
95 | - [`before`](#before)
|
96 | - [`after`](#after)
|
97 | - [Events](#events)
|
98 | - [`trigger`](#trigger)
|
99 | - [`click`](#click)
|
100 | - [`change`](#change)
|
101 | - [`focus`](#focus)
|
102 | - [`blur`](#blur)
|
103 | - :sparkles: [`type`](#type)
|
104 | - :sparkles: [`press`](#press)
|
105 | - :sparkles: [`sendKeys`](#sendKeys)
|
106 | - :sparkles: [`select`](#select)
|
107 |
|
108 | - [Misc](#misc)
|
109 | - [`eval`](#eval)
|
110 |
|
111 | - [Document](#document)
|
112 | - [`load`](#load)
|
113 | - [`getDriver`](#getDriver)
|
114 | - [`setDriver`](#setDriver)
|
115 |
|
116 | - :zap: [JsDom](#jsdom)
|
117 | - [`build`](#jsdom-build)
|
118 | - [`load`](#jsdom-load)
|
119 |
|
120 | - :zap: [Cheerio](#cheerio)
|
121 | - [`build`](#cheerio-build)
|
122 | - [`load`](#cheerio-load)
|
123 |
|
124 | - :zap: [Network](#network)
|
125 | - [`load`](#network-load)
|
126 |
|
127 |
|
128 | ##### `constructor(WebDriver|WebElement|Array<WebElement>|SQuery|Array<SQuery>)` <a name='constructor'></a>
|
129 |
|
130 | - [WebDriver](https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html)
|
131 | - [WebElement](https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebElement.html)
|
132 |
|
133 | ```javascript
|
134 | let SQuery = require('selenium-query');
|
135 | let $document = SQuery(driver);
|
136 | let $inputs = $document.find('inputs');
|
137 | ```
|
138 |
|
139 | ## Collection
|
140 |
|
141 | ##### `length:number` <a name='length'></a>
|
142 | Count of WebElements in a current set.
|
143 | > :exclamation: Due to asynchronous nature, sometimes you have to wait until the promise is resolved to get the correct `length` value
|
144 |
|
145 | ##### `eq(index:number):SQuery` <a name='eq'></a>
|
146 | Get the SQuery instance with only one element at the index.
|
147 | > :exclamation: Once again, wait until the promise is resolved, or **chain** the manipulations
|
148 | ```javascript
|
149 | $(driver)
|
150 | .find('button')
|
151 | .eq(0)
|
152 | .css('background-color', 'red')
|
153 | .done(() => console.log('The color has been changed.'))
|
154 | // instead of an equivalent
|
155 | $(driver)
|
156 | .find('button')
|
157 | .done(buttons => {
|
158 | buttons
|
159 | .eq(0)
|
160 | .done(firstButton => {
|
161 | firstButton
|
162 | .css('background-color', 'red')
|
163 | .done(() => console.log('The color has been changed.'))
|
164 | })
|
165 | });
|
166 | ```
|
167 |
|
168 | ##### `slice([start:number = 0, end:number = .length]):SQuery` <a name='slice'></a>
|
169 | Get elements range.
|
170 |
|
171 | ##### `each(function<node:WebElement, index:number, Promise|void 0>):SQuery` <a name='each'></a>
|
172 | Enumerate the collection. The callback function can return a promise, if an async job is performed.
|
173 |
|
174 | ##### `map(function<node:WebElement, index:number, Promise|any>):SQuery` <a name='map'></a>
|
175 | Map the collection into the new one. Return the value from the function or a promise which resolves then with the value.
|
176 |
|
177 | ##### `toArray():Promise<Array<any>>` <a name='toarray'></a>
|
178 | Returns a promise which resolves with an Array instance of current elements in collection
|
179 |
|
180 |
|
181 | ## Traverse
|
182 |
|
183 | ##### `find(selector:string):SQuery` <a name='find'></a>
|
184 | Find element(s).
|
185 |
|
186 | ##### `filter(selector:string):SQuery` <a name='filter'></a>
|
187 | Filter element(s) out of the current collection.
|
188 |
|
189 | ##### `children([selector:string]):SQuery` <a name='children'></a>
|
190 | Get, and optionally filter, children of every element in the collection.
|
191 |
|
192 | ##### `parent():SQuery` <a name='parent'></a>
|
193 | Get parent elements of every element in the collection
|
194 |
|
195 | ##### `closest(selector):SQuery` <a name='closest'></a>
|
196 | Find ancestor of every element in the collection
|
197 |
|
198 |
|
199 | ## Attributes
|
200 |
|
201 | ##### `attr(key:string | key:string, val:any | attributes:Object ):SQuery|Promise<any>` <a name='attr'></a>
|
202 | Get attribute value of the first element in the collection, or set attribute(s) to each element.
|
203 |
|
204 | ##### `removeAttr(key:string):SQuery` <a name='removeAttr'></a>
|
205 | Remove the attribute
|
206 |
|
207 | ##### `prop(key:string | key:string, val:any | properties:Object):SQuery|Promise<any>` <a name='prop'></a>
|
208 | Get property value of the first element in the collection, or set property(ies) to each element.
|
209 |
|
210 | ##### `removeProp(key:string):SQuery` <a name='removeProp'></a>
|
211 | Delete property
|
212 |
|
213 | ##### `val([value:string]):SQuery` <a name='val'></a>
|
214 | Get or set `value` property, like `input.value`
|
215 |
|
216 | ##### `css(key:string | key:string, val:string | css:Object ):SQuery|Promise<any>` <a name='css'></a>
|
217 | Get or set style properties
|
218 |
|
219 | ## Class
|
220 |
|
221 | ##### `hasClass(name:string):Promise<boolean>` <a name='hasClass'></a>
|
222 | Check if the first element has the class name.
|
223 |
|
224 | ##### `addClass(name:string):SQuery` <a name='addClass'></a>
|
225 | Add the class name(s) to every element in the collection
|
226 |
|
227 | ##### `removeClass(name:string):SQuery` <a name='removeClass'></a>
|
228 | Remove the class name(s) of every element in the collection
|
229 |
|
230 | ##### `toggleClass(name:string):SQuery` <a name='toggleClass'></a>
|
231 | Toggle the class name(s) of every element in the collection
|
232 |
|
233 | ## Manipulate
|
234 |
|
235 | ##### `remove():SQuery` <a name='remove'></a>
|
236 | Remove the elements from the parent nodes
|
237 |
|
238 | ## Dimensions
|
239 |
|
240 | ##### `height():Promise<number>` <a name='height'></a>
|
241 | ##### `width():Promise<number>` <a name='width'></a>
|
242 | ##### `innerHeight():Promise<number>` <a name='innerHeight'></a>
|
243 | ##### `innerWidth():Promise<number>` <a name='innerWidth'></a>
|
244 | ##### `offset():Promise<object{top,left}>` <a name='offset'></a>
|
245 | ##### `position():Promise<object{top,left}>` <a name='position'></a>
|
246 | ##### `scrollTop():Promise<number>` <a name='scrollTop'></a>
|
247 | ##### `scrollLeft():Promise<number>` <a name='scrollLeft'></a>
|
248 |
|
249 | ## Content
|
250 |
|
251 | ##### `html([html:string]):SQuery|Promise<string>` <a name='html'></a>
|
252 | ##### `text([text:string]):SQuery|Promise<string>` <a name='text'></a>
|
253 |
|
254 | ##### `append(html:string):SQuery` <a name='append'></a>
|
255 | ##### `prepend(html:string):SQuery` <a name='prepend'></a>
|
256 | ##### `before(html:string):SQuery` <a name='before'></a>
|
257 | ##### `after(html:string):SQuery` <a name='after'></a>
|
258 |
|
259 | ## Events
|
260 |
|
261 | ##### `trigger(type:string [, data:Object]):SQuery` <a name='trigger'></a>
|
262 | Trigger native or custom event.
|
263 |
|
264 | ##### `click():SQuery` <a name='click'></a>
|
265 | ##### `change():SQuery` <a name='change'></a>
|
266 | Trigger `change` event
|
267 | ##### `focus():SQuery` <a name='click'></a>
|
268 | ##### `blur():SQuery` <a name='click'></a>
|
269 |
|
270 | ##### `type(text:string):SQuery` <a name='type'></a>
|
271 | Enter the text.
|
272 | > :exclamation: Meta keys are supported in `{}`
|
273 |
|
274 | ##### `press(combination:string):SQuery` <a name='press'></a>
|
275 | Press key combination. E.g.: `ctrl+c`, `a+b+c`, `ctrl+alt+d`, `ctrl++` _(`control` and `plus` keys)_
|
276 |
|
277 | ##### `sendKeys(text:string):SQuery` <a name='sendKeys'></a>
|
278 | Call native Selenums `sendKeys` fn on each element
|
279 |
|
280 | ##### `select(text:string | start:number[, end:number]):SQuery` <a name='select'></a>
|
281 | Select an option from the `select` element, or if the `input` the selects a text or range
|
282 |
|
283 |
|
284 | ## Misc
|
285 |
|
286 | ##### `eval(fn:Function, ...args):Promise<any>` <a name='eval'></a>
|
287 | Evaluate function in Browser.
|
288 | > :exclamation: The first argument is the first element in the set
|
289 | ```javascript
|
290 | $(driver)
|
291 | .find('button')
|
292 | .eval(function(el){
|
293 | // browser context
|
294 | // do smth. with the Element and return a value
|
295 | });
|
296 | ```
|
297 |
|
298 |
|
299 | ## Document
|
300 |
|
301 | #### `static` `load(url:string[, config:WebDriverOptions]):SQuery` <a name='load'></a>
|
302 | Create or reuse a WebDriver, and load the page.
|
303 |
|
304 | #### `WebDriverOptions` defaults
|
305 | ```javascript
|
306 | {
|
307 | name: 'Chrome',
|
308 | args: ['no-sandbox'],
|
309 | binaryPath: null,
|
310 |
|
311 | // For better control and to change the behaviour of how the options are created and applied,
|
312 | // you can define next functions
|
313 | applyOptions: function(builder, options) {},
|
314 | setOptions (builder, options) {},
|
315 | setArguments (options) {},
|
316 | setBinaryPath (options) {},
|
317 | setLogging (options) {}
|
318 | }
|
319 | ```
|
320 |
|
321 | ## JsDom
|
322 |
|
323 | #### `static` `SQuery.jsdom.build(config: IJsdomParams):SQuery` <a name='jsdom-build'></a>
|
324 |
|
325 | ```typescript
|
326 | interface IJsdomParams {
|
327 | html: string
|
328 | }
|
329 | ```
|
330 |
|
331 | Create SQuery collection with JsDom driver
|
332 |
|
333 | #### `static` `SQuery.jsdom.load(url: string, config: IJsdomLoadParams):SQuery` <a name='jsdom-load'></a>
|
334 |
|
335 | ```typescript
|
336 | interface IJsdomLoadParams {
|
337 | headers?: {[name: string] : string }
|
338 | method?
|
339 | query?: {[name: string] : string }
|
340 | payload?
|
341 | cookies?: string | string[]
|
342 | cache?: {
|
343 | folder?: string
|
344 | maxAge?: number
|
345 | }
|
346 | cacheQueryIgnore?: string[]
|
347 | /** Webdriver will load this url, or requested url, to set the cookies first */
|
348 | cookieOrigin?: string
|
349 | }
|
350 | ```
|
351 |
|
352 | ## Cheerio
|
353 |
|
354 | #### `static` `SQuery.cheerio.build(config: ICheerioParams):SQuery` <a name='cheerio-build'></a>
|
355 |
|
356 | ```typescript
|
357 | interface ICheerioParams {
|
358 | html: string
|
359 | }
|
360 | ```
|
361 |
|
362 | Create SQuery collection with Cheerio driver (_Only query and manipulation methods are implemented_)
|
363 |
|
364 | #### `static` `SQuery.cheerio.load(url: string, config: ICheerioLoadParams):SQuery` <a name='cheerio-load'></a>
|
365 |
|
366 | ```typescript
|
367 | interface ICheerioLoadParams {
|
368 | headers?: {[name: string] : string }
|
369 | method?
|
370 | query?: {[name: string] : string }
|
371 | payload?
|
372 | cookies?: string | string[]
|
373 | cache?: {
|
374 | folder?: string
|
375 | maxAge?: number
|
376 | }
|
377 | cacheQueryIgnore?: string[]
|
378 | /** Webdriver will load this url, or requested url, to set the cookies first */
|
379 | cookieOrigin?: string
|
380 | }
|
381 | ```
|
382 |
|
383 |
|
384 |
|
385 | ## Network
|
386 |
|
387 | HTTP Utils to load and submit data. Handles cache and cookies.
|
388 |
|
389 | #### `load` `SQuery.network.load(url: string, config: IHttpParams):IHttpResponse` <a name='network-load'></a>
|
390 |
|
391 | ```typescript
|
392 | interface IHttpParams {
|
393 | headers?: {[name: string] : string }
|
394 | method?: 'post' | 'get' | 'delete' | 'patch' | 'head' | string
|
395 | query?: {[name: string] : string }
|
396 | body?: string | Buffer
|
397 |
|
398 | cookies?: {[name: string] : string } | string[] | string
|
399 | cookiesDefault?: {[name: string] : string } | string[] | string
|
400 |
|
401 | cache?: boolean | {
|
402 | folder?: string
|
403 | maxAge?: number
|
404 | compress?: boolean
|
405 | //-ensureCacheAllowed? (resp): boolean
|
406 | }
|
407 | cacheQueryIgnore?: string[]
|
408 |
|
409 | retryCount?: number
|
410 | retryTimeout?: number
|
411 | follow?: number
|
412 | httpsProxy?: string
|
413 | ignoreSSLErrors?: boolean
|
414 | }
|
415 | interface IHttpResponse {
|
416 | status: number
|
417 | message?: string
|
418 |
|
419 | headers: {[name: string] : string }
|
420 | url: string
|
421 | body: any
|
422 | }
|
423 | ```
|
424 |
|
425 |
|
426 | **Example**
|
427 | ```javascript
|
428 | $
|
429 | .load('http://google.com')
|
430 | .find('input')
|
431 | .css('background-color', 'red');
|
432 | ```
|
433 |
|
434 |
|
435 | :checkered_flag:
|
436 |
|
437 | ---
|
438 |
|
439 | :copyright: MIT, Alex Kit
|