ElectronとはHTML、CSS、JavaScriptなどのWeb技術を使用してデスクトップアプリを作ることができるフレームワークです。
これを使用してプログラミング課題としてよく使われるオセロを実装してみたいと思います。
また今回はテストツールとしてJest、Cypressを使用します。
01.環境構築
02.基本開発
Electron環境設定
mac環境でパッケージ管理システムのhomebrew( https://brew.sh/index_ja )をインストールし以下のコマンドを 実行します。
brew install node
Windowsの場合homebrewは使わず https://nodejs.org/download/ )からダウンロードを行いダウンロードしてください。
Electronのインストールはプロジェクトローカルに行いグローバル環境に影響が出ないように行います。 (任意のディレクトリを作成してくだい。)
mkdir electron_reversi
cd electron_reversi
npm初期化
パッケージの設定が記載されるpackage.jsonを作成するコマンドです。
後ほど書き換えも可能な為まずはデフォルトのまま作成してください。
npm init
package.jsonのmainを書き換え以下の様にします。
こちらはnpm initではentry pointとされていた部分です。
インストール状態の確認のため一旦src配下の呼び出しにしますが後でまた変更します。
"main": “./main/main.js",
Electronのインストール
npmを使用しインストールします。
npm install --save-dev electron@5.0.4
※--save-devはローカルインストールを行うオプション です。
※@〜はバージョン指定です。指定なしで最新版をダウンロード出来ます。
起動
まずは起動できることを確認します。
npmから呼び出されるメインプロセスとメインプロセスから呼び出されるレンダラープロセスを作ります。
詳細を知りたい方はElectron公式ページをどうぞ。
// Electronのモジュールconst{app,BrowserWindow,ipcMain}=require('electron');// Electronの初期化完了後に実行app.on("ready",()=>{ //ウィンドウサイズを1280*720(フレームサイズを含まない)に設定する varmainWindow=newBrowserWindow({width:1280,height:720,useContentSize:true});//タイトル設定mainWindow.setTitle("reversi"); //使用するhtmlファイルを指定する mainWindow.loadURL(`file://${__dirname}/../dist/reversi.html`); // ウィンドウが閉じられたらアプリも終了 mainWindow.on("closed",()=>{ mainWindow=null; });});// 全てのウィンドウが閉じたら終了app.on("window-all-closed",()=>{ app.quit();});// メニューはリスタートのみconsttemplateMenu=[{label:'Game',submenu:[{label:'Restart',accelerator:'CmdOrCtrl+R',click(item,focusedWindow){if(focusedWindow)focusedWindow.reload()},}]}];
<!DOCTYPE html><html><head> <metacharset="UTF-8"><style>html{overflow:hidden;}</style></head><body> <p>Hello World</p></body></html>
実際に起動してみます。
./node_modules/.bin/electron .
このままだと起動コマンドが分かりづらいためnpm(package.json)にコマンドを登録します。
./node_modules/.bin/へのパスが通っているものとして記載できます。
"scripts":{ "start":"electron ."},
今後のアプリ起動は以下の様になります。
npm start
起動できなかった場合は一度見直してみてください。
エラーが出ている場合には検索してみてください。
トランスパイル
オセロでは石を置く判断や勝敗、パスなど複数の関数が必要になります。
分かりやすくソースを管理するためにes6に準じたファイル分割、クラス作成します。
現在のelectronではes6をそのまま実行できない為、webpack・babelで変換処理(=トランスパイル)を行います。
( https://ics.media/entry/16028/ )
インストール
npm install -D webpack webpack-cli babel-loader @babel/core @babel/preset-env
起動コマンドをnpm に登録
"scripts":{ "start":"electron .", "build":"webpack", "watch":"webpack -w"}
webpackの設定を記載
varmainConfig={ mode:"development", node:{ __dirname:false, __filename:false }, module:{ rules:[ { // 拡張子 .js の場合 test:/\.js$/, exclude:/node_modules/, use:[ { // Babel を利用する loader:"babel-loader" } ] } ] }, target:'electron-main', entry:{ "main":"./main/main.js" }};varrendererConfig={ mode:"development", node:{ __dirname:false, __filename:false }, module:{ rules:[ { // 拡張子 .js の場合 test:/\.js$/, exclude:/node_modules/, use:[ { // Babel を利用する loader:"babel-loader" } ] } ] }, target:'electron-renderer', entry:{ "reversi":"./renderer/reversi.js" }};module.exports=[mainConfig,rendererConfig];.babelrc{ presets:["@babel/preset-env"], "env":{ "test":{ "plugins":["transform-es2015-modules-commonjs"] } }}
トランスパイル用に空のjsファイルを作成し、
トランスパイルを実行します。
touch renderer/reversi.js
npm run build
出力されたらエントリーポイントを出力ディレクトリに変更します。
"main":“./dist/main.js",
これで起動すると先ほどと同じように出力が行われるはずです。
常駐トランスパイル
変更毎に手動でトランスパイルすると手間な時はターミナルに常駐させることもできます。
ソースの保存毎に自動でトランスパイルが走ります。
起動用のターミナルとは別に常駐ターミナルを開いておきましょう。
npm run watch
テスト設定
ここからはテスト駆動開発のための設定を行なっていきます。
テストツールのJEST、Electron用テストフレームワークSpectronをインストールします。
またbabelがテスト時にも動作するようプラグインも追加しておきます。
npm install --save-dev jest spectron babel-jest babel-plugin-transform-es2015-modules-commonjs
テストコマンドもnpmへ記載します。
"scripts": {
"start": "electron .",
"build": "webpack",
"watch": "webpack -w",
"test": "jest"
},
mainプロセステスト
アプリを起動しウィンドウが一つ表示されることを確認するテストを記載します。
constApplication=require('spectron').Applicationconstassert=require('assert')constelectronPath=require('electron')// Require Electron from the binaries included in node_modules. describe('Window',function(){ //タイムアウト時間を30秒に設定 jest.setTimeout(30000) letapp //テスト開始時に動作 beforeAll(function(){ //electronアプリのインスタンス作成 app=newApplication({ path:electronPath, args:[`/${__dirname}/..`] }); //アプリをスタート returnapp.start() }) //テスト終了時に動作 afterAll(function(){ //アプリをストップ returnapp.stop() }) it("アプリケーションを起動するとウィンドウが1つ表示される",function(){ app.client.getWindowCount().then((count)=>assert.equal(count,1)) })})
テストを実行します。
npm test
PASSと表示されていればテスト成功です。
テストが動作しない場合は一度再起動することで動作することがあるようです。
rendererテスト
レンダラーはページ毎の処理を行います。
関数レベルでテスト書いてみましょう。
importBoardfrom'../renderer/board';describe('Board',function(){ describe('put',()=>{ it('石置き関数では1と-1が交互に置かれる',()=>{ constboard=newBoard() board.put(1,2) expect(board.player).toBe(-1) board.put(2,2) expect(board.player).toBe(1) }) })})
npm testを実行するとCannot find moduleになります。
条件を満たすファイルを作成してみましょう。
exportdefaultclassPut_stone{ constructor(){ this.player=1; } put(x,y){ this.player=-this.player }}
テストを実行してグリーンになることを確認してください
結合テスト
表示、モジュールを組み合わせてのテストにはcypressを使います。
npm install cypress --save-dev
実行には以下のコマンドです。
停止しない限り常駐するので書き換え等を行う場合に新しいターミナルを開いておきましょう。
./node_modules/.bin/cypress open
出てくる画面でOK,got it!をクリックします。
cypress/integration内にあるテストモジュールが表示されます。
exampleフォルダ内には様々な例が保存されており、クリックすることでCypress用のテストサイトに対して流すことができます。
Run all specsは全てのテスト順次を流すボタンです。
cypress/integration内のexampleフォルダごと削除しても問題ありませんが
構文など調べる際には便利なのでひとまずそのままにしておきます。
Helloテスト
最初に作ったHello worldをテストしてみましょう。
保存した時点でcypressが起動済みなら自動で検知されテストが実施されます。
describe('reversi',function(){ it('initialize',function(){ cy.visit('/dist/reversi.html') cy.get('p').contains("Hello") })})
testコマンドではcypress配下のテストは実行したくないため
jsonを変更します。
"test": "jest test"
これでやっと環境構築ができました。
次でオセロの実装を行います。
02.基本開発