electron12 でdialogを呼び出す(ContextBridge)

electron12対応へ、前回は

右クリックメニュー(ContextMenu)カスタム処理をmain-processへ移動した(electron 12対応) 

 まで。

 nodeIntegrationはともかくmoduleは将来廃止になるとのこと。

npmパッケージで逃げることもできるが、まとめて移行することに。

つまりいろいろなものをrender-processからmain-processへ移動することになり、ContextBridgeの導入を避けることができない。

 

 # electron11まで使えたdialog呼び出しコード

これまではrender-process側の適当なjsファイル内で完結する下記2行程度の記述で ダイアログを表示することができたのだが、ようはこれが使えなくなる。

 ``` js/mypage.js

const {dialog} = require("electron").remote;

dialog.showMessageBoxSync(null)

```

 

# contextIsolationを有効化

 エラーメッセージによると、ContextBridgeを使うには、contextIsolationを有効化しなければならない

(electron12からデフォルトでtrue)

# ページ上のjsでdialogは呼べない(renderer-process)

ContextBridgeでmain-processへ飛ばしてそこからrequest('electron').dialogを呼ばなければならない。

```

MN@daisy-bell:lina_dicto/$ git diff lina_dicto/js/index.js         }catch(err){
-               message_dialog('error', 'user preference error', "user preference load error:\n" + err.message);
+               window.myApi.message_dialog('error', 'user preference error', "user preference load error:\n" + err.message);
        }
 
 }, false)
```


 

# preload.js内でdialogは呼べない

最初はpreload.js内でdialogを呼ぼうとしたのだが、なぜかrequire('electron')からcontextBridge, ipcRendererのようにdialogを取り出すことができない。

(preload.js内でcontextBridge, ipcRenderer以外が使えないということかもしれない。

後のContextBridgeのサンプルコードなども、見る限りpreload.js自体はrender-process側ということのように見えるし。)

 

# ContextBridgeでdialogを呼び出す

 


 

諸々の非同期を経由で呼び出しているが、 dialog.showMessageBoxSync()はウィンドウを渡せばウィンドウはモーダルに動作する(意図どおりブロックする)ように見える。

(まあとりあえずエラー通知にしか使ってないのでそこはどちらでもよしとする。 )

ページ上のjs -> preload.js上のcontextBridge -> main-processのjs と飛ばす。


 ``` js/index.js (renderer-process)

 window.myApi.message_dialog('error', 'HELLO', 'hello!');

 ```

```preload.js

 const { contextBridge, ipcRenderer, } = require("electron");
contextBridge.exposeInMainWorld(
    "myApi", {

        message_dialog: (strtype, strtitle, strmessage) => {

            ipcRenderer.invoke('tomain-message-dialog', strtype, strtitle, strmessage);

```

``` index.js(main-process)

const { ipcMain } = require('electron');
ipcMain.handle('tomain-message-dialog', async (event, strtype, strtitle, strmessage) => {
    const { dialog } = require('electron');
    dialog.showMessageBoxSync(

            // win, // ここmain-processで生成したwindowを指定できる
            {
                type: strtype,
                buttons: ['OK'],
                title: strtitle,
                message: strmessage,
            });
});
```

 

TODO: 確認ダイアログを出してOK,NGを同期で返す。

(現行の互換動作。あるいはこれは諦めてもいい。今は不要なので)

TODO: ファイル読み出し結果を戻す

 TODO: ところでcontextIsolationが有効な時に nodeIntegration: trueが 無効になってない?

 

 

右クリックメニュー(ContextMenu)カスタム処理をmain-processへ移動した(electron 12対応)

右クリックメニュー(ContextMenu)カスタム処理をmain-processへ移動した。

electron 12へ移行するための対応の一部。

(移行作業としてはelectron11で移行作業をしてから12にアップデートするつもり)。


``` エラーメッセージ

 Uncaught ReferenceError: Menu is not defined
```


 



 

