Skip to content
This repository has been archived by the owner on Nov 24, 2018. It is now read-only.

add waitFn and wait for array #304

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ const chromeless = new Chromeless({
- [`click(selector: string)`](docs/api.md#api-click)
- [`wait(timeout: number)`](docs/api.md#api-wait-timeout)
- [`wait(selector: string)`](docs/api.md#api-wait-selector)
- [`wait(fn: (...args: any[]) => boolean, ...args: any[])`] - Not implemented yet
- [`wait(selectors: string[])`](docs/api.md#api-wait-selectors)
- [`wait(fn: (...args: any[]) => boolean, ...args: any[])`](docs/api.md#api-wait-function)
- [`clearCache()`](docs/api.md#api-clearcache)
- [`focus(selector: string)`](docs/api.md#api-focus)
- [`press(keyCode: number, count?: number, modifiers?: any)`](docs/api.md#api-press)
Expand Down
32 changes: 26 additions & 6 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Chromeless provides TypeScript typings.
- [`click(selector: string)`](#api-click)
- [`wait(timeout: number)`](#api-wait-timeout)
- [`wait(selector: string, timeout?: number)`](#api-wait-selector)
- [`wait(fn: (...args: any[]) => boolean, ...args: any[])`] - Not implemented yet
- [`wait(selectors: string[], timeout?: number)`](#api-wait-selectors)
- [`wait(fn: (...args: any[]) => boolean, ...args: any[])`](#api-wait-function)
- [`clearCache()`](docs/api.md#api-clearcache)
- [`focus(selector: string)`](#api-focus)
- [`press(keyCode: number, count?: number, modifiers?: any)`](#api-press)
Expand Down Expand Up @@ -157,13 +158,30 @@ await chromeless.wait('div#loaded', 1000)

---------------------------------------

<a name="api-wait-fn" />
<a name="api-wait-selectors" />

### wait(fn: (...args: any[]) => boolean, ...args: any[]): Chromeless<T>
### wait(selectors: string[], timeout?: number): Chromeless<T>

Not implemented yet
Wait until one of the elements appears.

__Arguments__
- `selectors` - Array of DOM selectors to wait for
- `timeout` - How long to wait for any of the element to appear (default is value of waitTimeout option)

__Example__

```js
await chromeless.wait(['div#error', 'div#success'])
await chromeless.wait(['div#error', 'div#success'], 1000)
```

---------------------------------------

<a name="api-wait-function" />

### wait(fn: (...args: any[]) => boolean, ...args: any[]): Chromeless<T>

Wait until a function returns.
Wait until a function returns truthy value. Note that the function is run in headless browser.

__Arguments__
- `fn` - Function to wait for
Expand All @@ -172,7 +190,9 @@ __Arguments__
__Example__

```js
await chromeless.wait(() => { return console.log('@TODO: put a better example here') })
await chromeless.wait((divId) => {
return !!document.getElementById(divId)
}, 'div#loaded')
```

---------------------------------------
Expand Down
14 changes: 13 additions & 1 deletion src/__tests__/test.html

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,21 +92,36 @@ export default class Chromeless<T extends any> implements Promise<T> {

wait(timeout: number): Chromeless<T>
wait(selector: string, timeout?: number): Chromeless<T>
wait(fn: (...args: any[]) => boolean, ...args: any[]): Chromeless<T>
wait(selectors: string[], timeout?: number): Chromeless<T>
wait(fn: (...args: any[]) => boolean, ...args: any[]): Chromeless<T>
wait(firstArg, ...args: any[]): Chromeless<T> {
switch (typeof firstArg) {
case 'number': {
this.queue.enqueue({ type: 'wait', timeout: firstArg })
break
}
case 'string': {
this.queue.enqueue({ type: 'wait', selector: firstArg, timeout: args[0] })
this.queue.enqueue({
type: 'wait',
selector: firstArg,
timeout: args[0],
})
break
}
case 'function': {
this.queue.enqueue({ type: 'wait', fn: firstArg, args })
break
}
case 'object': {
if (Array.isArray(firstArg) && firstArg.length) {
this.queue.enqueue({
type: 'wait',
selectors: firstArg,
timeout: args[0],
})
break
}
}
default:
throw new Error(`Invalid wait arguments: ${firstArg} ${args}`)
}
Expand Down
32 changes: 26 additions & 6 deletions src/chrome/local-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
nodeExists,
wait,
waitForNode,
waitForNodes,
waitFunction,
click,
evaluate,
screenshot,
Expand Down Expand Up @@ -58,10 +60,12 @@ export default class LocalRuntime {
case 'wait': {
if (command.selector) {
return this.waitSelector(command.selector, command.timeout)
} else if (command.selectors) {
return this.waitSelectors(command.selectors, command.timeout)
} else if (command.timeout) {
return this.waitTimeout(command.timeout)
} else {
throw new Error('waitFn not yet implemented')
return this.waitFunction(command.fn, command.args)
}
}
case 'clearCache':
Expand Down Expand Up @@ -150,13 +154,32 @@ export default class LocalRuntime {

private async waitSelector(
selector: string,
waitTimeout: number = this.chromelessOptions.waitTimeout
waitTimeout: number = this.chromelessOptions.waitTimeout,
): Promise<void> {
this.log(`Waiting for ${selector} ${waitTimeout}`)
await waitForNode(this.client, selector, waitTimeout)
this.log(`Waited for ${selector}`)
}

private async waitSelectors(
selectors: string[],
waitTimeout: number = this.chromelessOptions.waitTimeout,
): Promise<void> {
this.log(`Waiting for ${selectors} ${waitTimeout}`)
await waitForNodes(this.client, selectors, waitTimeout)
this.log(`Waited for ${selectors}`)
}

private async waitFunction(
fn: string,
args: any[],
waitTimeout: number = this.chromelessOptions.waitTimeout,
): Promise<void> {
this.log(`Waiting for function`)
await waitFunction(this.client, fn, args, waitTimeout)
this.log(`Waited for function`)
}

private async click(selector: string): Promise<void> {
if (this.chromelessOptions.implicitWait) {
this.log(`click(): Waiting for ${selector}`)
Expand Down Expand Up @@ -395,10 +418,7 @@ export default class LocalRuntime {

// Returns the S3 url or local file path
async returnPdf(options?: PdfOptions): Promise<string> {
const {
filePath,
...cdpOptions
} = options || { filePath: undefined }
const { filePath, ...cdpOptions } = options || { filePath: undefined }
const data = await pdf(this.client, cdpOptions)

if (isS3Configured()) {
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export type Command =
type: 'wait'
timeout?: number
selector?: string
selectors?: string[]
fn?: string
args?: any[]
}
Expand Down
114 changes: 88 additions & 26 deletions src/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@ const testUrl = `data:text/html,${testHtml}`

const getPngMetaData = async (filePath): Promise<any> => {
const fd = fs.openSync(filePath, 'r')
return await new Promise((resolve) => {
fs.read(fd, Buffer.alloc(24), 0, 24, 0,
(err, bytesRead, buffer) => resolve({
width: buffer.readUInt32BE(16),
height: buffer.readUInt32BE(20)
}))
return await new Promise(resolve => {
fs.read(fd, Buffer.alloc(24), 0, 24, 0, (err, bytesRead, buffer) =>
resolve({
width: buffer.readUInt32BE(16),
height: buffer.readUInt32BE(20),
}),
)
})
}

// POC
test('evaluate (document.title)', async t => {
const chromeless = new Chromeless({ launchChrome: false })
const title = await chromeless
.goto(testUrl)
.evaluate(() => document.title)
const title = await chromeless.goto(testUrl).evaluate(() => document.title)

await chromeless.end()

Expand All @@ -32,12 +31,8 @@ test('evaluate (document.title)', async t => {

test('screenshot and pdf path', async t => {
const chromeless = new Chromeless({ launchChrome: false })
const screenshot = await chromeless
.goto(testUrl)
.screenshot()
const pdf = await chromeless
.goto(testUrl)
.pdf()
const screenshot = await chromeless.goto(testUrl).screenshot()
const pdf = await chromeless.goto(testUrl).pdf()

await chromeless.end()

Expand All @@ -48,18 +43,85 @@ test('screenshot and pdf path', async t => {
})

test('screenshot by selector', async t => {
const version = await CDP.Version()
const versionMajor = parseInt(/Chrome\/(\d+)/.exec(version['User-Agent'])[1])
// clipping will only work on chrome 61+
const version = await CDP.Version()
const versionMajor = parseInt(/Chrome\/(\d+)/.exec(version['User-Agent'])[1])
// clipping will only work on chrome 61+

const chromeless = new Chromeless({ launchChrome: false })
const screenshot = await chromeless.goto(testUrl).screenshot('img')

await chromeless.end()

const png = await getPngMetaData(screenshot)
t.is(png.width, versionMajor > 60 ? 512 : 1440)
t.is(png.height, versionMajor > 60 ? 512 : 900)
})

test('wait for selector', async t => {
const chromeless = new Chromeless({ launchChrome: false })
const divExists = await chromeless
.goto(testUrl)
.click('#click')
.wait('#add-div')
.exists('#add-div')

await chromeless.end()

t.truthy(divExists)
})

test('wait for time', async t => {
const chromeless = new Chromeless({ launchChrome: false })
const divExists = await chromeless
.goto(testUrl)
.click('#click')
.wait(1200)
.exists('#add-div')

await chromeless.end()

t.truthy(divExists)
})

test('wait for function', async t => {
const chromeless = new Chromeless({ launchChrome: false })
const divExists = await chromeless
.goto(testUrl)
.click('#click')
.wait(() => {
return !!document.getElementById('add-div')
})
.exists('#add-div')

await chromeless.end()

t.truthy(divExists)
})

const chromeless = new Chromeless({ launchChrome: false })
const screenshot = await chromeless
.goto(testUrl)
.screenshot('img')
test('wait for function with args', async t => {
const chromeless = new Chromeless({ launchChrome: false })
const divExists = await chromeless
.goto(testUrl)
.click('#click')
.wait(divId => {
return !!document.getElementById(divId)
}, 'add-div')
.exists('#add-div')

await chromeless.end()

t.truthy(divExists)
})

await chromeless.end()
test('wait for selectors array', async t => {
const chromeless = new Chromeless({ launchChrome: false })
const divExists = await chromeless
.goto(testUrl)
.click('#click')
.wait(['#this-wont-exist', '#add-div'])
.exists('#add-div')

await chromeless.end()

const png = await getPngMetaData(screenshot)
t.is(png.width, versionMajor > 60 ? 512 : 1440)
t.is(png.height, versionMajor > 60 ? 512 : 900)
t.truthy(divExists)
})