脚本を用いた電子アプリのテスト


プロジェクトとチームがちょうど始まっているとき、テストは通常主な焦点でありません.しかし、プロジェクトが成長し、より多くのユーザーを得るように、より重点を置くテスト、特に、再利用することができますし、開発タスクの一部として行うことができる自動テストが置かれます.
この点において、Monokleは他のプロジェクトやチームとは異なるものではなく、地面を走らせて何かをビルドする必要があります.現在、私たちは、新しい機能が追加されるとき、コア機能が意図的に影響されないことを確認する必要がある位置にいます.したがって、私たちの夜間のビルドで走るベースライン統合テストをすることは、全く意味をなします.
でもねえ.決して遅くないので、電子と統合テストで我々の話は始まります.
モノクルは、反応のようなWebフレームワークを使用して複数のプラットフォーム間でデスクトップアプリケーションを構築するために使用される電子を使用して構築されています.そこに複数のテストフレームワークがありますが、非常に少数のOSのテストを実行することができますいくつかのmonokleなどの電子アプリケーションをテストします.私たちは脚本家を使うことに決めました-それは両方のオープンソースで、電子支持を焼いたようです.
Playwright他のテストフレームワーク(セレン、サイプレス)と同様に動作しますこれは、実際のアプリケーションを起動し、ユーザーが行うアクションを模倣する、要素をクリックして、テキスト入力で物事を書く、別の流れを経て行く.アサーションは、UIで期待される結果が起こることを確認するために追加されます.たとえば、パネルの開きやラベルの変更などです.より多くの時間がかかりますが、この方法でアプリケーションを起動するのは、ソースコードから実行するのに適しています.小さな違いがあるかもしれません.

実際のテストに飛び込みましょう


依存関係のインストール