Menuはmein-processでいじるものになっていた模様。

ApplicationMenuカスタム処理をmain-processへ移動した(electron 12対応)

を参照 。

 

main-processのindex.jsの先頭付近に移動した。

```

  7 require(path.join(__dirname, 'main-process/application-menu.js'))¬
  8 require(path.join(__dirname, 'main-process/context-menu.js'))

```

 electron-api-demosでは`  Menu.setApplicationMenu()`はコールバックに仕込んでいたが、コンテキストメニューではしていないので、踏襲してそのまま呼び出しで済ませることにした。

```

 10 const {Menu, MenuItem, app, ipcMain} = require('electron')¬
 11 let menu = new Menu();¬
 12 ¬
 13 menu.append(new MenuItem({¬
 14 >-------label: 'Copy',¬
 15 >-------accelerator: 'CmdOrCtrl+C',¬
 16 >-------role: 'copy'¬
 17 }));¬

 ```

 

またコンテキストメニュー表示のコールバックも、 renderer-processでしていたのをelectron-api-demosの通りにmain-processへ移した。

rendererとmain-processでイベント が違う。

Before:

``` (renderer-process)

 11 >------->-------window.addEventListener('contextmenu', function (e) {¬
 12 >------->------->-------e.preventDefault();¬
 13 >------->------->-------menu.popup(remote.getCurrentWindow());¬
 14 >------->-------}, false);¬

```

After: 'main-process/application-menu.js'(main-process)
 ```

app.on('browser-window-created', (event, win) => {
  win.webContents.on('context-menu', (e, params) => {
    menu.popup(win, params.x, params.y)
  })
})

ipcMain.on('show-context-menu', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender)
  menu.popup(win)
})

```

 

 

 

ApplicationMenuのときと同じく、Rendererに持たせたmoduleを呼び出す機能は動作しない。

 

そちらは後でpreload.js, ipcRendererなどを使う方式へ移行する。

ApplicationMenuカスタム処理をmain-processへ移動した(electron 12対応)

ApplicationMenu(画面上部のメニューバー)カスタム処理をmain-processへ移動した。

electron 12へ移行するための対応の一部。

(移行作業としてはelectron11で移行作業をしてから12にアップデートするつもり)。


``` エラーメッセージ

 Uncaught ReferenceError: require is not defined
    at menu.js:3

```

```menu.js:3

const remote = require('electron').remote;

```

 


 

だが根本的にはMenuはmein-processでいじるものになっていた模様。

electron-api-demos ではそうしている。

```electron-api-demos main.js

function loadDemos () {
  const files = glob.sync(path.join(__dirname, 'main-process/**/*.js'))
  files.forEach((file) => { require(file) })
}

``` 

ただしこの時点でelectron-api-demosはelectron9を使っていた。

(いろいろ言いたいことはあるが略)


Before:

``` index.html

<script type="text/javascript" src="./js/menu.js"></script>

```

After:

```

 require(path.join(__dirname, 'js/menu.js'))

```

これだけでもMenuカスタムが動作するようになってしまう。

ようは`  Menu.setApplicationMenu()`を呼んでしまえばOKのようだが、electron-api-demosではコールバックに仕込むことをオススメしている模様。

```

app.on('ready', () => {
  const menu = Menu.buildFromTemplate(template) // テンプレートを変数に格納
  Menu.setApplicationMenu(menu)
})

```


これをすることで、Menuカスタムは動作するし、Quit(App.quit()呼び出し)などは動作するが、Rendererに持たせたmoduleを呼び出す機能は動作しない。

 

そちらは後でpreload.js, ipcRendererなどを使う方式へ移行するとして、とりあえず次は右クリックのContextMenu。

食洗機の分岐水栓をDIY工事はあきらめてお願いした(結果的によかった)

 食洗機の『プチ食洗(NP-TCR3)』を購入しました。
