javascript - 如何自动化 ElectronJS 应用程序
问题描述
我们正在为我们的桌面工作开发一个用于特定网站自动化的 ElectronJS 应用程序,其中包括登录、表单填写、报告下载等常见任务。
我们已经尝试过 ElectronJS、Spectron、NightmareJS、Puppeteer 等的基本教程,它们都可以单独工作,但是在相互集成时可用的文档非常少(尽管开放的 github 问题)。
我们要实现以下目标:
- 登录状态 (
session
) 不应该在 ElectronJS 应用程序关闭时被删除,并且应该在应用程序重新启动时可用。 - 很少有菜单按钮可以启动一些自动化任务,例如现有的下载、表单填写等
browserWindow
我们不需要无头自动化,在幕后会发生一些神奇的事情。我们只需要当前页面上的基于菜单/按钮单击的操作/任务。
NightmareJS
等Puppeteer
似乎都启动了自己的网页实例(因为它们是为测试独立应用程序而构建的),但我们需要的是现有BrowserWindows
.
是实现这些目标puppeteer
的nightmarejs
正确工具吗?如果是,有任何文件吗?
或者,我们是否应该mouseclick
在控制台中注入我们自己的原生 JS 事件(如 etc 事件)来执行操作?
解决方案
您可以使用puppeteer-core
. core
默认情况下,该版本不下载 Chromium,如果您想控制 Electron 应用程序,则不需要它。
然后在测试中调用launch
方法,在该方法中定义electron
为可执行文件而不是 Chromium,如下面的代码片段所示:
const electron = require("electron");
const puppeteer = require("puppeteer-core");
const delay = ms =>
new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
(async () => {
try {
const app = await puppeteer.launch({
executablePath: electron,
args: ["."],
headless: false,
});
const pages = await app.pages();
const [page] = pages;
await page.setViewport({ width: 1200, height: 700 });
await delay(5000);
const image = await page.screenshot();
console.log(image);
await page.close();
await delay(2000);
await app.close();
} catch (error) {
console.error(error);
}
})();
电子 5.xy 及更高版本的更新(目前最高为 7.xy,我尚未在 8.xy beta 上对其进行测试),puppeteer.connect
使用 where 代替launch
方法:
// const assert = require("assert");
const electron = require("electron");
const kill = require("tree-kill");
const puppeteer = require("puppeteer-core");
const { spawn } = require("child_process");
let pid;
const run = async () => {
const port = 9200; // Debugging port
const startTime = Date.now();
const timeout = 20000; // Timeout in miliseconds
let app;
// Start Electron with custom debugging port
pid = spawn(electron, [".", `--remote-debugging-port=${port}`], {
shell: true
}).pid;
// Wait for Puppeteer to connect
while (!app) {
try {
app = await puppeteer.connect({
browserURL: `http://localhost:${port}`,
defaultViewport: { width: 1000, height: 600 } // Optional I think
});
} catch (error) {
if (Date.now() > startTime + timeout) {
throw error;
}
}
}
// Do something, e.g.:
// const [page] = await app.pages();
// await page.waitForSelector("#someid")//
// const text = await page.$eval("#someid", element => element.innerText);
// assert(text === "Your expected text");
// await page.close();
};
run()
.then(() => {
// Do something
})
.catch(error => {
// Do something
kill(pid, () => {
process.exit(1);
});
});
获取pid
和使用kill
是可选的。对于在某些 CI 平台上运行脚本无关紧要,但对于本地环境,您必须在每次尝试失败后手动关闭电子应用程序。
简单的演示仓库: https ://github.com/peterdanis/electron-puppeteer-demo
推荐阅读
- java - 反序列化对象列表的困难
- python - 我们是否需要在写入和写入行时处理打开文件的错误?
- animation - swiftui在使用lazyvgrid的滚动视图中滚动太快时冻结
- reactjs - React-Leaflet:如何清除功能组并使用状态添加新组件?
- c++ - 调用 void 函数后删除行尾
- python - 锁定失败!pipenv 安装请求
- material-ui - 我在使用 Matrial ui 图标时遇到了一些问题
- next.js - Next.js 动态路由模板
- javascript - 警告:无法在尚未安装的组件上调用 setState。调用 API 时
- go - 在 Go 中将 Athena 输出转换为强类型模型