multi-automator
Version:
Multi terminal automation
282 lines (281 loc) • 8.42 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* @desc: Web Handler
* @author: john_chen
* @date: 2023.03.13
*/
const fs_1 = require("fs");
const puppeteer_core_1 = __importStar(require("puppeteer-core"));
const Element_1 = __importDefault(require("./Element"));
const config_1 = require("../config");
;
/**
* web 操控类
*/
class WebHandler {
/**
* Web Operator
*
* @param {string} browserPath 浏览器地址,默认使用 chromium 地址
*/
constructor(browserPath) {
this.emulate = false;
this.model = 'Galaxy S5';
this.browserPath = browserPath;
this.launchOptions = {};
this.browser = null;
this.page = null;
this.screenSize = null;
}
/**
* init 参数处理
*
* @param launchOptions LaunchOptions
*/
checkInitOptions(launchOptions) {
launchOptions = launchOptions || {};
const { headless = true, devtools = false, defaultViewport = null, args = ['--start-fullscreen'], ignoreDefaultArgs = ['--enable-automation', '--enable-blink-features=IdleDetection'] } = launchOptions;
this.launchOptions = {
executablePath: this.browserPath,
headless,
devtools,
defaultViewport,
args,
ignoreDefaultArgs
};
}
/**
* init Web operator
*
* @param {boolean} headless 是否采用无头方案
* @param {boolean} devtools 是否启用调试工具
* @param {array{object}} cookies 需要设置的 cookies
* @param {string} emulate 是否开启设备模拟
* @param {object} headers headers
*/
async init(launchOptions, cookies, emulate = false, headers) {
config_1.logger.info('[web.init]');
await this.close();
// 初始化浏览器实例
this.checkInitOptions(launchOptions);
this.browser = await puppeteer_core_1.default.launch(this.launchOptions);
// 初始化页面实例
let pages = await this.browser.pages();
this.page = pages[0];
// 选定设备型号
if (emulate) {
this.emulate = true;
// @ts-ignore
await this.page.emulate(puppeteer_core_1.KnownDevices[this.model]);
}
// 设置 cookie
if (undefined !== cookies && cookies.length > 0) {
for (let cookie of cookies) {
await this.page.setCookie(cookie);
}
}
// 设置 header
if (undefined !== headers) {
await this.page.setExtraHTTPHeaders(headers);
}
}
/**
* 通过 xpath 获取元素操作对象列表
*
* @param {string} expression XPath表达式
* @returns {Promise<Array<ElementHandle>>}
*/
async $x(expression) {
let result = await this.page.$x(expression);
let elements = [];
for (let element of result) {
elements.push(new Element_1.default({
device: this,
element,
}));
}
return elements;
}
/**
* 通过 CSS 选择器获取元素操作对象
*
* @param selector CSS 选择器
* @returns {Promise<ElementHandle|null>}
*/
async $(selector) {
let element = await this.page.$(selector);
if (null === element) {
return null;
}
return new Element_1.default({
device: this,
element,
});
}
/**
* 通过 CSS 选择器获取元素操作对象列表
*
* @param selector CSS 选择器
* @returns {Promise<Array<ElementHandle>>}
*/
async $$(selector) {
let result = await this.page.$$(selector);
let elements = [];
for (let element of result) {
elements.push(new Element_1.default({
device: this,
element,
}));
}
return elements;
}
/**
* 屏幕点击
*
* @param {number} x 横坐标
* @param {number} y 纵坐标
* @return <Promise>
*/
async tap(x, y) {
let res = await this.page.touchscreen.tap(x, y);
return res;
}
/**
* 页面滑动
*
* @param {number} fx 起点横坐标
* @param {number} fy 起点纵坐标
* @param {number} tx 终点横坐标
* @param {number} ty 终点纵坐标
*/
async swipe(fx, fy, tx, ty) {
await this.page.mouse.move(fx, fy);
await this.page.mouse.wheel({ deltaX: fx - tx, deltaY: fy - ty });
}
/**
* 跳转 HTTP 地址
*
* @param {string} path url 路径
* @returns {string}
*/
async goto(path = '') {
for (let index = 0; index < 3; index++) {
try {
await this.page.goto(path, { waitUntil: 'networkidle2' });
break;
}
catch (err) {
config_1.logger.error(`[web.goto] page.goto fail ${index + 1} times: ${err.message}`);
continue;
}
}
return path;
}
/**
* 获取页面宽高
*
* @return {object} screenSize
* @return {number} screenSize.width 宽度,单位是像素
* @return {number} screenSize.height 高度,单位是像素
*/
async getScreenSize() {
if (this.emulate) {
let res = await this.page.viewport();
if (null === this.screenSize) {
this.screenSize = {
width: res.width * res.deviceScaleFactor,
height: res.height * res.deviceScaleFactor,
};
}
}
else {
let { width, height } = await this.page.evaluate(() => {
return {
width: window.innerWidth,
height: window.innerHeight,
deviceScaleFactor: window.devicePixelRatio,
};
});
this.screenSize = {
width,
height,
};
}
return this.screenSize;
}
/**
* 截图
*
* @param {string} path 图片路径
* @returns {Promise{Buffer|String}}
*/
async screenshot(path) {
let res = await this.page.screenshot(path);
if (path) {
(0, fs_1.writeFileSync)(path, res);
}
return res;
}
/**
* 页面 dom 树
*
* @return <Promise{string}>
*/
async source() {
let res = await this.page.content();
return res;
}
/**
* browser version
*
* @returns {string} 浏览器版本号
*/
async version() {
let res = await this.browser.version();
let version = res.split('/')[1];
return version;
}
/**
* 关闭设备
*/
async close() {
config_1.logger.info('[web.close]');
if (this.page && this.page.close) {
await this.page.close();
this.page = null;
}
if (this.browser && this.browser.close) {
await this.browser.close();
this.browser = null;
}
}
}
exports.default = WebHandler;
;