食洗機にはビルトイン・据え置き水栓・据え置きタンク式、がありますが、購入したのは水栓をつなげる必要がある機種です。
わたしが購入前に参考にしたブログでは『買って取り付ければ終了だよ』で、『分岐水栓を取り付ける必要があるけれど自分でできるし簡単だよ』という感じでした。
おおむねそのとおりではあるものの、わたしの場合はイロイロあって業者さんに頼むことにし、結果的に頼んでよかった、と思ったのでその話を。





ちなみに、のちに出てくる蛇口と分岐水栓は以下。
蛇口 takagi JA200LN-NNDK01
分岐水栓 JH9024



# 設置前の準備
設置場所が必要なのもそうですが、ちょこちょこと以下のような必要なものが出てきます。

「分岐水栓」以外は、近所のスーパーやドラッグストアで当たり前に売っているようなものばかりなので、本体を買ってからつどつど集めるとかでも十分だと思います。
以前、3Dプリンタを買ったときは、必要な周辺小物の多さに驚き苦労しました。
それからすれば本当に少なく、3Dプリンタのように通販でしか手に入らないような専用品もほぼありません。
とはいえあらかじめ買っておいたほうが、設置と動作確認がスムーズです。

必要なもの
- 分岐水栓
- 食洗機専用洗剤

場合によって必要
- 電源延長コード
買った食洗機の電源コードは1mくらいでした。
食洗機の設置場所はおおむね水道の横、炊飯器等の家電から離れているため、我が家の場合は必要でした。

本体を中古で購入する場合、
- 給水ホース
- 排水ホース
がついてくるかどうか確認しておくといいです。
なければ家電量販店で買えるようです。


# なぜ水場工事業者さんに頼まなければならなかったか
『DIYでやってみて、だめだったら業者にお願いしよう』というのもアリだと思います。
(それで水道を壊したりしたら余計に出費がかさむことにはなりますが。)
というか私のばあい、その方針でやってみて、工事をする前に降参した流れになります。

## 1. 分岐水栓のとりつけには専用の工具が必要
購入前に参考にした、同じ蛇口と分岐水栓のブログ記事などでは、言及がなくて驚きました。

分岐水栓の取扱説明書を見ると、分岐水栓のとりつけには『「トルクレンチ」が必要』とあります。
このトルクレンチが厄介で、必要なのは「水道工事用の10Nのトルクレンチ」であるのに対し、市販されているのは車用の「40N以上のトルクレンチ」ばかりです。
水道工事用の10N前後のトルクレンチは、ホームセンタではとりあつかいがありません。
Webで見てみると、どうやら6000円スタートの業務用キットには含まれているようですが、あきらかに業務用で、Web上で軽く調べるだけでは、詳細情報を知ることができません。

どうやら、購入前に参考にしたブログ記事などの方々は適当に締めているようです。
家庭で特殊めな10Nのトルクレンチを持っていたのかもしれませんが。
また後に書く工事の様子を見た限り、それでも多くの場合はうまく行くのかもしれません。
専用工具で適切な強さで締める必要があるナットを、素人の自分がやって、水が飛び散って止まらないなどとなったら目もあてられないので、業者にお願いする判断をしました。

## 2. 蛇口や水道管が悪くなっていた場合に対処できない
分岐水栓を設置する水まわり工事をお願いするにあたって、ネットなどで水道工事は悪い業者に引っかかると見積もり以上のすごい額を請求される、という話をどこかで読んだことがあったので、事前にいろいろ確認しました。
その、電話口での概算見積もりの段階で「見積もりから大きく外れる事情にはどんな場合がありますか」と訊いてみたところ、「水道の蛇口が経年劣化でダメになっていて、蛇口の交換等が必要になってしまう場合があります」との回答をいただきました。

確かにDIYでそうなったら、蛇口の交換になって(まあそれもがんばればDIYできる範囲ではありますが)手に負えないだろうなと思いました。
(がんばれる人もいると思いますが、わたしはやめておきたい)

