gpt4 book ai didi

typescript - 对 Spectron 中的 ipcMain 事件作出 react

转载 作者:行者123 更新时间:2023-12-03 12:34:29 24 4
gpt4 key购买 nike

我有一个 Electron 应用程序,它首先启动一个启动器窗口(在渲染器进程中),它启动多个后台服务。在这些后台服务成功启动后,它会发送 "services-running"在其ipcRenderer回到主进程,该进程又通过关闭启动器窗口并启动主应用程序窗口来对该事件作出 react 。该事件当然由 ipcMain.on('services-running',...) 接收。

我分别对所有处理程序进行了单元测试,所以这些都很好,现在我想集成测试通过 ipcMain 的事件.

这就是我的集成测试目前的样子:

import { Application } from 'spectron';
import * as electron from "electron";
import { expect } from 'chai';
import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';

let app: Application;

global.before(() => {
app = new Application({
path: "" + electron,
args: ["app/main.js"],
env: {
ELECTRON_ENABLE_LOGGING: true,
ELECTRON_ENABLE_STACK_DUMPING: true,
NODE_ENV: "integrationtest"
},
startTimeout: 20000,
chromeDriverLogPath: '../chromedriverlog.txt'
});

chai.use(chaiAsPromised);
chai.should();
});

describe('Application', () => {

before('Start Application', () => {
return app.start();
});

after(() => {
if(app && app.isRunning()){
return app.stop();
}
});

it('should start the launcher', async () => {
await app.client.waitUntilWindowLoaded();
return app.client.getTitle().should.eventually.equal('Launcher');
});

it('should start all services before timing out', async (done) => {
console.log('subscribed');
app.electron.remote.ipcMain.on('services-running', () => {
done();
});
});

});

第一个测试工作正常。尽管我可以看到 subscribed,但在达到超时后,第二次测试最终会失败。在弹出主窗口之前的 shell 上,因此肯定会触发该事件。

我在文档中读到 nodeIntegration需要启用以使用 spectron 访问完整的 Electron api,我所有的渲染器进程都以 {nodeIntegration: true} 开始在他们各自的 webPreferences .但是由于我对主进程感兴趣,我认为这不适用(或者至少我认为它不应该,因为主进程本身就是一个节点进程)。

所以我的主要问题是,我将如何绑定(bind)到 ipcMain事件并将其包括在我的断言中。另外我怎么知道启动器窗口何时关闭并且“主”窗口已打开?

作为奖励,我对 spectron api 有一些理解问题。
  • 如果我查看 spectron.d.ts electron Application 的属性(property)是 Electron.AllElectron 类型这又是 MainInterface并直接拥有ipcMain属性(property)。因此,据我了解,访问 ipcMain应该是 app.electron.ipcMain (未定义),远程来自哪里以及为什么它在 spectron.d.ts 中不可见.
  • SpectronClient 上的方法全部返回 Promise<void> .所以我必须awaitthen那些。如果我查看 javascript 示例,它们会链接客户端语句:
  • return app.client
    .waitUntilWindowLoaded()
    .getTitle().should.equal('Launcher');

    这在 typescript 中不起作用,因为您不能链接到 Promise<void>显然,......这在js中如何工作?

    最佳答案

    所以我分别解决了这些问题。我将所有内容迁移到类并使用字段/构造函数注入(inject)将所有依赖项放入我的类中,因此我可以模拟它们,包括来自 Electron 的东西。

    export class LauncherRenderer implements Renderer {

    protected mongo: MongoProcess;
    protected logger: Logger;
    protected ipc: IpcRenderer;

    protected STATUS_LABEL: string = 'status-text';

    constructor() {
    this.ipc = ipcRenderer;

    this.mongo = new MongoProcess(this.ipc);

    this.logger = new Logger('launcher', this.ipc);
    }

    在类里面我会一直使用 this.ipc订阅事件时。对于单元测试,我有一个 FakeIpc类(class):
    import { EventEmitter } from 'events';

    export class FakeIpc {

    public emitter: EventEmitter = new EventEmitter();

    public send(channel: string, message?: any): void { }

    public on(event: string, listener: () => void): void {
    this.emitter.on(event, listener);
    }

    public emit(event: string): void {
    this.emitter.emit(event);
    }
    }

    LauncherRenderer 设置单元测试时我注入(inject) FakeIpc进入渲染器:
     beforeEach(() => {
    fakeIpc = new FakeIpc();
    spyOn(fakeIpc, 'on').and.callThrough();
    spyOn(fakeIpc, 'send').and.callThrough();

    mongoMock = createSpyObj('mongoMock', ['start', 'stop', 'forceStop']);

    underTest = new LauncherRenderer();

    underTest.mongo = mongoMock;
    underTest.ipc = fakeIpc;
    });

    这样,如果订阅已经完成,我可以监视 ipc,或者使用公共(public) trigger拍摄 ipc 事件并测试我的类(class)是否对它做出正确 react 的方法。

    对于集成测试,我认识到我不应该关心事件之类的内部结构(这是在单元测试中完成的),只关心那些结果(窗口关闭和打开)。像这样:
        it('should start the launcher', async () => {
    await app.client.waitUntilWindowLoaded();
    const title: string = await app.client.getTitle();
    expect(title).toEqual('Launcher');
    });

    在下一个测试中,我等到启动器消失并打开一个新窗口,这样事件必须有效,否则不会发生。
        it('should open main window after all services started within 120s', async () => {
    let handles: any = await app.client.windowHandles();

    try {
    await Utils.waitForPredicate(async () => {
    handles = await app.client.windowHandles();
    return Promise.resolve(handles.value.length === 2);
    }, 120000);
    await app.client.windowByIndex(1);
    } catch (err) {
    return Promise.reject(err);
    }

    const title: string = await app.client.getTitle();
    expect(title).toEqual('Main Window');
    });
    waitForPredicate只是一个帮助方法,它等待 promise 解决或在达到超时后终止测试。
    public static waitForPredicate(
    predicate: () => Promise<boolean>,
    timeout: number = 10000,
    interval: number = 1000,
    expectation: boolean = true): Promise<void> {
    return new Promise<any>(async (res, rej) => {
    let currentTime: number = 0;
    while (currentTime < timeout) {
    // performance.now() would be nicer, but that doesn't work in jasmin tests
    const t0: number = Date.now();
    const readyState: boolean | void = await predicate().catch(() => rej());
    if (readyState === expectation) {
    res();
    return;
    }
    await Utils.sleep(interval);
    const t1: number = Date.now();
    currentTime += t1 - t0;
    }
    // timeout
    rej();
    });
    }

    public static sleep(ms: number): Promise<void> {
    if (this.skipSleep) {
    return Promise.resolve();
    }
    return new Promise<void>((res) => setTimeout(res, ms));
    }

    关于typescript - 对 Spectron 中的 ipcMain 事件作出 react ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56889026/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com