ice/tests/utils/browser.ts

120 lines
3.7 KiB
TypeScript
Raw Normal View History

import http from 'http';
import url from 'url';
import path from 'path';
import fse from 'fs-extra';
import puppeteer from 'puppeteer';
2022-01-27 14:32:38 +08:00
export interface IPage extends puppeteer.Page {
html?: () => Promise<string>;
$text?: (selector: string, trim?: boolean) => Promise<string | null>;
$$text?: (selector: string, trim?: boolean) => Promise<(string | null)[]>;
$attr?: (selector: string, attr: string) => Promise<string | null>;
$$attr?: (selector: string, attr: string) => Promise<(string | null)[]>;
2022-01-27 14:32:38 +08:00
push?: (url: string, options?: puppeteer.WaitForOptions & { referer?: string }) => Promise<puppeteer.HTTPResponse>;
}
interface IBrowserOptions {
cwd?: string;
port?: number;
server?: http.Server;
}
export default class Browser {
private server: http.Server;
private browser: puppeteer.Browser;
private baseUrl: string;
constructor(options: BrowserOptions) {
2022-01-27 14:32:38 +08:00
const { server } = options;
if (server) {
this.server = server;
} else {
const { cwd, port } = options;
this.server = this.createServer(cwd, port);
}
}
createServer(cwd: string, port: number) {
return http.createServer((req, res) => {
const requrl: string = req.url || '';
const pathname = `${cwd}${url.parse(requrl).pathname}`.split(path.sep).join('/');
2022-01-27 14:32:38 +08:00
if (fse.existsSync(pathname)) {
switch (path.extname(pathname)) { // set HTTP HEAD
case '.html':
res.writeHead(200, { 'Content-Type': 'text/html' });
break;
case '.js':
res.writeHead(200, { 'Content-Type': 'text/javascript' });
break;
case '.css':
res.writeHead(200, { 'Content-Type': 'text/css' });
break;
case '.gif':
res.writeHead(200, { 'Content-Type': 'image/gif' });
break;
case '.jpg':
res.writeHead(200, { 'Content-Type': 'image/jpeg' });
break;
case '.png':
res.writeHead(200, { 'Content-Type': 'image/png' });
break;
default:
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
});
}
fse.readFile(pathname, (_err, data) => {
res.end(data);
});
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 Not Found</h1>');
console.log(`${pathname} Not Found.`);
}
}).listen(port, '127.0.0.1');
}
async start() {
2022-01-27 14:32:38 +08:00
this.browser = await puppeteer.launch();
}
async close() {
if (!this.browser) { return; }
2022-01-27 14:32:38 +08:00
await this.browser.close();
this.server.close();
}
async page(url: string, disableJS?: boolean) {
2022-01-27 14:32:38 +08:00
this.baseUrl = url;
if (!this.browser) { throw new Error('Please call start() before page(url)'); }
const page: Page = await this.browser.newPage();
if (disableJS) {
page.setJavaScriptEnabled(false);
}
2022-01-27 14:32:38 +08:00
await page.goto(url);
page.push = (url, options) => page.goto(`${this.baseUrl}${url}`, options);
page.html = () =>
page.evaluate(() => window.document.documentElement.outerHTML);
page.$text = (selector, trim) => page.$eval(selector, (el, trim) => {
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent;
2022-01-27 14:32:38 +08:00
}, trim);
page.$$text = (selector, trim) =>
page.$$eval(selector, (els, trim) => els.map((el) => {
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent;
2022-01-27 14:32:38 +08:00
}), trim);
page.$attr = (selector, attr) =>
page.$eval(selector, (el, attr) => el.getAttribute(attr as string), attr);
page.$$attr = (selector, attr) =>
page.$$eval(
2022-01-27 14:32:38 +08:00
selector,
(els, attr) => els.map(el => el.getAttribute(attr as string)),
attr,
2022-01-27 14:32:38 +08:00
);
return page;
}
}