## 3. 蛇口が固着して素人工事が難しい場合がある(私の場合)
実際に工事をお願いして、作業者の方がとりつけ工事をしているのを間近で見学させてもらいました。

そのときに起こったことなのですが、蛇口の「化粧リング」が固着して外れませんでした。
この化粧リングは、ナットやパッキンのように固く締めて水漏れを防ぐような部品ではなくて、ネジ溝はあるけれど固定用で「かぶせてあるカバー」程度のものです。
しかし(それゆえなのか)、水道のカルキで固まってしまっていました。
わりと強めに蛇口を揺すったりしてみたものの、通常のレンチでは、化粧リングが外れず。
最終的に「蛇口に傷がついてしまいますが良いですか?」という確認のあと、見たことのないゴツめのレンチで無理やりねじ開けてもらいました。
(借家なので退出料に上乗せになるかもしれません。言われないと気づかない程度の跡なので大丈夫だろうだとは思っていますが。)

なお固着していたのは半分内側みたいな位置の部品だったため、日頃の掃除の有無は、固着の発生にあまり関係ないと思います。
住んでいるのは、わたしが入居した時点で築3年程度、住み始めてから2年程度なので、蛇口は設置してから長くて5年くらいのはずでした。


# 工事をお願いする際の注意

- 予備の部品とかを持ってきていないかも
これは、かなり推測混じりですが。
分岐水栓の付属品はあらかじめ確認しておいたほうがよいです。
工事中に『パッキンがないかも』という話が出て、そのときにけっこう真剣に探して、すぐに手持ちがあるような話にはなりませんでした。
こちらは確認したわけでないですし、汎用の水栓などは持ってきているのだと思います。
しかし分岐水栓は種類も多いので、部品の代替えがある期待はしないほうがよいかもです。

- 分岐水栓はあらかじめ買っておいて大丈夫
というか、分岐水栓に種類がたくさんあるのもあって、あらかじめ買っておかないと在庫や手持ちにない場合があるのかもしれません。
私の場合、上記のとおりあらかじめ買った状態で工事のお願いをしたので、そのへん持ち込みしてもらって別途購入できるのか等は、確認していません。

- 分岐水栓の取扱説明書をなくさない(工事経験のない機種かも)
自宅の蛇口はメーカがtakagiだったので、それなりにメジャな蛇口なのではと思っていたのですが、そうでもなかったようです。
作業者の方は、分岐水栓についてきた取扱説明書を見ながら作業、という感じになっていました。
分岐水栓の取扱説明書のほうに蛇口側のことについてはそれなりに書いてあったので蛇口の取扱説明書は不要でした。
(でもあるなら用意しておいたほうが良いと思います。)

WiFiルータ買い替えで、通信環境が改善(Archer AX20 WiFiルータ買い替えで、WiFi6対応子機でなくても遠くが高速になりました)

 Archer AX20 WiFiルータを購入しました。

[Aterm WG2200HP]を使っていたのですが、もうそろそろ数年になるので買い換えよう、という軽い動機です。

NEC ルータの設定画面が重い、というのもちょっとあります。

(設定画面なんて何もなければ数年は触りませんが、触るのは何かあったタイミングなので、遅いとストレスがある)

結果として、期待していなかったのですが思いがけずWiFi環境が改善して驚きました。

 

 


 

[Archer AX20 新世代Wi-Fi 6 無線LANルーター 1201+574Mbps MU-MIMO W PA3 Dバンド]

TP-Linkのルータは同じ製品に2つ以上の名前が ついているようで混乱しますが。

設定画面では[ AX1800 Wi-Fi 6 Router]と名乗っています。

 



 少し困ったのは、いちばん最初にWANにPPPoE接続でインターネットにつながるまで、時間がかかりました。

2回ほど接続失敗し、最終的に再起動などを試しているうちに接続されました。

以下、開封写真。

こんな感じで入っています。

