Skip to content

Commit

Permalink
feat: add htmlSelector option
Browse files Browse the repository at this point in the history
BREAKING CHANGE:

now the signature of `request` has been changed to:

```js
require(options)
// vs. previous
// require(options | options[], browserOptions)
```
  • Loading branch information
egoist committed Mar 13, 2022
1 parent 1e1dbf2 commit 087f1d9
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 38 deletions.
10 changes: 10 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/typescript-node/.devcontainer/base.Dockerfile

# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, 12-bullseye, 16-buster, 14-buster, 12-buster
ARG VARIANT="16-bullseye"
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}

# https://github.com/microsoft/vscode-dev-containers/blob/e5d1e95407309e972a37914cfd18a53c480bc1fa/script-library/docs/desktop-lite.md#installing-a-browser
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& curl -sSL https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -o /tmp/chrome.deb \
&& apt-get -y install /tmp/chrome.deb
35 changes: 35 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/typescript-node
{
"name": "Node.js & TypeScript",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick a Node version: 16, 14, 12.
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local on arm64/Apple Silicon.
"args": {
"VARIANT": "16-bullseye"
}
},

// Set *default* container specific settings.json values on container create.
"settings": {},


// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"dbaeumer.vscode-eslint"
],

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "yarn install",

// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node",
"features": {
"git": "latest"
}
}
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,18 @@ import { cleanup } from 'taki'
cleanup()
```

### Multiplate URLs
### Custom html selector

By default it returns the html for the entire document, but you can specify a selector to get the html for a specific element.

```js
request([
{ url: 'https://sao.js.org' },
{ url: 'https://sao.js.org/#/create' },
]).then((result) => {
// Then the result will an array of html string
})
const { request } = require('taki')

request({ url: 'https://example.com', htmlSelector: '.some-element' }).then(
(html) => {
console.log(html)
}
)
```

### Manually take snapshot
Expand Down
59 changes: 30 additions & 29 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export type RequestOptions = {
minify?: boolean
resourceFilter?: (ctx: ResourceFilterCtx) => boolean
blockCrossOrigin?: boolean
proxy?: string
headless?: boolean
sandbox?: boolean
htmlSelector?: string
}

async function getHTML(browser: Browser, options: RequestOptions) {
Expand Down Expand Up @@ -64,29 +68,38 @@ async function getHTML(browser: Browser, options: RequestOptions) {
}
return next()
})
let resolveFunction: Function | undefined
let resolveResult: Function | undefined
let content: string = ''
type Result = { content: string }
const promise = new Promise<Result>((resolve) => (resolveFunction = resolve))
const resultPromise = new Promise<Result>(
(resolve) => (resolveResult = resolve)
)
if (options.manually) {
const functionName =
typeof options.manually === 'string' ? options.manually : 'snapshot'
await page.exposeFunction(functionName, (result: any) => {
resolveFunction!(result)
resolveResult!(result)
})
}
await page.goto(options.url, {
waitUntil: options.manually ? 'domcontentloaded' : 'networkidle2',
})
let result: Result | undefined
if (options.manually) {
result = await promise
result = await resultPromise
} else if (typeof options.wait === 'number') {
await page.waitForTimeout(options.wait)
} else if (typeof options.wait === 'string') {
await page.waitForSelector(options.wait)
}
content = result ? result.content : await page.content()
content = result
? result.content
: options.htmlSelector
? await page.evaluate(
(selector) => document.querySelector(selector).innerHTML,
[options.htmlSelector]
)
: await page.content()
options.onBeforeClosingPage && (await options.onBeforeClosingPage(page))
await page.close()
options.onAfterRequest && options.onAfterRequest(options.url)
Expand All @@ -109,37 +122,25 @@ async function getHTML(browser: Browser, options: RequestOptions) {

let browser: Browser | undefined

export type BrowserOptions = { proxy?: string; headless?: boolean }

export async function request(
options: RequestOptions,
browserOptions?: BrowserOptions
): Promise<string>
export async function request(
options: RequestOptions[],
browserOptions?: BrowserOptions
): Promise<string[]>
export type BrowserOptions = {
proxy?: string
headless?: boolean
sandbox?: boolean
}

export async function request(
options: RequestOptions | RequestOptions[],
{ proxy, headless }: BrowserOptions = {}
) {
export async function request(options: RequestOptions) {
if (!browser) {
browser = await pptr.launch({
executablePath: findChrome(),
args: [proxy && `--proxy-server=${proxy}`].filter(truthy),
headless,
args: [
options.proxy && `--proxy-server=${options.proxy}`,
options.sandbox === false && '--no-sandbox',
].filter(truthy),
headless: options.headless,
})
}

try {
const result = Array.isArray(options)
? await Promise.all(options.map((option) => getHTML(browser!, option)))
: await getHTML(browser, options)
return result
} catch (error) {
throw error
}
return getHTML(browser, options)
}

export async function cleanup() {
Expand Down
16 changes: 15 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ test.after.always(async () => {
await cleanup()
})

test('basic', async t => {
test('basic', async (t) => {
const html = await request({
url: `http://localhost:${port}/basic.html`,
sandbox: false,
})
t.snapshot(html)
})
Expand All @@ -37,6 +38,7 @@ test('minify', async (t) => {
const html = await request({
url: `http://localhost:${port}/basic.html`,
minify: true,
sandbox: false,
})
t.snapshot(html)
})
Expand All @@ -45,6 +47,7 @@ test('wait for selector', async (t) => {
const html = await request({
url: `http://localhost:${port}/wait-for-selector.html`,
wait: '#bar',
sandbox: false,
})
t.snapshot(html)
})
Expand All @@ -53,6 +56,17 @@ test('manually', async (t) => {
const html = await request({
url: `http://localhost:${port}/manually.html`,
manually: true,
sandbox: false,
})

t.snapshot(html)
})

test('custom html selector', async (t) => {
const html = await request({
url: `http://localhost:${port}/html-selector.html`,
htmlSelector: '#app',
sandbox: false,
})

t.snapshot(html)
Expand Down
12 changes: 12 additions & 0 deletions test/server/html-selector.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">app</div>
</body>
</html>
6 changes: 6 additions & 0 deletions test/snapshots/index.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ Generated by [AVA](https://avajs.dev).
</style>␊
</body></html>`

## custom html selector

> Snapshot 1
'app'

## manually

> Snapshot 1
Expand Down
Binary file modified test/snapshots/index.test.ts.snap
Binary file not shown.
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"lib": [
"ES2019"
"ES2019",
"DOM"
] /* Specify library files to be included in the compilation. */,
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
Expand Down

0 comments on commit 087f1d9

Please sign in to comment.