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

Feature/array click array scroll #246

Open
wants to merge 3 commits 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const chromeless = new Chromeless({
- [`goto(url: string)`](docs/api.md#api-goto)
- [`setUserAgent(useragent: string)`](docs/api.md#api-setUserAgent)
- [`click(selector: string)`](docs/api.md#api-click)
- [`clickArrayElements(selector: string, arrayNumber: number)`](docs/api.md#api-clickarrayelements)
- [`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
Expand All @@ -154,11 +155,13 @@ const chromeless = new Chromeless({
- [`mouseup(selector: string)`](docs/api.md#api-mouseup)
- [`scrollTo(x: number, y: number)`](docs/api.md#api-scrollto)
- [`scrollToElement(selector: string)`](docs/api.md#api-scrolltoelement)
- [`scrollToElementArrayElements(selector: string, arrayNumber: number)`](docs/api.md#api-scrolltoelement-array-elements)
- [`setHtml(html: string)`](docs/api.md#api-sethtml)
- [`setViewport(options: DeviceMetrics)`](docs/api.md#api-setviewport)
- [`evaluate<U extends any>(fn: (...args: any[]) => void, ...args: any[])`](docs/api.md#api-evaluate)
- [`inputValue(selector: string)`](docs/api.md#api-inputvalue)
- [`exists(selector: string)`](docs/api.md#api-exists)
- [`existsArrayElement(selector: string, arrayNumber: number)`](docs/api.md#api-existsarrayelement)
- [`screenshot()`](docs/api.md#api-screenshot)
- [`pdf(options?: PdfOptions)`](docs/api.md#api-pdf)
- [`html()`](docs/api.md#api-html)
Expand Down
15 changes: 15 additions & 0 deletions deployAWSChromeless.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
# We get the aws_access_key_id, aws_secret_access_key and region from the files where aws save the information
aws_access_key_id_string=`cat $HOME/.aws/credentials | grep aws_access_key_id`
aws_secret_access_key_string=`cat $HOME/.aws/credentials | grep aws_secret_access_key`
region=`cat $HOME/.aws/config | grep region`

IFS=' = ' read -ra KEYID <<< "$aws_access_key_id_string"

IFS=' = ' read -ra AKEY <<< "$aws_secret_access_key_string"

IFS=' = ' read -ra REGION <<< "$region"

# We run the command in a dockerfile to prevent package version errors. It's only necessary to have docker installed
docker build --build-arg "IOT_ENDPOINT=$(aws iot describe-endpoint --region=${REGION[1]} --output text)" --build-arg "REGION=${REGION[1]}" -t deploy-chromeless-git-version -f dockerfiles/Dockerfile.deploy .
docker run -e "AWS_SECRET_ACCESS_KEY=${AKEY[1]}" -e "AWS_ACCESS_KEY_ID=${KEYID[1]}" -ti deploy-chromeless-git-version
30 changes: 30 additions & 0 deletions dockerfiles/Dockerfile.deploy
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM node:latest

USER root
# replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# update the repository sources list
# and install dependencies
RUN apt-get update \
&& apt-get install -y curl \
&& apt-get install -y git-all \
&& apt-get install git \
&& apt-get install -y python python-dev python-pip python-virtualenv \
&& apt-get -y autoclean
RUN pip install awscli==1.11.131

# Get chromeless code and install all the containing packages

WORKDIR /opt/app
# Get github code
RUN git clone https://github.com/graphcool/chromeless.git
WORKDIR /opt/app/chromeless/serverless
RUN npm install

# We replace our aws account default region and iot endpoint with the one in github code
ARG IOT_ENDPOINT
ARG REGION
RUN sed -i 's/eu-west-1/'"$REGION"'/g' serverless.yml
RUN sed -i 's/${env:AWS_IOT_HOST}/'"$IOT_ENDPOINT"'/g' serverless.yml
ENTRYPOINT ["npm", "run", "deploy"]
50 changes: 45 additions & 5 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ Chromeless provides TypeScript typings.
- [`goto(url: string)`](#api-goto)
- [`setUserAgent(useragent: string)`](#api-setuseragent)
- [`click(selector: string)`](#api-click)
- [`clickArrayElements(selector: string, arrayNumber: number)`](#api-clickarrayelements)
- [`wait(timeout: number)`](#api-wait-timeout)
- [`wait(selector: string)`](#api-wait-selector)
- [`wait(selector: string, timeout?: number)`](#api-wait-selector)
- [`wait(fn: (...args: any[]) => boolean, ...args: any[])`] - Not implemented yet
- [`clearCache()`](docs/api.md#api-clearcache)
- [`focus(selector: string)`](#api-focus)
Expand All @@ -23,11 +24,13 @@ Chromeless provides TypeScript typings.
- [`mouseup(selector: string)`](#api-mouseup)
- [`scrollTo(x: number, y: number)`](#api-scrollto)
- [`scrollToElement(selector: string)`](#api-scrolltoelement)
- [`scrollToElementArrayElements(selector: string, arrayNumber: number)`](#api-scrolltoelementarrayelements)
- [`setHtml(html: string)`](#api-sethtml)
- [`setViewport(options: DeviceMetrics)`](#api-setviewport)
- [`evaluate<U extends any>(fn: (...args: any[]) => void, ...args: any[])`](#api-evaluate)
- [`inputValue(selector: string)`](#api-inputvalue)
- [`exists(selector: string)`](#api-exists)
- [`existsArrayElement(selector: string, arrayNumber: number)`](#api-existsarrayelement)
- [`screenshot()`](#api-screenshot)
- [`pdf(options?: PdfOptions)`](#api-pdf)
- [`html()`](#api-html)
Expand Down Expand Up @@ -108,6 +111,24 @@ await chromeless.click('#button')

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

<a name="api-clickarrayelements" />

### click(selector: string): Chromeless<T>

Click on something in the DOM.

__Arguments__
- `selector` - DOM selector for element to click
- `arrayNumber` - Get the element in a certain position to click

__Example__

```js
await chromeless.clickArrayElements('button', 19)
```

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

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

### wait(timeout: number): Chromeless<T>
Expand All @@ -127,17 +148,19 @@ await chromeless.wait(1000)

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

### wait(selector: string): Chromeless<T>
### wait(selector: string, timeout?: number): Chromeless<T>

Wait until something appears. Useful for waiting for things to render.

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

__Example__

```js
await chromeless.wait('div#loaded')
await chromeless.wait('div#loaded', 1000)
```

---------------------------------------
Expand Down Expand Up @@ -299,13 +322,13 @@ await chromeless.mouseup('#placeholder')
Scroll to somewhere in the document.

__Arguments__
- `x` - Offset from top of the document
- `y` - Offset from the left of the document
- `x` - Offset from the left of the document
- `y` - Offset from the top of the document

__Example__

```js
await chromeless.scrollTo(500, 0)
await chromeless.scrollTo(0, 500)
```

---------------------------------------
Expand All @@ -324,6 +347,23 @@ __Example__
```js
await chromeless.scrollToElement('.button')
```
---------------------------------------

<a name="api-scrolltoelementarrayelements" />

### scrollToElementArrayElements(selector: string, arrayNumber: number): Chromeless<T>

Scroll to location of element. Behavior is simiar to `<a href="#fragment"></a>` — target element will be at the top of viewport

__Arguments__
- `selector` - DOM selector for element to scroll to
- `arrayNumber` - Position of the element in array selector

__Example__

```js
await chromeless.scrollToElementArrayElements('button', 18)
```

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

Expand Down
26 changes: 24 additions & 2 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,14 @@ export default class Chromeless<T extends any> implements Promise<T> {
return this
}

clickArrayElements(selector: string, arrayNumber: number): Chromeless<T> {
this.queue.enqueue({ type: 'clickArrayElements', selector, arrayNumber })

return this
}

wait(timeout: number): Chromeless<T>
wait(selector: string): Chromeless<T>
wait(selector: string, timeout?: number): Chromeless<T>
wait(fn: (...args: any[]) => boolean, ...args: any[]): Chromeless<T>
wait(firstArg, ...args: any[]): Chromeless<T> {
switch (typeof firstArg) {
Expand All @@ -99,7 +105,7 @@ export default class Chromeless<T extends any> implements Promise<T> {
break
}
case 'string': {
this.queue.enqueue({ type: 'wait', selector: firstArg })
this.queue.enqueue({ type: 'wait', selector: firstArg, timeout: args[0] })
break
}
case 'function': {
Expand Down Expand Up @@ -174,6 +180,12 @@ export default class Chromeless<T extends any> implements Promise<T> {
return this
}

scrollToElementArrayElements(selector: string, arrayNumber: number): Chromeless<T> {
this.queue.enqueue({ type: 'scrollToElementArrayElements', selector, arrayNumber })

return this
}

setViewport(options: DeviceMetrics): Chromeless<T> {
this.queue.enqueue({ type: 'setViewport', options })

Expand Down Expand Up @@ -217,6 +229,16 @@ export default class Chromeless<T extends any> implements Promise<T> {
return new Chromeless<boolean>({}, this)
}

existsArrayElement(selector: string, arrayNumber: number): Chromeless<boolean> {
this.lastReturnPromise = this.queue.process<boolean>({
type: 'returnArrayElementExists',
selector,
arrayNumber
})

return new Chromeless<boolean>({}, this)
}

screenshot(): Chromeless<string> {
this.lastReturnPromise = this.queue.process<string>({
type: 'returnScreenshot',
Expand Down
69 changes: 62 additions & 7 deletions src/chrome/local-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import * as os from 'os'
import * as path from 'path'
import {
nodeExists,
nodeArrayElementsExists,
wait,
waitForNode,
waitForNodeArrayElements,
click,
clickArrayElements,
evaluate,
screenshot,
html,
Expand All @@ -24,6 +27,7 @@ import {
getValue,
scrollTo,
scrollToElement,
scrollToElementArrayElements,
setHtml,
press,
setViewport,
Expand Down Expand Up @@ -57,10 +61,10 @@ export default class LocalRuntime {
case 'setViewport':
return setViewport(this.client, command.options)
case 'wait': {
if (command.timeout) {
if (command.selector) {
return this.waitSelector(command.selector, command.timeout)
} else if (command.timeout) {
return this.waitTimeout(command.timeout)
} else if (command.selector) {
return this.waitSelector(command.selector)
} else {
throw new Error('waitFn not yet implemented')
}
Expand All @@ -71,10 +75,14 @@ export default class LocalRuntime {
return this.setUserAgent(command.useragent)
case 'click':
return this.click(command.selector)
case 'clickArrayElements':
return this.clickArrayElements(command.selector, command.arrayNumber)
case 'returnCode':
return this.returnCode(command.fn, ...command.args)
case 'returnExists':
return this.returnExists(command.selector)
case 'returnArrayElementExists':
return this.returnArrayElementExists(command.selector, command.arrayNumber)
case 'returnScreenshot':
return this.returnScreenshot()
case 'returnHtml':
Expand All @@ -91,6 +99,8 @@ export default class LocalRuntime {
return this.scrollTo(command.x, command.y)
case 'scrollToElement':
return this.scrollToElement(command.selector)
case 'scrollToElementArrayElements':
return this.scrollToElementArrayElements(command.selector, command.arrayNumber)
case 'deleteCookies':
return this.deleteCookies(command.name, command.url)
case 'clearCookies':
Expand Down Expand Up @@ -149,9 +159,12 @@ export default class LocalRuntime {
await wait(timeout)
}

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

Expand All @@ -178,6 +191,30 @@ export default class LocalRuntime {
this.log(`Clicked on ${selector}`)
}

private async clickArrayElements(selector: string, arrayNumber: number): Promise<void> {
if (this.chromelessOptions.implicitWait) {
this.log(`clickArrayElements(): Waiting for ${selector}`)
await waitForNodeArrayElements(
this.client,
selector,
arrayNumber,
this.chromelessOptions.waitTimeout,
)
}

const exists = await nodeArrayElementsExists(this.client, selector, arrayNumber)
if (!exists) {
throw new Error(`clickArrayElements(): node for selector ${selector} doesn't exist`)
}

const { scale } = this.chromelessOptions.viewport
if (this.chromelessOptions.scrollBeforeClick) {
await scrollToElementArrayElements(this.client, selector, arrayNumber)
}
await clickArrayElements(this.client, selector, arrayNumber, scale)
this.log(`Clicked on ${selector}`)
}

private async returnCode<T>(fn: string, ...args: any[]): Promise<T> {
return (await evaluate(this.client, fn, ...args)) as T
}
Expand All @@ -198,6 +235,19 @@ export default class LocalRuntime {
return scrollToElement(this.client, selector)
}

private async scrollToElementArrayElements<T>(selector: string, arrayNumber: number): Promise<void> {
if (this.chromelessOptions.implicitWait) {
this.log(`scrollToElementArrayElements(): Waiting for ${selector}`)
await waitForNodeArrayElements(
this.client,
selector,
arrayNumber,
this.chromelessOptions.waitTimeout,
)
}
return scrollToElementArrayElements(this.client, selector, arrayNumber)
}

private async mousedown(selector: string): Promise<void> {
if (this.chromelessOptions.implicitWait) {
this.log(`mousedown(): Waiting for ${selector}`)
Expand Down Expand Up @@ -347,6 +397,10 @@ export default class LocalRuntime {
return await nodeExists(this.client, selector)
}

async returnArrayElementExists(selector: string, arrayNumber: number): Promise<boolean> {
return await nodeArrayElementsExists(this.client, selector, arrayNumber)
}

async returnInputValue(selector: string): Promise<string> {
const exists = await nodeExists(this.client, selector)
if (!exists) {
Expand All @@ -364,7 +418,8 @@ export default class LocalRuntime {
process.env['CHROMELESS_S3_BUCKET_NAME'] &&
process.env['CHROMELESS_S3_BUCKET_URL']
) {
const s3Path = `${cuid()}.png`
const prefix = process.env['CHROMELESS_S3_OBJECT_KEY_PREFIX'] || ''
const s3Path = `${prefix}${cuid()}.png`
const s3 = new AWS.S3()
await s3
.putObject({
Expand Down
Loading