まず、いくつかの依存関係をインストールする必要があります.npm install –save-dev @playwright/test playwright playwright-core xvfb-maybe asar
  • PlayWrightは、我々が使用しているテストライブラリです
  • xvfbはテストを実行するために使用されます.
  • asarはosパッケージを解析するために使われます.
  • 設定ファイルの作成


    脚本家をつくってください.設定.以下のようになります.
    import {PlaywrightTestConfig} from '@playwright/test';
    
    const config: PlaywrightTestConfig = {
    
     testDir: './<test-directory>',
    
     timeout: 60000,
    
     expect: {
    
       toMatchSnapshot: {threshold: 0.2},
    
     },
    
    };
    
    
    export default config;
    

    書き板を書く


    解析し、電子の最終的なビルドを実行するにはhere .
    これらの非常に有用な機能の信用はSpaceage 誰が彼らを作ったのですか.
    これらの関数をelectronHelpers.ts ファイル.
    同じファイルでは、実際のアプリケーションを起動するために我々のテストで使用されるstartapp関数を作成します.
    /**
    
    * Find the latest build and start monokle app for testing
    
    */
    
    export async function startApp(): Promise<StartAppResponse> {
    
     // find the latest build in the out directory
    
     const latestBuild = findLatestBuild();
    
     // parse the directory and find paths and other info
    
     const appInfo = parseElectronApp(latestBuild);
    
     const electronApp = await electron.launch({
    
       args: [appInfo.main],
    
       executablePath: appInfo.executable,
    
       recordVideo: {
    
         dir: '<recording-path>',
    
         size: {
    
           width: 1200,
    
           height: 800
    
         },
    
       },
    
     });
    
    
    /**
    
     // wait for splash-screen to pass
    
     await electronApp.firstWindow();
    
     while (electronApp.windows().length === 2) {
    
       // eslint-disable-next-line no-await-in-loop
    
       await pause(100);
    
     }
    
    
     const windows = electronApp.windows();
    
     if (windows.length !== 1) {
    
       throw new Error('too many windows open');
    
     }
    
     const appWindow: Page = windows[0];
    
     appWindow.on('console', console.log);
    
    
    /**
    
     // Capture a screenshot.
    
     await appWindow.screenshot({
    
       path: 'screenshots/initial-screen.png'
    
     });
    
    
     return {appWindow, appInfo, electronApp};
    
    }
    
    
    最後に、電子ヘルパー.tsは次のようになります.
    export async function startApp(): Promise<StartAppResponse> {
    
      ...
    
    }
    
    
    export async function findLatestBuild() {
    
      ...
    
    }
    
    
    export async function parseElectronApp() {
    
      ...
    
    }
    

    実際のテストの作成


    import {Page} from 'playwright';
    
    import {test} from '@playwright/test';
    
    import {ElectronApplication} from 'playwright-core';
    
    import {ElectronAppInfo, startApp} from './electronHelpers';
    
    
    let appWindow: Page;
    
    let appInfo: ElectronAppInfo;
    
    let electronApp: ElectronApplication;
    
    
    test.beforeAll(async () => {
    
     const startAppResponse = await startApp();
    
     appWindow = startAppResponse.appWindow;
    
     appInfo = startAppResponse.appInfo;
    
     electronApp = startAppResponse.electronApp;
    
    });
    
    
    test('test click', async () => {
    
     // Click button.
    
     await appWindow.click('text=Click me');
    
    // Exit app.
    
     await electronApp.close();
    
    });
    
    
    test.afterAll(async () => {
    
     await appWindow.screenshot({path: 'screenshots/final-screen.png'});
    
     await appWindow.context().close();
    
     await appWindow.close();
    
    });
    
    上記のコードスニペットは、ウェブサイトをテストするのと非常に似ている電子アプリで何ができるかについて、かなり良い考えを与えることができます.また、方法については、PlayWrightのウェブサイト上の広範なドキュメントがあります:アクセス要素、クリック、テキスト入力やその他の値、スクロールなどで入力します.
    テストを実行するには、スクリプトをスクリプトに追加します.JSONファイルこのコマンドxvfb-maybe npx playwright test - and run it with “npm run <script name>”

    UIテストのためのモデルの使用


    電子的なものではないが、脚本家のライブラリはモデルを使っている.https://playwright.dev/docs/test-pom .
    これらのモデルの周りのコードを構成すると、コードの再利用性、コード書き込みに関する軽いテスト、テストが何をすべきかを理解しやすくなります.
    POMSはまた、いくつかのUIリファクタリングがドアにノックしているときにコードがより汎用的な方法で構造化されている場合に役立ちます.
    モデルがどのように見えるかの例です.
    import {Locator, Page} from 'playwright';
    
    
    export class TestModal {
    
    
     private _page: Page;
    
    
     private readonly _okButton: Locator;
    
    
     constructor(page: Page) {
    
       this._page = page;
    
    
       this._okButton = page.locator('#button-identifier');
    
     }
    
    
     async clickSave() {
    
       await this._okButton.click();
    
     }
    
    
    }
    
    

    ipcrenderer <> ipcmainの間でOSファイルの選択や動作を変更する


    私たちがファイル選択フローを自動化しなければならなくなるまで、Playwrightを使っている私の時間は本当に素晴らしかったです.
    これは風のように聞こえる.簡単な方法でこれを行う方法についてのドキュメントを見つけることは困難でした.
    ファイルの選択ウィンドウを開く方法はdialog.showSaveDialogSync 電子フレームワークと劇作家からは、そのウィンドウをクリックすることはできませんので、ネイティブの電子メール環境内で作成されていません.
    テストの特定のファイルまたはフォルダを開く自動化するには、その特定のイベントの新しいハンドラーを追加する必要がありましたが、IPCチャネルを上書きすることはできませんし、1つだけリスナーを持つことができます.そこで次のようにしました.
  • アプリケーションが自動化モードにあるときに別のチャネルを持つアプリでいくつかのコードを注入する
  • テストでのみオートメーションフラグを使用してアプリケーションを起動します(これは他のユースケースで使用可能なアプリケーションを残します);
  • テストでは、アプリケーションを起動し、ハンドラをオーバーライドしたときと同じチャネル名を使用します.
  • const name = 'some name';
    
    const chanel = 'select-file-automation';
    
    await electronApp.evaluate(({ ipcMain }, params) => {
    
     ipcMain.handle(params.chanel, () => {
    
       return [params.name];
    
     });
    
    }, { chanel, name });
    
    このロジックを使用すると、簡単にモノクロのようなコードエディターにとって重要なフォルダーとファイルの操作などのOS固有の機能をテストすることができます.

    の自動化


    我々のテストのほとんどを得るために、我々は我々が昼間に何かを壊したかどうかを見ることができるように私たちの毎晩のビルドでそれらを追加しました.すべてのコミットで実行するためにそれらを追加しない理由は、すべてが実行するために~ 20分かかるということでした.
    我々は、スクリーンショットやレコードのビデオを取るために脚本家を設定して、これらのファイルは、GitHubアクションからアクセスすることができますだけでなく、どのようにテストが働いているか、失敗のリアルタイムビューを取得します.
    に移動します.

    以下はGithubアクションの一部として電子テストを実行するためのスニペットです.完全な流れがあるhere .
    name: ui-tests
    
    
    on:
    
    push:
    
    branches:
    
    - test-branch
    
    
    workflow_dispatch:
    
    
    jobs:
    
    ui-tests-mac:
    
    runs-on: macOS-latest
    
    
    steps:
    
    ... prepare for tests, install &build
    
    - name: E2E Test for Electron 🧪
    
    run: npm run ui-test
    
    
    - name: Upload Test Results 🗃
    
    if: always()
    
    uses: actions/upload-artifact@v2
    
    with:
    
    name: playwright-output
    
    path: test-output/**
    
    
    ui-tests-windows:
    
    runs-on: windows-latest
    
    
    steps:
    
    ... prepare for tests, install &build
    
    - name: E2E Test for Electron 🧪
    
    run: npm run ui-test
    
    
    - name: Upload Test Results 🗃
    
    if: always()
    
    uses: actions/upload-artifact@v2
    
    with:
    
    name: playwright-output
    
    path: test-output/**
    

    将来のステップ


    Monokleプロジェクトが成長するにつれて、テストを終了することができます.
    ブレイクアウトロジックelectronHelpers.ts ファイルを維持し、私たちのプロジェクトの独立して再利用することができますNPMライブラリに
    ブレイクアウト特定antd 他の人によってテストのために再利用されることができる図書館への論理(モーダル、木、ペイン)モデル;
    Monokleの一般的な機能のテストを展開します.
    私たちの作業テストの完全な例とそれらを実行する方法はhere と対応するdocumentation .
    いつものようにリリースからダウンロードすることができますGitHub そして、苦情を言うか、我々の上でMonokleを称賛してくださいDiscord Server - あなたからのお便りを楽しみにしています!