最初、箱のサイズが大きいなと思ったのですが、面の大きさでは、NECやバッファローの箱型のルータの1.5倍サイズがあります。(厚みは少し薄い)


GPLライセンスの告知。

しっかりしてるの好感がもてます。

入っているLANケーブルはCET5eでした。別にそれで困らないとは思いますが、ウチではすでにあるCAT6ケーブルを使いました。

裏側。

裏側のネジ口ですが、写真で見て上下にハマるミゾがあり、左右方向にはありません。

なので壁掛けの際、アンテナが横に向くように釣ろうとすると、ひっかかりがなくてオチてしまうかもしれません。

またミゾは浅いので、壁掛けのフックでは厚みで うまくひっかかりません。

ダルマ型画鋲あたりが必要です。


 LANとWANの口。

写真では見づらいですが、電源含むボタン類もこの面に集まっています。

LEDがボタン一つで消灯できるのは嬉しかったです。 


電波強度について

家のドア3つ隔てた家の端でWiFi通信して、画像ダウンロード表示がストレスない程度に高速になりました。

(以前のNECルータを使っていたときは、この距離ではワンテンポあって、複数枚画像のあるページでは読み込みが10秒以上とか、Twitter読み込みに失敗することもありました)


我が家にWiFi6子機はなく、 経年で故障前にしておこうくらいの買い替えでした。

前のNECルータも5Ghz対応済みの機種で、WiFi6子機でなければ他のWiFi通信条件は同じだろうという感じです。

 

なので、高速化などは特に狙っていなかったのですが、WiFi環境が改善したため、思いがけず良い買い物でした。


spectronの新しい記法 (WebdriverIO v5対応その2)

electron9~10あたりで、UIテストツールであるspectronのバックエンドのwebdriverioが、v4から v5に更新されました。

呼び出しが非同期に変化して、これまでのメソッドチェーンを使う記法が動作しなくなったため、テストの全面書き直しが必要となります。

webdriverio自体に実践的なサンプルが見つからなかったりして難儀しましたが、いろいろ調べて、最終的に参考にしたのはwebdriverioのドキュメントでした。


https://webdriver.io/docs/api.html

```

it('can handle commands using async/await', async function () { const inputElement = await $('#input') let value = await inputElement.getValue() console.log(value) // outputs: some value })

```

 

以前のspectron(WebdriverIO v4のメソッドチェーン)

```

    it("input basic test", function() {
        return this.app.client.waitForVisible('#query-area__query-input__input')
                .waitForEnabled('#query-area__query-input__input')
                .clearElement('#query-area__query-input__input')
                .setValue('#query-area__query-input__input', "Bonan matenon!")
                .getValue('#query-area__query-input__input')
                .then(function(text){assert(text === 'Bonan matenon!')})
    })

```

新しいspectron(WebderiverIO v5のasync/await)

```

    it("input basic test", async function() {

        const inputElement = await this.app.client.$('#query-area__query-input__input');
        let text;
        text = await inputElement.getValue();
        console.log(`before:'${text}'`);
        await inputElement.waitForEnabled()
        await inputElement.clearValue()
        await inputElement.setValue("Bonan matenon!")
        text = await inputElement.getValue();
        console.log(`after:'${text}'`);
        assert(text === 'Bonan matenon!')
        // assert(text === '') // test to test (falure)
    })

``` 

今回書き換えたソース:

https://github.com/MichinariNukazawa/lina_dicto/blob/master/lina_dicto/test/input_ui.js


## Element がないことをテストする処理に時間がかかる?

```

        // このnot existテスト部分で5000msec近くかかるようだが詳細不明
        isExisting = await preprintElement.isExisting();
        assert(! isExisting); // not exist

```


以上です。

spectronの新しい記法 (WebdriverIO v5対応その1)

electronのUIテストであるspectronのバックエンドのwebdriverioが、v4から v5に更新されました。

