Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 对Spectron中的主要事件作出反应_Typescript_Electron_Spectron_Ipcmain - Fatal编程技术网

Typescript 对Spectron中的主要事件作出反应

Typescript 对Spectron中的主要事件作出反应,typescript,electron,spectron,ipcmain,Typescript,Electron,Spectron,Ipcmain,我有一个electron应用程序,它首先启动一个启动程序窗口(在渲染器进程中),启动几个后台服务。成功启动这些后台服务后,它将其IPC渲染器上的“正在运行的服务”发送回主进程,主进程通过关闭启动器窗口并启动主应用程序窗口对该事件作出反应。当然,ipcMain.on('services-running',…) 我分别对所有处理程序进行了单元测试,因此这些处理程序都很好,现在我想对通过ipcMain的事件进行集成测试 这就是我的集成测试目前的情况: import { Application } fr

我有一个electron应用程序,它首先启动一个启动程序窗口(在渲染器进程中),启动几个后台服务。成功启动这些后台服务后,它将其
IPC渲染器上的
“正在运行的服务”
发送回主进程,主进程通过关闭启动器窗口并启动主应用程序窗口对该事件作出反应。当然,
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();
        });
    });

});
第一个测试运行良好。虽然在主窗口弹出之前,我可以在shell上看到
subscribed
,但在达到超时后,第二个测试最终将失败,因此事件肯定会触发

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

所以我的主要问题是,我如何绑定到
ipcMain
事件,并将这些事件包含在我的断言中。还有,我如何知道启动器窗口何时关闭,“主”窗口何时打开

作为奖励,我对spectron api有一些理解上的问题

  • 如果我看一下
    spectron.d.ts
    应用程序的
    electron
    属性属于
    electron.allegectron
    类型,它又是
    main接口
    ,直接具有
    ipcMain
    属性。因此,在我的理解中,访问
    ipcMain
    应该是
    app.electron.ipcMain
    (未定义),远程设备来自何处,为什么它在
    spectron.d.ts
    中不可见

  • SpectronClient
    上的方法全部返回
    Promise
    。所以我必须等待
  • 然后
    这些。如果我看一下javascript示例,它们会链接客户机语句:


    这在typescript中不起作用,因为您无法链接到
    承诺
    显然,。。。这在js中是如何工作的?

    因此我分别解决了这些问题。我将所有内容迁移到类中,并使用字段/构造函数注入将所有依赖项放入类中,这样我就可以模拟它们,包括来自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
    类:

    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);
        }
    }
    
    启动器渲染器设置单元测试时,我将
    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事件,并测试我的类是否正确响应它

    对于我认识到的集成测试,我不应该关心内部事件(这是在单元测试中完成的),只关心这些事件的结果(窗口关闭和打开)。像这样:

        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
    仅仅是一个助手方法,它在到达超时后等待承诺来解析或终止测试

    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));
    }
    
    公共静态waitForPredicate(
    谓词:()=>承诺,
    超时:数字=10000,
    间隔:数字=1000,
    期望值:布尔值=真):承诺{
    返回新承诺(异步(res,rej)=>{
    设currentTime:number=0;
    while(当前时间<超时){
    //performance.now()会更好,但这在jasmin测试中不起作用
    常量t0:number=Date.now();
    const readyState:boolean | void=wait谓词().catch(()=>rej());
    如果(readyState==期望值){
    res();
    回来
    }
    等待效用。睡眠(间隔);
    常数t1:number=Date.now();
    电流时间+=t1-t0;
    }
    //超时
    rej();
    });
    }
    公共静态睡眠(ms:number):承诺{
    如果(这个跳过睡眠){
    返回承诺。解决();
    }
    返回新承诺((res)=>setTimeout(res,ms));
    }
    
    您在这方面运气好吗?我也试着去听一些主要的事件…@格言我把我的变通方法作为一个答案。我认为这是一个关于如何处理模仿内部库内容的通用解决方案。从那时起,我们就一直在使用这种方法,在其他语言的一些项目中也使用了这种方法,目前它仍然有效。太好了,非常感谢!只是一个关于waitForPredicate的问题。它看起来像一个整洁的实用程序。你如何实现它?您是否以某种方式使用setInterval()检查谓词?@motoson我添加了它:)超级!再次感谢!=)
    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));
    }