呼び出しが非同期に変化して、これまでのメソッドチェーンを使う記法が動作しなくなったため、テストの全面書き直しが必要となります。

 


 

 

webdriverio自体に実践的なサンプルが見つからなかったりして難儀しました。

そのうちspectron公式サイトのトップにサンプルが載っているのを発見。

https://www.electronjs.org/spectron

基本のWindow表示テストですが、v4のテストコードを引き写してきた覚えのある手元の同等テストとはあれこれ違う感じです。

引き写したというのはREADME.md のmochaも使ったテストで、これ自体はv5環境下でも動作します。

 https://github.com/electron-userland/spectron

 

ここでは、v5対応の準備運動代わりに、サンプルにいろいろ追加してみました。

 

## CIテストに組み込むのでパスを固定値から自動判定に変更したい

 

```

const electronPath = require('electron') // Require Electron from the binaries included in node_modules.
const path = require('path')

var app = new Application({
    //path: '/Applications/MyApp.app/Contents/MacOS/MyApp'
    path: electronPath,
    args: [path.join(__dirname, '..')]
})

```

 

## assert()でエラーを検出した場合に、最終的に自動で停止してほしい

エラーが起こってcatch節に落ちると、Ctrl+CでKillするまでターミナルに帰ってこない。 

ローカルのターミナルでテストしている限りは少し不便なだけだが、このままではCIに都合が悪い(いずれタイムアウトするだろうが、早く終わらせてエラー通知させたい)。

catch節に落ちた際に、能動的に`app.stop()`を呼ぶことでテストが終了しターミナル操作が帰ってくる。

```

    return app.stop()
}).catch(function (error) {
    // Log any failures
    console.error('Test failed', error.message)
    app.stop() // ++ exit test and return to terminal control.

})

```

 

## assert()でエラーを検出した場合に、ターミナルにエラーコードを返す

サンプルコードのままでは、ターミナルへの戻り値がゼロになっており、CIでエラー検出できない。

 これはassert()ではやらず、タイムアウトで実現した。

mochaテストフレームワークに、非同期テストのために 「done()を呼ばないとタイムアウトになる」という仕組みがあるので、それを利用した。

 

 ```

it("Test for Window", function(done){ // ++ In mocha testing framework
  app.start().then(function () {
    ~
  }).then(function () {
    // Stop the application
 
    done(); // ++ Test result to Success
    return app.stop()
  }).catch(function (error) {
    // Log any failures
    console.error('Test failed', error.message)
    app.stop()
 
    // done(); // ++ Test result `not done()` is Failure, of timeout when catched error.
  })  
}).timeout(4000); // ++ Optional timeout msec (default 2000)

```

実行結果:

```

MN@daisy-bell:lina_dicto/$ ARG=wakeup make test
cd lina_dicto && npm run test test/wakeup

> lina_dicto@0.2.11 test /home/nuka/lina_dicto/lina_dicto
> mocha --require intelli-espower-loader "test/wakeup"



Test failed   # test/wakeup.js:26
 
  assert.equal(title, "lina_dicto - Esperanto / Japanese -xx")
               |                                              
               "lina_dicto - Esperanto / Japanese -"          
 
  1) window

  0 passing (2s)
  1 failing

  1) window:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/nuka/lina_dicto/lina_dicto/test/wakeup.js)
      at listOnTimeout (internal/timers.js:554:17)
      at processTimers (internal/timers.js:497:7)



npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! lina_dicto@0.2.11 test: `mocha --require intelli-espower-loader "test/wakeup"`
~
MN@daisy-bell:lina_dicto/$ echo $?
2

```




 

electron12 でdialogを呼び出す(ContextBridge)

electron12対応へ、前回は 右クリックメニュー(ContextMenu)カスタム処理をmain-processへ移動した(electron 12対応)   まで。  nodeIntegrationはともかくmoduleは将来廃止になるとのこと。 npmパッケージで逃げるこ...