├── .eslintrc.json
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── docs
├── ipc-main.md
└── ipc-renderer.md
├── examples
├── basic
│ ├── index.html
│ ├── main.js
│ └── package.json
└── multi-version
│ ├── bar
│ ├── index.js
│ └── package.json
│ ├── foo
│ ├── index.js
│ └── package.json
│ ├── index.html
│ ├── main.js
│ └── package.json
├── index.js
├── lib
├── common.js
├── ipc-main.js
└── ipc-renderer.js
├── package.json
└── test
├── fixtures
├── app-main2all-exclude-self
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2all
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2main-reply
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2main
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply-a-user-error
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply-cancel-request
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply-error-invalid-first-arg
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply-error-win-destroyed
│ ├── index.html
│ ├── index2.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply-more-than-once
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply-nested
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply-timeout
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win-reply
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2win
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-main2wins
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2all-exclude-self
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ └── send.html
├── app-win2all
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ └── send.html
├── app-win2main-reply-a-user-error
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply-cancel-request
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply-error-no-callback
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply-error-win-destroyed
│ ├── index.html
│ ├── index2.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply-invalid-first-arg
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply-more-than-once
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply-nested
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply-timeout
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main-reply
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2main
│ ├── index.html
│ ├── main.js
│ └── package.json
├── app-win2wins-exclude-self
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ └── send.html
└── app-win2wins
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ └── send.html
├── send-to-all.spec.js
├── send-to-main-reply.spec.js
├── send-to-main.spec.js
├── send-to-win-reply.spec.js
├── send-to-win.spec.js
└── send-to-wins.spec.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint:recommended",
3 | "rules": {
4 | "comma-dangle": 0,
5 | "no-console": 0,
6 | "no-constant-condition": 0,
7 | "semi": 1
8 | },
9 | "parserOptions": {
10 | "ecmaVersion": 6,
11 | "ecmaFeatures": {
12 | "jsx": true
13 | }
14 | },
15 | "env": {
16 | "browser": true,
17 | "node": true,
18 | "es6": true,
19 | "mocha": true
20 | },
21 | "plugins": [
22 | ],
23 | "globals": {
24 | "deprecate": false,
25 | "helper": false,
26 | "tap": false,
27 | "unused": false
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # mac-osx files
2 | .DS_store
3 | profile
4 |
5 | # visual-studio files
6 | *.ncb *.sln
7 | *.suo
8 | *.vcproj.*.user
9 | *.pdb
10 | *.idb
11 | *.csproj
12 | *.csproj.user
13 |
14 | # visual-studio code
15 | .vscode/
16 |
17 | # exvim files
18 | *.err
19 | *.exvim
20 | .exvim.*/
21 |
22 | # webstorm
23 | .idea
24 |
25 | # log files
26 | *.log
27 |
28 | # project files
29 | node_modules/
30 | bower_components/
31 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | - osx
4 |
5 | language: node_js
6 |
7 | node_js:
8 | - "6"
9 |
10 | branches:
11 | only:
12 | - master
13 |
14 | cache:
15 | - directories:
16 | - node_modules
17 |
18 | install:
19 | - export DISPLAY=':99.0'
20 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
21 | - npm install
22 |
23 | script: npm test
24 |
25 | notifications:
26 | email: false
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## CHANGELOG
2 |
3 | ### v1.3.3
4 |
5 | - Better warning message for version conflict (again).
6 |
7 | ### v1.3.2
8 |
9 | - Better warning message for version conflict.
10 |
11 | ### v1.3.1
12 |
13 | - Add version protection warning message.
14 |
15 | ### v1.3.0
16 |
17 | - Add version protection so that different versions of the `electorn-ipc-plus` can run in the same process.
18 |
19 | ### v1.2.3
20 |
21 | - Register window `closed` event in `app.on('browser-window-created')`.
22 |
23 | ### v1.2.2
24 |
25 | - Add `ipcPlus.internal` for internal use.
26 |
27 | ### v1.2.1
28 |
29 | - Fix window close will not clear caches in main process.
30 |
31 | ### v1.2.0
32 |
33 | - Window closed will force close all sessions relate with it in main process.
34 |
35 | ### v1.1.0
36 |
37 | - Allow user reply custom Error.
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2017 Johnny Wu
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # electron-ipc-plus
2 |
3 | [](https://travis-ci.org/electron-utils/electron-ipc-plus)
4 | [](https://ci.appveyor.com/project/jwu/electron-ipc-plus)
5 | [](https://david-dm.org/electron-utils/electron-ipc-plus)
6 | [](https://david-dm.org/electron-utils/electron-ipc-plus#info=devDependencies)
7 |
8 | Improved IPC for Electron
9 |
10 | ## Features
11 |
12 | - Enhance IPC Programming Experience
13 | - Allow sending ipc message to specific window
14 | - Allow sending ipc request and waiting for the reply in callback function
15 |
16 | ## Install
17 |
18 | ```bash
19 | npm install --save electron-ipc-plus
20 | ```
21 |
22 | ## Run The Example
23 |
24 | ```bash
25 | npm start examples/${name}
26 | ```
27 |
28 | ## Usage
29 |
30 | ### Send request from main process to renderer process and wait for reply.
31 |
32 | **main process**
33 |
34 | ```javascript
35 | const ipcPlusM = require('electron-ipc-plus');
36 |
37 | ipcPlusM.sendToWin(browserWin, 'app:say-hello', 'hello renderer process!', (err, message) => {
38 | console.log(`renderer replied: ${message}`);
39 | });
40 | ```
41 |
42 | **renderer process**
43 |
44 | ```javascript
45 | const ipcPlusR = require('electron-ipc-plus');
46 |
47 | ipcPlusR.on('app:say-hello', (event, message) => {
48 | console.log(`main process said: ${message}`);
49 |
50 | setTimeout(() => {
51 | event.reply(null, 'hi main process!');
52 | }, 500);
53 | });
54 | ```
55 |
56 | ### Send request from renderer process to main process and wait for reply.
57 |
58 | **renderer process**
59 |
60 | ```javascript
61 | const ipcPlusR = require('electron-ipc-plus');
62 |
63 | ipcPlusR.sendToMain('app:say-hello', 'hello main process!', (err, message) => {
64 | console.log(`main replied: ${message}`);
65 | });
66 | ```
67 |
68 | **main process**
69 |
70 | ```javascript
71 | const ipcPlusM = require('electron-ipc-plus');
72 |
73 | ipcPlusM.on('app:say-hello', (event, message) => {
74 | console.log(`renderer process said: ${message}`);
75 |
76 | setTimeout(() => {
77 | event.reply(null, 'hi renderer process!');
78 | }, 500);
79 | });
80 | ```
81 |
82 | ## FAQ
83 |
84 | ### How can I know if an IPC is waiting for reply?
85 |
86 | Just check if `event.reply` exists:
87 |
88 | ```javascript
89 | ipcMain.on('app:say-hello', (event, message) => {
90 | if ( event.reply ) {
91 | event.reply(null, 'hi renderer process!');
92 | }
93 | });
94 | ```
95 |
96 | ### Can I reply a message for multiple times?
97 |
98 | Only the first reply will be handled, after that the session will be closed and the rest of replies will be ignored.
99 |
100 | ### What happen when the window closed and I still waiting the reply from it.
101 |
102 | An error with the code `'EWINCLOSED'` will be sent to your reply handler.
103 |
104 | ```javascript
105 | ipcPlus.sendToWin(win, 'app:say-hello', (err, message) => {
106 | if ( err && err.code === 'EWINCLOSED' ) {
107 | console.error('Window closed');
108 | }
109 | });
110 | ```
111 |
112 | ### What happen when the reply is timed out.
113 |
114 | An error with the code `'ETIMEDOUT'` will be sent to your reply handler.
115 |
116 | ```javascript
117 | ipcPlus.sendToWin(win, 'app:say-hello', (err, message) => {
118 | if ( err && err.code === 'ETIMEDOUT' ) {
119 | console.error('Target failed to reply you: timedout for 100ms');
120 | }
121 | }, 100);
122 | ```
123 |
124 | ## Known Issues
125 |
126 | ### ipcPlus.sendToWin could leave wild sessions in main process when the window reload.
127 |
128 | I try to solve this problem by the code below:
129 |
130 | ```javascript
131 | app.on('browser-window-created', (event, browserWin) => {
132 | // close all session once the window closed
133 | browserWin.webContents.once('did-navigate', () => {
134 | _closeAllSessionsInWin(browserWin);
135 | });
136 | });
137 | ```
138 |
139 | The problem is 'did-navigate' will be triggerred at the first time we open the window and I disable the above solution and leave this to user.
140 | Currently the best way to solve it is wrapping your own reload function, and manually close all sessions in that wrapped function.
141 |
142 | ### Different versions of electron-ipc-plus.
143 |
144 | When running an Electron app that has several modules depends on different version of `electron-ipc-plus`,
145 | we will receive a warning message.
146 |
147 | ## API Reference
148 |
149 | - [Module: ipcPlus (main process)](docs/ipc-main.md)
150 | - [Module: ipcPlus (renderer process)](docs/ipc-renderer.md)
151 |
152 | ## License
153 |
154 | MIT © 2017 Johnny Wu
155 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | build: off
2 |
3 | os: unstable
4 |
5 | platform:
6 | - x86
7 | - x64
8 |
9 | branches:
10 | only:
11 | - master
12 |
13 | skip_tags: true
14 |
15 | environment:
16 | nodejs_version: "6"
17 |
18 | cache:
19 | - node_modules -> package.json
20 |
21 | install:
22 | - ps: Install-Product node $env:nodejs_version
23 | - npm install npm
24 | - .\node_modules\.bin\npm install
25 |
26 | test_script:
27 | - node --version
28 | - .\node_modules\.bin\npm --version
29 | - .\node_modules\.bin\npm test
30 |
--------------------------------------------------------------------------------
/docs/ipc-main.md:
--------------------------------------------------------------------------------
1 |
2 | # Module: ipcPlus (main process)
3 |
4 | ## Methods
5 |
6 | ### ipcPlusM.option(opts)
7 |
8 | - `opts` object
9 | - `excludeSelf` boolean - exclude send ipc message to main process when calling `ipcPlusM.sendToAll`.
10 |
11 | Ipc option used in `ipcPlusM.sendToAll` and `ipcPlusM.sendToWins`.
12 |
13 | ### ipcPlusM.sendToAll(message[, ...args, option])
14 |
15 | - `message` string - Ipc message.
16 | - `...args` ... - Whatever arguments the message needs.
17 | - `option` object - You can indicate the last argument as an IPC option by `ipcPlusM.option({...})`.
18 |
19 | Send `message` with `...args` to all opened window and to main process asynchronously.
20 |
21 | **NOTE**
22 |
23 | This is a broadcast method, it will not recieve callback.
24 |
25 | ### ipcPlusM.sendToWins(message[, ...args, option])
26 |
27 | - `message` string - Ipc message.
28 | - `...args` ... - Whatever arguments the message needs.
29 | - `option` object - You can indicate the last argument as an IPC option by `ipcPlusM.option({...})`.
30 |
31 | Send `message` with `...args` to all opened windows asynchronously. The renderer process can handle it by listening to the message through the `electron.ipcRenderer` or `ipcPlus` module.
32 |
33 | **NOTE**
34 |
35 | This is a broadcast method, it will not recieve callback.
36 |
37 | Example:
38 |
39 | **Send IPC message (main process)**
40 |
41 | ```javascript
42 | const ipcPlusM = require('electron-ipc-plus');
43 |
44 | ipcPlusM.sendToWins('foobar:say-hello', 'Hello World!');
45 | ```
46 |
47 | **Receive IPC message (renderer process)**
48 |
49 | ```html
50 |
51 |
52 |
59 |
60 |
61 | ```
62 |
63 | ### ipcPlusM.sendToMain(message[, ...args, callback, timeout])
64 |
65 | - `message` string - Ipc message.
66 | - `...args` ... - Whatever arguments the message needs.
67 | - `callback` function - You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
68 | - `timeout` number - You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
69 |
70 | Returns `number` - If we have callback function, a session ID will returned.
71 |
72 | Example:
73 |
74 | **Send IPC message (main process)**
75 |
76 | ```javascript
77 | const ipcPlusM = require('electron-ipc-plus');
78 |
79 | ipcPlusM.sendToMain('foobar:say-hello', (err, msg) => {
80 | if ( err && err.code === 'ETIMEOUT' ) {
81 | console.error('Timeout for ipc message foobar:say-hello');
82 | return;
83 | }
84 |
85 | console.log(`foobar replied: ${msg}`);
86 | });
87 | ```
88 |
89 | **Receive and Reply IPC message (main process)**
90 |
91 | ```javascript
92 | const {ipcMain} = require('electron');
93 |
94 | ipcMain.on('foobar:say-hello', event => {
95 | event.reply(null, 'Hi');
96 | });
97 | ```
98 |
99 | ### ipcPlusM.sendToWin(win, message[, ...args, callback, timeout])
100 |
101 | - `win` BrowserWindow
102 | - `message` string - Ipc message.
103 | - `...args` ... - Whatever arguments the message needs.
104 | - `callback` function - You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
105 | - `timeout` number - You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
106 |
107 | Returns `number` - If we have callback function, a session ID will returned.
108 |
109 | Example:
110 |
111 | **Send IPC message (main process)**
112 |
113 | ```javascript
114 | const ipcPlusM = require('electron-ipc-plus');
115 |
116 | ipcPlusM.sendToMain('foobar:say-hello', (err, msg) => {
117 | if ( err && err.code === 'ETIMEOUT' ) {
118 | console.error('Timeout for ipc message foobar:say-hello');
119 | return;
120 | }
121 |
122 | console.log(`foobar replied: ${msg}`);
123 | });
124 | ```
125 |
126 | **Receive and Reply IPC message (renderer process)**
127 |
128 | ```javascript
129 | const {ipcRenderer} = require('electron');
130 |
131 | ipcRenderer.on('foobar:say-hello', event => {
132 | event.reply(null, 'Hi');
133 | });
134 | ```
135 |
136 | ### ipcPlusM.cancelRequest(sessionId)
137 |
138 | - `sessionId` number - The session ID.
139 |
140 | Cancel request sent to main or renderer process via `ipcPlusM.sendToMain` or `ipcPlusM.sendToWin`.
141 |
142 | ## Properties
143 |
144 | ### ipcPlusM.debug
145 |
146 | Turn on/off the debug information. No use in current version.
--------------------------------------------------------------------------------
/docs/ipc-renderer.md:
--------------------------------------------------------------------------------
1 | # Module: ipcPlus (renderer process)
2 |
3 | ## Methods
4 |
5 | ### ipcPlusR.option(opts)
6 |
7 | - `opts` object
8 | - `excludeSelf` boolean - exclude send ipc message to main process when calling `ipcPlusR.sendToAll`.
9 |
10 | Ipc option used in `ipcPlusR.sendToAll` and `ipcPlusR.sendToWins`.
11 |
12 | ### ipcPlusR.sendToAll(message[, ...args, option])
13 |
14 | - `message` string - Ipc message.
15 | - `...args` ... - Whatever arguments the message needs.
16 | - `option` object - You can indicate the last argument as an IPC option by `ipcPlusR.option({...})`.
17 |
18 | Send `message` with `...args` to all opened window and to main process asynchronously.
19 |
20 | **NOTE**
21 |
22 | This is a broadcast method, it will not recieve callback.
23 |
24 | ### ipcPlusR.sendToWins(message[, ...args, option])
25 |
26 | - `message` string - Ipc message.
27 | - `...args` ... - Whatever arguments the message needs.
28 | - `option` object - You can indicate the last argument as an IPC option by `ipcPlusR.option({...})`.
29 |
30 | Send `message` with `...args` to all opened windows asynchronously. The renderer process can handle it by listening to the message through the `electron.ipcRenderer` or `ipcPlus` module.
31 |
32 | **NOTE**
33 |
34 | This is a broadcast method, it will not recieve callback.
35 |
36 | Example:
37 |
38 | **Send IPC message (renderer process)**
39 |
40 | ```javascript
41 | const ipcPlusR = require('electron-ipc-plus');
42 |
43 | ipcPlusR.sendToWins('foobar:say-hello', 'Hello World!');
44 | ```
45 |
46 | ### ipcPlusR.sendToMainSync(message[, ...args])
47 |
48 | - `message` string - Ipc message.
49 | - `...args` ... - Whatever arguments the message needs.
50 |
51 | Returns ...: Whatever returns from main process.
52 |
53 | Send `message` with `...args` to main process synchronously. (This is same as `electron.ipcRenderer.sendSync`).
54 |
55 | ### ipcPlusR.sendToMain(message[, ...args, callback, timeout])
56 |
57 | - `message` string - Ipc message.
58 | - `...args` ... - Whatever arguments the message needs.
59 | - `callback` function - You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
60 | - `timeout` number - You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
61 |
62 | Returns `number` - If we have callback function, a session ID will returned.
63 |
64 | Example:
65 |
66 | **Send IPC message (renderer process)**
67 |
68 | ```javascript
69 | const ipcPlusR = require('electron-ipc-plus');
70 |
71 | ipcPlusR.sendToMain('foobar:say-hello', (err, msg) => {
72 | if ( err && err.code === 'ETIMEOUT' ) {
73 | console.error('Timeout for ipc message foobar:say-hello');
74 | return;
75 | }
76 |
77 | console.log(`foobar replied: ${msg}`);
78 | });
79 | ```
80 |
81 | **Receive and Reply IPC message (main process)**
82 |
83 | ```javascript
84 | const {ipcRenderer} = require('electron');
85 |
86 | ipcRenderer.on('foobar:say-hello', event => {
87 | event.reply(null, 'Hi');
88 | });
89 | ```
90 |
91 | ### ipcPlusR.cancelRequest(sessionId)
92 |
93 | - `sessionId` number - The session ID.
94 |
95 | Cancel request sent to main process via `ipcPlusR.sendToMain`.
96 |
97 | ## Properties
98 |
99 | ### ipcPlusR.debug
100 |
101 | Turn on/off the debug information. No use in current version.
--------------------------------------------------------------------------------
/examples/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | main2all
6 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/examples/basic/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow, ipcMain} = require('electron');
2 | const ipcPlus = require('../../index.js');
3 |
4 | let win0, win1;
5 |
6 | app.on('ready', function () {
7 | // win0
8 | win0 = new BrowserWindow({
9 | center: true,
10 | width: 400,
11 | height: 300,
12 | x: 100,
13 | y: 100,
14 | backgroundColor: '#09f',
15 | });
16 | win0.loadURL('file://' + __dirname + '/index.html');
17 |
18 | // win1
19 | win1 = new BrowserWindow({
20 | center: true,
21 | width: 400,
22 | height: 300,
23 | x: 100 + 400 + 50,
24 | y: 100,
25 | backgroundColor: '#888',
26 | });
27 | win1.loadURL('file://' + __dirname + '/index.html');
28 | });
29 |
30 | ipcPlus.on('app:main2all', (event, ...args) => {
31 | console.log(`received "app:main2all" at main process: ${args}`);
32 | });
33 |
34 | ipcPlus.on('app:win2all', (event, ...args) => {
35 | console.log(`received "app:win2all" at main process: ${args}`);
36 | });
37 |
38 | ipcPlus.on('app:win2main-reply', (event, ...args) => {
39 | console.log(`received "app:win2main-reply" at main process: ${args}, reply: bar after 500ms`);
40 | setTimeout(() => {
41 | event.reply(null, 'bar');
42 | }, 500);
43 | });
44 |
45 | ipcPlus.on('app:win2main-reply-error', (event, ...args) => {
46 | console.log(`received "app:win2main-reply-error" at main process: ${args}, reply: bar after 500ms`);
47 | setTimeout(() => {
48 | event.reply(new Error('Hello'));
49 | }, 500);
50 | });
51 |
52 | // ====================
53 | // handle buttons
54 | // ====================
55 |
56 | ipcMain.on('btn:main2all', () => {
57 | console.log('send app:main2all foo, bar');
58 | ipcPlus.sendToAll('app:main2all', 'foo', 'bar');
59 | });
60 |
61 | ipcMain.on('btn:main2all-exclude-self', () => {
62 | console.log('send app:main2all (exclude-self): foo, bar');
63 | ipcPlus.sendToAll('app:main2all', 'foo', 'bar', ipcPlus.option({
64 | excludeSelf: true
65 | }));
66 | });
67 |
68 | ipcMain.on('btn:main2wins', () => {
69 | console.log('send app:main2wins foo, bar');
70 | ipcPlus.sendToWins('app:main2wins', 'foo', 'bar');
71 | });
72 |
73 | ipcMain.on('btn:main2win', (event) => {
74 | console.log('send app:main2win foo, bar');
75 | let win = BrowserWindow.fromWebContents( event.sender );
76 | ipcPlus.sendToWin(win, 'app:main2win', 'foo', 'bar');
77 | });
78 |
79 | ipcMain.on('btn:main2win-reply', (event) => {
80 | let win = BrowserWindow.fromWebContents( event.sender );
81 | console.log('send app:main2win-reply foo');
82 | ipcPlus.sendToWin(win, 'app:main2win-reply', 'foo', (err, ...args) => {
83 | console.log(`received "app:main2win-reply" at main process: ${args}`);
84 | });
85 | });
86 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/examples/multi-version/bar/index.js:
--------------------------------------------------------------------------------
1 | const ipcPlus = require('electron-ipc-plus');
2 |
3 | module.exports = {
4 | ipcPlus
5 | };
6 |
--------------------------------------------------------------------------------
/examples/multi-version/bar/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bar",
3 | "version": "1.0.0",
4 | "main": "main.js",
5 | "dependencies": {
6 | "electron-ipc-plus": "1.3.3"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/multi-version/foo/index.js:
--------------------------------------------------------------------------------
1 | const ipcPlus = require('electron-ipc-plus');
2 |
3 | module.exports = {
4 | ipcPlus
5 | };
6 |
--------------------------------------------------------------------------------
/examples/multi-version/foo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "foo",
3 | "version": "1.0.0",
4 | "main": "main.js",
5 | "dependencies": {
6 | "electron-ipc-plus": "1.3.2"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/multi-version/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello
6 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/examples/multi-version/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const foo = require('foo');
3 | const bar = require('bar');
4 |
5 | let win;
6 |
7 | app.on('ready', function () {
8 | // win
9 | win = new BrowserWindow({
10 | center: true,
11 | width: 400,
12 | height: 300,
13 | x: 100,
14 | y: 100,
15 | backgroundColor: '#09f',
16 | });
17 | win.loadURL('file://' + __dirname + '/index.html');
18 | });
19 |
20 | foo.ipcPlus.on('app:hello', event => {
21 | console.log('Received from renderer');
22 | event.reply(null, 'Hello World');
23 | });
24 |
--------------------------------------------------------------------------------
/examples/multi-version/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js",
5 | "dependencies": {
6 | "foo": "file:./foo",
7 | "bar": "file:./bar"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const platform = require('electron-platform');
4 |
5 | let ipcPlus;
6 |
7 | if ( platform.isMainProcess ) {
8 | ipcPlus = require('./lib/ipc-main');
9 | } else {
10 | ipcPlus = require('./lib/ipc-renderer');
11 | }
12 |
13 | // ==========================
14 | // exports
15 | // ==========================
16 |
17 | module.exports = ipcPlus;
18 |
--------------------------------------------------------------------------------
/lib/common.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let common = {};
4 |
5 | common._wrapError = function (args) {
6 | if ( args.length === 0 ) {
7 | return true;
8 | }
9 |
10 | let first = args[0];
11 | if ( first === null ) {
12 | return true;
13 | }
14 |
15 | if ( first instanceof Error ) {
16 | first = {
17 | __error__: true,
18 | stack: first.stack,
19 | message: first.message,
20 | // if this is a system error
21 | code: first.code,
22 | errno: first.errno,
23 | syscall: first.syscall,
24 | };
25 |
26 | args[0] = first;
27 | return true;
28 | }
29 |
30 | // reply invalid-args error
31 | let err = new Error();
32 | args.unshift({
33 | __error__: true,
34 | stack: err.stack,
35 | message: 'Invalid argument for event.reply(), first argument must be null or Error',
36 | code: 'EINVALIDARGS',
37 | });
38 |
39 | return false;
40 | };
41 |
42 | common._unwrapError = function (args) {
43 | let err = args[0];
44 |
45 | if ( err && err.__error__ ) {
46 | return err;
47 | }
48 |
49 | return null;
50 | };
51 |
52 | common._popOptions = function (args) {
53 | let opts = args[args.length - 1];
54 |
55 | if ( opts && typeof opts === 'object' && opts.__ipc__ ) {
56 | args.pop(); // args.splice(-1,1);
57 | return opts;
58 | }
59 |
60 | return null;
61 | };
62 |
63 | common._popReplyAndTimeout = function (args) {
64 | // arguments check
65 | let reply, timeout;
66 | let lastArg = args[args.length - 1];
67 |
68 | if (typeof lastArg === 'number') {
69 | if ( args.length < 2 ) {
70 | return null;
71 | }
72 |
73 | timeout = lastArg;
74 | lastArg = args[args.length - 2];
75 | if (typeof lastArg !== 'function') {
76 | return null;
77 | }
78 |
79 | reply = lastArg;
80 | args.splice(-2,2);
81 | } else {
82 | if (typeof lastArg !== 'function') {
83 | return null;
84 | }
85 |
86 | reply = lastArg;
87 | timeout = 5000;
88 | args.pop();
89 | }
90 |
91 | return {
92 | reply: reply,
93 | timeout: timeout,
94 | };
95 | };
96 |
97 | /**
98 | * @method option
99 | * @param {object} - opts
100 | * @param {boolean} - opts.excludeSelf
101 | * @param {boolean} - opts.waitForReply
102 | * @param {number} - opts.timeout
103 | *
104 | * Ipc option used as last arguments in message.
105 | */
106 | common.option = function (opts) {
107 | opts.__ipc__ = true;
108 | return opts;
109 | };
110 |
111 | /**
112 | * @class ErrorTimeout
113 | */
114 | class ErrorTimeout extends Error {
115 | /**
116 | * @param {string} message
117 | * @param {string} sessionId
118 | * @param {number} timeout
119 | */
120 | constructor ( message, sessionId, timeout ) {
121 | super(`ipc timeout. message: ${message}, session: ${sessionId}`);
122 |
123 | this.code = 'ETIMEDOUT';
124 | this.ipc = message;
125 | this.sessionId = sessionId;
126 | this.timeout = timeout;
127 | }
128 | }
129 |
130 | /**
131 | * @class ErrorWinClosed
132 | */
133 | class ErrorWinClosed extends Error {
134 | /**
135 | * @param {string} message
136 | * @param {string} winID
137 | */
138 | constructor ( message, winID ) {
139 | super(`ipc failed to reply, window ${winID} closed, message: ${message}`);
140 |
141 | this.code = 'EWINCLOSED';
142 | this.ipc = message;
143 | this.winID = winID;
144 | }
145 | }
146 |
147 | common.ErrorTimeout = ErrorTimeout;
148 | common.ErrorWinClosed = ErrorWinClosed;
149 |
150 | module.exports = common;
151 |
--------------------------------------------------------------------------------
/lib/ipc-main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if ( global.__electron_ipc_plus__ ) {
4 | console.warn(`A different version of electron-ipc-plus already running in the process: ${global.__electron_ipc_plus__.id}, make sure your dependencies use the same version of electron-ipc-plus.`);
5 | }
6 |
7 | /**
8 | * @module ipcPlus
9 | */
10 | let ipcPlus = {};
11 | module.exports = ipcPlus;
12 |
13 | global.__electron_ipc_plus__ = ipcPlus;
14 |
15 | // requires
16 | const {app, ipcMain, BrowserWindow} = require('electron');
17 | const common = require('./common');
18 |
19 | const pkgJson = require('../package.json');
20 | const moduleID = `${pkgJson.name}@${pkgJson.version}`;
21 |
22 | let _nextSessionId = 1000;
23 | let _id2sessionInfo = {};
24 | let _debug = false;
25 |
26 | let _ErrorTimeout = common.ErrorTimeout;
27 | let _ErrorWinClosed = common.ErrorWinClosed;
28 | let _wrapError = common._wrapError;
29 | let _unwrapError = common._unwrapError;
30 | let _popOptions = common._popOptions;
31 | let _popReplyAndTimeout = common._popReplyAndTimeout;
32 | let _ipcOption = common.option;
33 |
34 | // ========================================
35 | // exports
36 | // ========================================
37 |
38 | /**
39 | * @property id
40 | */
41 | ipcPlus.id = moduleID;
42 |
43 | // internal uses
44 | ipcPlus.internal = {
45 | _popReplyAndTimeout,
46 | _popOptions,
47 | _wrapError,
48 | _unwrapError,
49 | _newSession,
50 | _closeSession,
51 | };
52 |
53 | /**
54 | * @method option
55 | * @param {object} opts
56 | * @param {boolean} opts.excludeSelf
57 | * @param {number} [opts.sessionId] - used internally
58 | * @param {boolean} [opts.waitForReply] - used internally
59 | * @param {number} [opts.timeout] - used internally
60 | *
61 | * Ipc option used as last arguments in message.
62 | */
63 | ipcPlus.option = _ipcOption;
64 |
65 | /**
66 | * @method on
67 | *
68 | * Same as ipcMain.on
69 | */
70 | ipcPlus.on = ipcMain.on.bind(ipcMain);
71 |
72 | /**
73 | * @method once
74 | *
75 | * Same as ipcMain.once
76 | */
77 | ipcPlus.once = ipcMain.once.bind(ipcMain);
78 |
79 | /**
80 | * @method removeListener
81 | *
82 | * Same as `ipcMain.removeListener`.
83 | */
84 | ipcPlus.removeListener = ipcMain.removeListener.bind(ipcMain);
85 |
86 | /**
87 | * @method sendToAll
88 | * @param {string} message
89 | * @param {...*} [args] - whatever arguments the message needs
90 | * @param {object} [options] - you can indicate the options by ipcPlus.option({ excludeSelf: true })
91 | *
92 | * Send `message` with `...args` to all opened window and to main process asynchronously.
93 | */
94 | ipcPlus.sendToAll = function (message, ...args) {
95 | if (args.length) {
96 | let excludeSelf = false;
97 | let opts = _popOptions(args);
98 |
99 | // check options
100 | if (opts && opts.excludeSelf) {
101 | excludeSelf = true;
102 | }
103 |
104 | args = [message, ...args];
105 |
106 | // send
107 | if (!excludeSelf) {
108 | _main2main.apply(null, args);
109 | }
110 | _send2wins.apply(null, args);
111 |
112 | return;
113 | }
114 |
115 | _main2main(message);
116 | _send2wins(message);
117 | };
118 |
119 | /**
120 | * @method sendToWins
121 | * @param {string} message
122 | * @param {...*} [args] - whatever arguments the message needs
123 | *
124 | * Send `message` with `...args` to all opened windows asynchronously. The renderer process
125 | * can handle it by listening to the message through the `electron.ipcRenderer` or `ipcPlus` module.
126 | *
127 | * @example
128 | * **Send IPC message (main process)**
129 | *
130 | * ```js
131 | * const ipcPlus = require('electron-ipc-plus');
132 | *
133 | * ipcPlus.sendToWins('foo:bar', 'Hello World!');
134 | * ```
135 | *
136 | * **Receive IPC message (renderer process)**
137 | *
138 | * ```html
139 | *
140 | *
141 | *
148 | *
149 | *
150 | * ```
151 | */
152 | ipcPlus.sendToWins = _send2wins;
153 |
154 | /**
155 | * @method sendToMain
156 | * @param {string} message
157 | * @param {...*} [args] - whatever arguments the message needs
158 | * @param {function} [callback] - You can specify a callback function to receive IPC reply at the last or the 2nd last argument.
159 | * @param {number} [timeout] - You can specify a timeout for the callback at the last argument. If no timeout specified, it will be 5000ms.
160 | * @return {number} sessionID
161 | *
162 | * Send `message` with `...args` to main process asynchronously. It is possible to add a callback as the last or the 2nd last argument
163 | * to receive replies from the IPC receiver.
164 | *
165 | * Example:
166 | *
167 | * **Send IPC message (main process)**
168 | *
169 | * ```js
170 | * const ipcPlus = require('electron-ipc-plus');
171 | *
172 | * ipcPlus.sendToMain('foobar:say-hello', (err, msg) => {
173 | * if ( err.code === 'ETIMEDOUT' ) {
174 | * console.error('Timeout for ipc message foobar:say-hello');
175 | * return;
176 | * }
177 | *
178 | * console.log(`foobar replied: ${msg}`);
179 | * });
180 | * ```
181 | *
182 | * **Receive and Reply IPC message (main process)**
183 | *
184 | * ```js
185 | * const {ipcMain} = require('electron');
186 | *
187 | * ipcMain.on('foobar:say-hello', event => {
188 | * event.reply(null, 'Hi');
189 | * });
190 | * ```
191 | */
192 | ipcPlus.sendToMain = function (message, ...args) {
193 | if ( typeof message !== 'string' ) {
194 | console.error('Call to `sendToMain` failed. The message must be a string.');
195 | return;
196 | }
197 |
198 | let opts = _popReplyAndTimeout(args);
199 | if ( !opts ) {
200 | args = [message, ...args];
201 | if ( _main2main.apply ( null, args ) === false ) {
202 | console.error( `sendToMain "${message}" failed, no response received.` );
203 | }
204 | return;
205 | }
206 |
207 | let sessionId = _newSession(message, 'main', opts.reply, opts.timeout);
208 |
209 | args = [message, ...args, _ipcOption({
210 | sessionId: sessionId,
211 | waitForReply: true,
212 | timeout: opts.timeout, // this is only used as debug info
213 | })];
214 |
215 | if ( _main2mainOpts.apply ( null, args ) === false ) {
216 | console.error( `sendToMain "${message}" failed, no response received.` );
217 | }
218 |
219 | return sessionId;
220 | };
221 |
222 | /**
223 | * @method sendToWin
224 | * @param {BrowserWindow} win
225 | * @param {string} message
226 | * @param {...*} [args] - whatever arguments the message needs
227 | * @param {object} [options] - you can indicate the options by ipcPlus.option({ excludeSelf: true })
228 | *
229 | * Send `message` with `...args` to specific window.
230 | */
231 | ipcPlus.sendToWin = function (win, message, ...args) {
232 | // NOTE: it is possible the webContents is destroyed
233 | if ( win.webContents.isDestroyed() ) {
234 | return;
235 | }
236 |
237 | // TODO: what is win.webContents.isLoading()?
238 | // should we setInterval to recheck/retry for several times?
239 |
240 | if ( typeof message !== 'string' ) {
241 | console.error(`Send message failed for "${message}". The message must be a string`);
242 | return;
243 | }
244 |
245 | let opts = _popReplyAndTimeout(args);
246 | if ( !opts ) {
247 | args = [win, message, ...args];
248 | if ( _send2win.apply(null, args) === false ) {
249 | console.error( `send message "${message}" to window failed. No response was received.` );
250 | }
251 | return;
252 | }
253 |
254 | let sessionId = _newSession(message, `${win.id}@main`, opts.reply, opts.timeout, win);
255 |
256 | //
257 | args = [win, `${moduleID}:main2renderer`, message, ...args, _ipcOption({
258 | sessionId: sessionId,
259 | waitForReply: true,
260 | timeout: opts.timeout, // this is only used as debug info
261 | })];
262 | _send2win.apply(null, args);
263 |
264 | return sessionId;
265 | };
266 |
267 | /**
268 | * @method cancelRequest
269 | * @param {number} sessionId
270 | *
271 | * Cancel request sent to main or renderer process.
272 | */
273 | ipcPlus.cancelRequest = function (sessionId) {
274 | _closeSession(sessionId);
275 | };
276 |
277 | /**
278 | * @method _closeAllSessions
279 | *
280 | * Force close all sessions.
281 | * Usually you don't need to call this function. This is for page reload or app relaunch purpose.
282 | */
283 | ipcPlus._closeAllSessions = function () {
284 | let ids = Object.keys(_id2sessionInfo);
285 |
286 | for ( let i = 0; i < ids.length; ++i ) {
287 | let sessionId = ids[i];
288 | _closeSession (sessionId);
289 | }
290 | };
291 |
292 | /**
293 | * @property {boolean} debug
294 | *
295 | * Turn on/off the debug code in the module.
296 | */
297 | Object.defineProperty(ipcPlus, 'debug', {
298 | enumerable: true,
299 | get () { return _debug; },
300 | set ( value ) { _debug = value; },
301 | });
302 |
303 | // ========================================
304 | // Internal
305 | // ========================================
306 |
307 | /**
308 | * @param {string} message
309 | * @param {string} prefix - can be 'main', '${winID}@renderer' and so on...
310 | * @param {function} fn - callback function
311 | * @param {number} timeout
312 | * @param {BrowserWindow} [win]
313 | */
314 | function _newSession ( message, prefix, fn, timeout, win ) {
315 | let sessionId = `${prefix}:${_nextSessionId++}`;
316 | let timeoutId;
317 |
318 | if ( timeout !== -1 ) {
319 | timeoutId = setTimeout(() => {
320 | let info = _id2sessionInfo[sessionId];
321 |
322 | if ( info ) {
323 | delete _id2sessionInfo[sessionId];
324 |
325 | info.callback(new _ErrorTimeout( message, sessionId, timeout ));
326 | }
327 |
328 | // DISABLE
329 | // if ( _debug ) {
330 | // console.warn(`ipc timeout. message: ${message}, session: ${sessionId}`);
331 | // }
332 | }, timeout);
333 | }
334 |
335 | _id2sessionInfo[sessionId] = {
336 | sessionId: sessionId,
337 | timeoutId: timeoutId,
338 | callback: fn,
339 | win: win,
340 | message: message,
341 | };
342 |
343 | return sessionId;
344 | }
345 |
346 | function _closeSession ( sessionId ) {
347 | let info = _id2sessionInfo[sessionId];
348 |
349 | if ( info ) {
350 | delete _id2sessionInfo[sessionId];
351 |
352 | if ( info.timeoutId ) {
353 | clearTimeout(info.timeoutId);
354 | }
355 | }
356 |
357 | return info;
358 | }
359 |
360 | function _closeAllSessionsInWin (win) {
361 | let ids = Object.keys(_id2sessionInfo);
362 |
363 | for ( let i = 0; i < ids.length; ++i ) {
364 | let sessionId = ids[i];
365 | let info = _id2sessionInfo[sessionId];
366 |
367 | if ( info && info.win && info.win === win ) {
368 | delete _id2sessionInfo[sessionId];
369 |
370 | if ( info.timeoutId ) {
371 | clearTimeout(info.timeoutId);
372 | }
373 |
374 | info.callback(new _ErrorWinClosed(info.message, win));
375 | }
376 | }
377 | }
378 |
379 | function _send2win (win, ...args) {
380 | // NOTE: it is possible the webContents is destroyed
381 | if ( win.webContents.isDestroyed() ) {
382 | return false;
383 | }
384 |
385 | // TODO: what if win.webContents.isLoading()?
386 | // should we setInterval to recheck/retry for several times?
387 |
388 | win.webContents.send.apply(win.webContents, args);
389 |
390 | return true;
391 | }
392 |
393 | function _send2wins (message, ...args) {
394 | args = [message, ...args];
395 |
396 | let winlist = BrowserWindow.getAllWindows();
397 |
398 | for ( let i = 0; i < winlist.length; ++i ) {
399 | let win = winlist[i];
400 |
401 | // NOTE: it is possible the webContents is destroyed
402 | if ( win.webContents.isDestroyed() ) {
403 | continue;
404 | }
405 |
406 | // TODO: what if win.webContents.isLoading()?
407 | // should we setInterval to recheck/retry for several times?
408 |
409 | win.webContents.send.apply(win.webContents, args);
410 | }
411 | }
412 |
413 | /**
414 | * Send `args...` to windows except the excluded
415 | * @method _main2renderersExclude
416 | * @param {object} excluded - A [WebContents](https://github.com/atom/electron/blob/master/docs/api/browser-window.md#class-webcontents) object.
417 | * @param {...*} [args] - whatever arguments the message needs
418 | */
419 | function _main2renderersExclude (excluded, ...args) {
420 | let winlist = BrowserWindow.getAllWindows();
421 |
422 | for ( let i = 0; i < winlist.length; ++i ) {
423 | let win = winlist[i];
424 |
425 | // NOTE: it is possible the webContents is destroyed
426 | if ( win.webContents.isDestroyed() ) {
427 | continue;
428 | }
429 |
430 | // if window excluded
431 | if ( win.webContents === excluded ) {
432 | continue;
433 | }
434 |
435 | // TODO: what if win.webContents.isLoading()?
436 | // should we setInterval to recheck/retry for several times?
437 |
438 | win.webContents.send.apply(win.webContents, args);
439 | }
440 | }
441 |
442 | function _main2renderers (message, ...args) {
443 | if ( args.length === 0 ) {
444 | _send2wins( message );
445 | return;
446 | }
447 |
448 | // send
449 | _send2wins.apply( null, [message, ...args] );
450 | }
451 |
452 | function _main2mainOpts (message, ...args) {
453 | let event = {
454 | senderType: 'main',
455 | sender: {
456 | send: ipcPlus.sendToMain
457 | }
458 | };
459 |
460 | if ( args.length === 0 ) {
461 | return ipcMain.emit( message, event );
462 | }
463 |
464 | // process waitForReply option
465 | let opts = _popOptions(args);
466 | if ( opts && opts.waitForReply ) {
467 | // NOTE: do not directly use message in event.reply, it will cause Electron devtools crash
468 | let msg = message;
469 | event.reply = function (...replyArgs) {
470 | if ( _wrapError(replyArgs) === false ) {
471 | console.warn(`Invalid argument for event.reply of "${msg}": the first argument must be an instance of "Error" or "null"`);
472 | }
473 |
474 | let replyOpts = _ipcOption({
475 | sessionId: opts.sessionId
476 | });
477 | replyArgs = [`${moduleID}:reply`, event, ...replyArgs, replyOpts];
478 | return ipcMain.emit.apply( ipcMain, replyArgs );
479 | };
480 | }
481 |
482 | // insert event as 2nd parameter in args
483 | args = [message, event, ...args];
484 | return ipcMain.emit.apply( ipcMain, args );
485 | }
486 |
487 | function _main2main (message, ...args) {
488 | let event = {
489 | senderType: 'main',
490 | sender: {
491 | send: ipcPlus.sendToMain
492 | }
493 | };
494 |
495 | if ( args.length === 0 ) {
496 | return ipcMain.emit( message, event );
497 | }
498 |
499 | // insert event as 2nd parameter in args
500 | args = [message, event, ...args];
501 | return ipcMain.emit.apply( ipcMain, args );
502 | }
503 |
504 | function _renderer2mainOpts (event, message, ...args) {
505 | if ( args.length === 0 ) {
506 | return ipcMain.emit( message, event );
507 | }
508 |
509 | // process waitForReply option
510 | let opts = _popOptions(args);
511 | if ( opts && opts.waitForReply ) {
512 | // NOTE: do not directly use `event` and `message` in event.reply, it will cause Electron devtools crash
513 | let sender = event.sender;
514 | let msg = message;
515 |
516 | event.reply = function (...replyArgs) {
517 | // if the sender is invalid (destroyed)
518 | if ( sender.isDestroyed() ) {
519 | return;
520 | }
521 |
522 | if ( _wrapError(replyArgs) === false ) {
523 | console.warn(`Invalid argument for event.reply of "${msg}": the first argument must be an instance of "Error" or "null"`);
524 | }
525 |
526 | let replyOpts = _ipcOption({
527 | sessionId: opts.sessionId
528 | });
529 | replyArgs = [`${moduleID}:reply`, ...replyArgs, replyOpts];
530 | return sender.send.apply( sender, replyArgs );
531 | };
532 | }
533 |
534 | // refine the args
535 | args = [message, event, ...args];
536 | return ipcMain.emit.apply( ipcMain, args );
537 | }
538 |
539 | function _renderer2main (event, message, ...args) {
540 | if ( args.length === 0 ) {
541 | return ipcMain.emit( message, event );
542 | }
543 |
544 | // refine the args
545 | args = [message, event, ...args];
546 | return ipcMain.emit.apply( ipcMain, args );
547 | }
548 |
549 | function _renderer2renderersOpts (event, message, ...args) {
550 | // check options
551 | let opts = _popOptions(args);
552 | if (opts && opts.excludeSelf) {
553 | _main2renderersExclude.apply( null, [event.sender, message, ...args] );
554 | return;
555 | }
556 |
557 | _main2renderers.apply(null, [message, ...args]);
558 | }
559 |
560 | // ========================================
561 | // app event
562 | // ========================================
563 |
564 | app.on('browser-window-created', (event, browserWin) => {
565 | // close all session once the window closed
566 | browserWin.once('closed', () => {
567 | _closeAllSessionsInWin(browserWin);
568 | });
569 | });
570 |
571 | // ========================================
572 | // ipcPlus
573 | // ========================================
574 |
575 | ipcMain.on(`${moduleID}:renderer2all`, (event, message, ...args) => {
576 | let opts = _popOptions(args);
577 |
578 | _renderer2main.apply(null, [event, message, ...args]);
579 |
580 | if (opts && opts.excludeSelf) {
581 | _main2renderersExclude.apply( null, [event.sender, message, ...args] );
582 | } else {
583 | _main2renderers.apply(null, [message, ...args]);
584 | }
585 | });
586 |
587 | ipcMain.on(`${moduleID}:renderer2wins`, _renderer2renderersOpts );
588 |
589 | ipcMain.on(`${moduleID}:renderer2main`, (event, message, ...args) => {
590 | if ( _renderer2mainOpts.apply ( null, [event, message, ...args] ) === false ) {
591 | console.error( `Message "${message}" from renderer to main failed, no response receieved.` );
592 | }
593 | });
594 |
595 | ipcMain.on(`${moduleID}:reply`, (event, ...args) => {
596 | let opts = _popOptions(args);
597 |
598 | // create a new Error in current process
599 | // NOTE: don't do this in _unwrapError, it will make the new Error have wrong stack.
600 | let err = _unwrapError(args);
601 | if ( err ) {
602 | let lines = err.stack.split('\n');
603 | lines.shift(); // remove the message cause we add it in new Error;
604 |
605 | let newErr = new Error(err.message);
606 | newErr.stack += '\n\t--------------------\n' + lines.join('\n');
607 | newErr.code = err.code;
608 | newErr.code = err.code;
609 | newErr.errno = err.errno;
610 | newErr.syscall = err.syscall;
611 |
612 | args[0] = newErr;
613 | }
614 |
615 | // NOTE: we must close session before it apply, this will prevent window.close() invoked in
616 | // reply callback will make _closeSession called second times.
617 | let info = _closeSession(opts.sessionId);
618 | if (info) {
619 | info.callback.apply(null, args);
620 | }
621 | });
622 |
--------------------------------------------------------------------------------
/lib/ipc-renderer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if ( window.__electron_ipc_plus__ ) {
4 | console.warn(`A different version of electron-ipc-plus already running in the process: ${window.__electron_ipc_plus__.id}, make sure your dependencies use the same version of electron-ipc-plus.`);
5 | }
6 |
7 | /**
8 | * @module ipcPlus
9 | */
10 | let ipcPlus = {};
11 | module.exports = ipcPlus;
12 |
13 | window.__electron_ipc_plus__ = ipcPlus;
14 |
15 | const pkgJson = require('../package.json');
16 | const moduleID = `${pkgJson.name}@${pkgJson.version}`;
17 |
18 | // requires
19 | const {remote, ipcRenderer} = require('electron');
20 | const common = require('./common');
21 |
22 | // get window id
23 | const winID = remote.getCurrentWindow().id;
24 |
25 | let _nextSessionId = 1000;
26 | let _id2sessionInfo = {};
27 | let _debug = false;
28 |
29 | let _ErrorTimeout = common.ErrorTimeout;
30 | let _wrapError = common._wrapError;
31 | let _unwrapError = common._unwrapError;
32 | let _popOptions = common._popOptions;
33 | let _popReplyAndTimeout = common._popReplyAndTimeout;
34 | let _ipcOption = common.option;
35 |
36 | // ==========================
37 | // exports
38 | // ==========================
39 |
40 | /**
41 | * @property id
42 | */
43 | ipcPlus.id = moduleID;
44 |
45 | // internal uses
46 | ipcPlus.internal = {
47 | _popReplyAndTimeout,
48 | _popOptions,
49 | _wrapError,
50 | _unwrapError,
51 | _newSession,
52 | _closeSession,
53 | };
54 |
55 | /**
56 | * @method option
57 | * @param {object} opts
58 | * @param {boolean} opts.excludeSelf - exclude send ipc message to main process when calling `ipcPlusM.sendToAll`.
59 | * @param {number} [opts.sessionId] - used internally.
60 | * @param {boolean} [opts.waitForReply] - used internally.
61 | * @param {number} [opts.timeout] - used internally.
62 | *
63 | * Ipc option used in `ipcPlusM.sendToAll` and `ipcPlusM.sendToWins`.
64 | */
65 | ipcPlus.option = _ipcOption;
66 |
67 | /**
68 | * @method on
69 | *
70 | * Same as `ipcRenderer.on`.
71 | */
72 | ipcPlus.on = ipcRenderer.on.bind(ipcRenderer);
73 |
74 | /**
75 | * @method once
76 | *
77 | * Same as `ipcRenderer.once`.
78 | */
79 | ipcPlus.once = ipcRenderer.once.bind(ipcRenderer);
80 |
81 | /**
82 | * @method removeListener
83 | *
84 | * Same as `ipcRenderer.removeListener`.
85 | */
86 | ipcPlus.removeListener = ipcRenderer.removeListener.bind(ipcRenderer);
87 |
88 | /**
89 | * @method sendToAll
90 | * @param {string} message - Ipc message.
91 | * @param {...} [args] - Whatever arguments the message needs.
92 | * @param {object} [options] - You can indicate the last argument as an IPC option by `ipcPlus.option({...})`.
93 | *
94 | * Send `message` with `...args` to all opened window and to main process asynchronously.
95 | *
96 | */
97 | ipcPlus.sendToAll = function (message, ...args) {
98 | if ( typeof message !== 'string' ) {
99 | console.error('Call to `sendToAll` failed. The message must be a string.');
100 | return;
101 | }
102 |
103 | ipcRenderer.send.apply( ipcRenderer, [`${moduleID}:renderer2all`, message, ...args] );
104 | };
105 |
106 | /**
107 | * @method sendToWins
108 | * @param {string} message
109 | * @param {...*} [args] - whatever arguments the message needs
110 | * @param {object} [options] - You can indicate the last argument as an IPC option by `ipcPlus.option({...})`.
111 | *
112 | * Send `message` with `...args` to all opened windows asynchronously. The renderer process
113 | * can handle it by listening to the message through the `ipcRenderer` module.
114 | */
115 | ipcPlus.sendToWins = function (message, ...args) {
116 | if ( typeof message !== 'string' ) {
117 | console.error('Call to `sendToWins` failed. The message must be a string.');
118 | return;
119 | }
120 |
121 | ipcRenderer.send.apply( ipcRenderer, [`${moduleID}:renderer2wins`, message, ...args] );
122 | };
123 |
124 | /**
125 | * @method sendToMainSync
126 | * @param {string} message
127 | * @param {...*} [args] - whatever arguments the message needs
128 | * @return {Object} results
129 | *
130 | * Send `message` with `...args` to main process synchronized and return a result which is responded from main process
131 | */
132 | ipcPlus.sendToMainSync = function (message, ...args) {
133 | if ( typeof message !== 'string' ) {
134 | console.error('Call to `sendToMainSync` failed. The message must be a string.');
135 | return;
136 | }
137 |
138 | return ipcRenderer.sendSync.apply( ipcRenderer, [message, ...args] );
139 | };
140 |
141 | /**
142 | * @method sendToMain
143 | * @param {string} message
144 | * @param {...*} [args] - whatever arguments the message needs
145 | *
146 | * Send `message` with `...args` to main process asynchronously.
147 | */
148 | ipcPlus.sendToMain = function (message, ...args) {
149 | if ( typeof message !== 'string' ) {
150 | console.error('Call to `sendToMain` failed. The message must be a string.');
151 | return;
152 | }
153 |
154 | let opts = _popReplyAndTimeout(args);
155 | let sessionId;
156 |
157 | if ( opts ) {
158 | sessionId = _newSession(message, `${winID}@renderer`, opts.reply, opts.timeout);
159 |
160 | args = [`${moduleID}:renderer2main`, message, ...args, _ipcOption({
161 | sessionId: sessionId,
162 | waitForReply: true,
163 | timeout: opts.timeout, // this is only used as debug info
164 | })];
165 | } else {
166 | args = [message, ...args];
167 | }
168 |
169 | ipcRenderer.send.apply( ipcRenderer, args );
170 |
171 | return sessionId;
172 | };
173 |
174 | /**
175 | * @method cancelRequest
176 | * @param {number} sessionId
177 | *
178 | * Cancel request sent to main or renderer process.
179 | */
180 | ipcPlus.cancelRequest = function (sessionId) {
181 | _closeSession(sessionId);
182 | };
183 |
184 | /**
185 | * @method _closeAllSessions
186 | *
187 | * Force close all sessions.
188 | * Usually you don't need to call this function. This is for page reload or app relaunch purpose.
189 | */
190 | ipcPlus._closeAllSessions = function () {
191 | let ids = Object.keys(_id2sessionInfo);
192 |
193 | for ( let i = 0; i < ids.length; ++i ) {
194 | let sessionId = ids[i];
195 | _closeSession (sessionId);
196 | }
197 | };
198 |
199 | /**
200 | * @property {boolean} debug
201 | *
202 | * Turn on/off the debug code in the module.
203 | */
204 | Object.defineProperty(ipcPlus, 'debug', {
205 | enumerable: true,
206 | get () { return _debug; },
207 | set ( value ) { _debug = value; },
208 | });
209 |
210 | // ========================================
211 | // Internal
212 | // ========================================
213 |
214 | function _newSession ( message, prefix, fn, timeout ) {
215 | let sessionId = `${prefix}:${_nextSessionId++}`;
216 | let timeoutId;
217 |
218 | if ( timeout !== -1 ) {
219 | timeoutId = setTimeout(() => {
220 | let info = _id2sessionInfo[sessionId];
221 |
222 | if ( info ) {
223 | delete _id2sessionInfo[sessionId];
224 |
225 | info.callback(new _ErrorTimeout( message, sessionId, timeout ));
226 | }
227 |
228 | // DISABLE
229 | // if ( _debug ) {
230 | // console.warn(`ipc timeout. message: ${message}, session: ${sessionId}`);
231 | // }
232 | }, timeout);
233 | }
234 |
235 | _id2sessionInfo[sessionId] = {
236 | sessionId: sessionId,
237 | timeoutId: timeoutId,
238 | callback: fn,
239 | message: message,
240 | };
241 |
242 | return sessionId;
243 | }
244 |
245 | function _closeSession ( sessionId ) {
246 | let info = _id2sessionInfo[sessionId];
247 |
248 | if ( info ) {
249 | delete _id2sessionInfo[sessionId];
250 |
251 | if ( info.timeoutId ) {
252 | clearTimeout(info.timeoutId);
253 | }
254 | }
255 |
256 | return info;
257 | }
258 |
259 | function _main2rendererOpts (event, message, ...args) {
260 | if ( args.length === 0 ) {
261 | return ipcRenderer.emit( message, event );
262 | }
263 |
264 | // process waitForReply option
265 | let opts = _popOptions(args);
266 | if ( opts && opts.waitForReply ) {
267 | // NOTE: do not directly use event.sender in event.reply, it will cause Electron devtools crash
268 | let sender = event.sender;
269 | let msg = message;
270 | event.reply = function (...replyArgs) {
271 | if ( _wrapError(replyArgs) === false ) {
272 | console.warn(`Invalid argument for event.reply of "${msg}": the first argument must be an instance of Error or null`);
273 | }
274 |
275 | let replyOpts = _ipcOption({
276 | sessionId: opts.sessionId
277 | });
278 | replyArgs = [`${moduleID}:reply`, ...replyArgs, replyOpts];
279 | return sender.send.apply( sender, replyArgs );
280 | };
281 | }
282 |
283 | // refine the args
284 | args = [message, event, ...args];
285 | return ipcRenderer.emit.apply( ipcRenderer, args );
286 | }
287 |
288 | // ========================================
289 | // ipcPlus
290 | // ========================================
291 |
292 | ipcRenderer.on(`${moduleID}:main2renderer`, (event, message, ...args) => {
293 | if ( _main2rendererOpts.apply ( null, [event, message, ...args] ) === false ) {
294 | console.error( `Message "${message}" from main to renderer failed, no response was received.` );
295 | }
296 | });
297 |
298 | ipcRenderer.on(`${moduleID}:reply`, (event, ...args) => {
299 | let opts = _popOptions(args);
300 |
301 | // create a new Error in current process
302 | // NOTE: don't do this in _unwrapError, it will make the new Error have wrong stack.
303 | let err = _unwrapError(args);
304 | if ( err ) {
305 | let lines = err.stack.split('\n');
306 | lines.shift(); // remove the message cause we add it in new Error;
307 |
308 | let newErr = new Error(err.message);
309 | newErr.stack += '\n\t--------------------\n' + lines.join('\n');
310 | newErr.code = err.code;
311 | newErr.code = err.code;
312 | newErr.errno = err.errno;
313 | newErr.syscall = err.syscall;
314 |
315 | args[0] = newErr;
316 | }
317 |
318 | // NOTE: we must close session before it apply, this will prevent window.close() invoked in
319 | // reply callback will make _closeSession called second times.
320 | let info = _closeSession(opts.sessionId);
321 | if (info) {
322 | info.callback.apply(null, args);
323 | }
324 | });
325 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-ipc-plus",
3 | "version": "1.3.4",
4 | "description": "Improved IPC operations for electron",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "electron",
8 | "test": "mocha test/*.spec.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/jwu/electron-ipc-plus.git"
13 | },
14 | "keywords": [
15 | "Electron",
16 | "ipc"
17 | ],
18 | "author": "jwu",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/jwu/electron-ipc-plus/issues"
22 | },
23 | "homepage": "https://github.com/jwu/electron-ipc-plus",
24 | "dependencies": {
25 | "electron-platform": "^1.2.0"
26 | },
27 | "devDependencies": {
28 | "electron": "^1.6.2",
29 | "mocha": "^3.2.0",
30 | "spectron": "^3.6.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2all-exclude-self/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2all-exclude-self/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToAll('app:hello', ipcPlus.option({excludeSelf: true}));
15 | ipcPlus.sendToAll('app:hello', 'alpha', ipcPlus.option({excludeSelf: true}));
16 | ipcPlus.sendToAll('app:hello', 'beta', ipcPlus.option({excludeSelf: true}));
17 | ipcPlus.sendToAll('app:hello', 'cell', ipcPlus.option({excludeSelf: true}));
18 | }
19 | }
20 |
21 | app.on('ready', function () {
22 | for ( let i = 0; i < maxWins; ++i ) {
23 | let win = new BrowserWindow({
24 | x: 100 + 210*i,
25 | y: 100,
26 | width: 200,
27 | height: 200
28 | });
29 | win.loadURL('file://' + __dirname + '/index.html');
30 | win.webContents.once('dom-ready', domReady);
31 |
32 | wins.push(win);
33 | }
34 | });
35 |
36 | ipcPlus.on('app:hello', (event, ...args) => {
37 | global.ipcCalls.push(`app:hello ${args}`);
38 | });
39 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2all-exclude-self/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2all/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2all/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToAll('app:hello');
15 | ipcPlus.sendToAll('app:hello', 'alpha');
16 | ipcPlus.sendToAll('app:hello', 'beta');
17 | ipcPlus.sendToAll('app:hello', 'cell');
18 | }
19 | }
20 |
21 | app.on('ready', function () {
22 | for ( let i = 0; i < maxWins; ++i ) {
23 | let win = new BrowserWindow({
24 | x: 100 + 210*i,
25 | y: 100,
26 | width: 200,
27 | height: 200
28 | });
29 | win.loadURL('file://' + __dirname + '/index.html');
30 | win.webContents.once('dom-ready', domReady);
31 |
32 | wins.push(win);
33 | }
34 | });
35 |
36 | ipcPlus.on('app:hello', (event, ...args) => {
37 | global.ipcCalls.push(`app:hello ${args}`);
38 | });
39 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2all/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2main-reply/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2main-reply/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 |
15 | ipcPlus.on('app:hello', (event, ...args) => {
16 | global.ipcCalls.push(`app:hello ${args}`);
17 | event.reply(`${args} received`);
18 | });
19 | ipcPlus.on('app:hello-error', (event, ...args) => {
20 | global.ipcCalls.push(`app:hello ${args}`);
21 | event.reply(new Error('user'), `${args} failed`);
22 | });
23 |
24 | ipcPlus.sendToMain('app:hello', (err, msg) => {
25 | global.ipcCalls.push(`${msg}`);
26 | });
27 | ipcPlus.sendToMain('app:hello', 'alpha', (err, msg) => {
28 | global.ipcCalls.push(`${msg}`);
29 | });
30 | ipcPlus.sendToMain('app:hello', 'beta', (err, msg) => {
31 | global.ipcCalls.push(`${msg}`);
32 | });
33 | ipcPlus.sendToMain('app:hello', 'cell', (err, msg) => {
34 | global.ipcCalls.push(`${msg}`);
35 | });
36 | ipcPlus.sendToMain('app:hello-error', 'foobar', (err, msg) => {
37 | global.ipcCalls.push(`${err}, ${msg}`);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2main-reply/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2main/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2main/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 |
15 | ipcPlus.on('app:hello', (event, ...args) => {
16 | global.ipcCalls.push(`app:hello ${args}`);
17 | });
18 |
19 | ipcPlus.sendToMain('app:hello');
20 | ipcPlus.sendToMain('app:hello', 'alpha');
21 | ipcPlus.sendToMain('app:hello', 'beta');
22 | ipcPlus.sendToMain('app:hello', 'cell');
23 | });
24 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-a-user-error/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-a-user-error/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let win;
7 |
8 | function domReady () {
9 | ipcPlus.sendToWin(win, 'app:hello', (err, msg) => {
10 | if ( err ) {
11 | global.ipcCalls.push(`${err}, ${msg}`);
12 | return;
13 | }
14 | global.ipcCalls.push(`${msg}`);
15 | });
16 | ipcPlus.sendToWin(win, 'app:hello', 'alpha', (err, msg) => {
17 | if ( err ) {
18 | global.ipcCalls.push(`${err}, ${msg}`);
19 | return;
20 | }
21 | global.ipcCalls.push(`${msg}`);
22 | });
23 | ipcPlus.sendToWin(win, 'app:hello', 'beta', (err, msg) => {
24 | if ( err ) {
25 | global.ipcCalls.push(`${err}, ${msg}`);
26 | return;
27 | }
28 | global.ipcCalls.push(`${msg}`);
29 | });
30 | ipcPlus.sendToWin(win, 'app:hello', 'cell', (err, msg) => {
31 | if ( err ) {
32 | global.ipcCalls.push(`${err}, ${msg}`);
33 | return;
34 | }
35 | global.ipcCalls.push(`${msg}`);
36 | });
37 | }
38 |
39 | app.on('ready', function () {
40 | win = new BrowserWindow({
41 | x: 100 + 210,
42 | y: 100,
43 | width: 200,
44 | height: 200
45 | });
46 | win.loadURL('file://' + __dirname + '/index.html');
47 | win.webContents.once('dom-ready', domReady);
48 | });
49 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-a-user-error/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-cancel-request/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-cancel-request/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let win;
7 |
8 | function domReady () {
9 | let id;
10 |
11 | id = ipcPlus.sendToWin(win, 'app:hello', (err, msg) => {
12 | if ( err ) {
13 | global.ipcCalls.push(`${err}, ${msg}`);
14 | return;
15 | }
16 | global.ipcCalls.push(`${msg}`);
17 | });
18 | ipcPlus.cancelRequest(id);
19 |
20 | id = ipcPlus.sendToWin(win, 'app:hello', 'alpha', (err, msg) => {
21 | if ( err ) {
22 | global.ipcCalls.push(`${err}, ${msg}`);
23 | return;
24 | }
25 | global.ipcCalls.push(`${msg}`);
26 | });
27 | ipcPlus.cancelRequest(id);
28 |
29 | id = ipcPlus.sendToWin(win, 'app:hello', 'beta', (err, msg) => {
30 | if ( err ) {
31 | global.ipcCalls.push(`${err}, ${msg}`);
32 | return;
33 | }
34 | global.ipcCalls.push(`${msg}`);
35 | });
36 | ipcPlus.cancelRequest(id);
37 |
38 | id = ipcPlus.sendToWin(win, 'app:hello', 'cell', (err, msg) => {
39 | if ( err ) {
40 | global.ipcCalls.push(`${err}, ${msg}`);
41 | return;
42 | }
43 | global.ipcCalls.push(`${msg}`);
44 | });
45 | ipcPlus.cancelRequest(id);
46 | }
47 |
48 | app.on('ready', function () {
49 | win = new BrowserWindow({
50 | x: 100 + 210,
51 | y: 100,
52 | width: 200,
53 | height: 200
54 | });
55 | win.loadURL('file://' + __dirname + '/index.html');
56 | win.webContents.once('dom-ready', domReady);
57 | });
58 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-cancel-request/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-error-invalid-first-arg/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-error-invalid-first-arg/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let win;
7 |
8 | function domReady () {
9 | ipcPlus.sendToWin(win, 'app:hello', (err, msg) => {
10 | if ( err ) {
11 | global.ipcCalls.push(`${err.code}, ${msg}`);
12 | return;
13 | }
14 | global.ipcCalls.push(`${msg}`);
15 | });
16 | ipcPlus.sendToWin(win, 'app:hello', 'alpha', (err, msg) => {
17 | if ( err ) {
18 | global.ipcCalls.push(`${err.code}, ${msg}`);
19 | return;
20 | }
21 | global.ipcCalls.push(`${msg}`);
22 | });
23 |
24 | ipcPlus.sendToWin(win, 'app:hello', 'beta', (err, msg) => {
25 | if ( err ) {
26 | global.ipcCalls.push(`${err.code}, ${msg}`);
27 | return;
28 | }
29 | global.ipcCalls.push(`${msg}`);
30 | });
31 |
32 | ipcPlus.sendToWin(win, 'app:hello', 'cell', (err, msg) => {
33 | if ( err ) {
34 | global.ipcCalls.push(`${err.code}, ${msg}`);
35 | return;
36 | }
37 | global.ipcCalls.push(`${msg}`);
38 | });
39 | }
40 |
41 | app.on('ready', function () {
42 | win = new BrowserWindow({
43 | x: 100 + 210,
44 | y: 100,
45 | width: 200,
46 | height: 200
47 | });
48 | win.loadURL('file://' + __dirname + '/index.html');
49 | win.webContents.once('dom-ready', domReady);
50 | });
51 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-error-invalid-first-arg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-error-win-destroyed/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-error-win-destroyed/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-error-win-destroyed/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let win, win2;
7 | let readyCnt = 0;
8 |
9 | function domReady () {
10 | ++readyCnt;
11 |
12 | if ( readyCnt === 2 ) {
13 | ipcPlus.sendToWin(win, 'app:hello', 'foobar', (err, msg) => {
14 | if ( err ) {
15 | global.ipcCalls.push(`${err.code}, ${msg}`);
16 | return;
17 | }
18 | global.ipcCalls.push(`${msg}`);
19 | });
20 |
21 | setTimeout(() => {
22 | win.close();
23 | }, 100);
24 | }
25 | }
26 |
27 | app.on('ready', function () {
28 | win = new BrowserWindow({
29 | x: 300,
30 | y: 100,
31 | width: 200,
32 | height: 200
33 | });
34 | win.loadURL('file://' + __dirname + '/index.html');
35 | win.webContents.once('dom-ready', domReady);
36 |
37 | win2 = new BrowserWindow({
38 | x: 300 + 210,
39 | y: 100,
40 | width: 200,
41 | height: 200
42 | });
43 | win2.loadURL('file://' + __dirname + '/index2.html');
44 | win2.webContents.once('dom-ready', domReady);
45 | });
46 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-error-win-destroyed/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-more-than-once/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-more-than-once/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let win;
7 |
8 | function domReady () {
9 | ipcPlus.sendToWin(win, 'app:hello', (err, msg) => {
10 | if ( err ) {
11 | global.ipcCalls.push(`${err}, ${msg}`);
12 | return;
13 | }
14 | global.ipcCalls.push(`${msg}`);
15 | });
16 |
17 | ipcPlus.sendToWin(win, 'app:hello', 'alpha', (err, msg) => {
18 | if ( err ) {
19 | global.ipcCalls.push(`${err}, ${msg}`);
20 | return;
21 | }
22 | global.ipcCalls.push(`${msg}`);
23 | });
24 |
25 | ipcPlus.sendToWin(win, 'app:hello', 'beta', (err, msg) => {
26 | if ( err ) {
27 | global.ipcCalls.push(`${err}, ${msg}`);
28 | return;
29 | }
30 | global.ipcCalls.push(`${msg}`);
31 | });
32 |
33 | ipcPlus.sendToWin(win, 'app:hello', 'cell', (err, msg) => {
34 | if ( err ) {
35 | global.ipcCalls.push(`${err}, ${msg}`);
36 | return;
37 | }
38 | global.ipcCalls.push(`${msg}`);
39 | });
40 | }
41 |
42 | app.on('ready', function () {
43 | win = new BrowserWindow({
44 | x: 100 + 210,
45 | y: 100,
46 | width: 200,
47 | height: 200
48 | });
49 | win.loadURL('file://' + __dirname + '/index.html');
50 | win.webContents.once('dom-ready', domReady);
51 | });
52 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-more-than-once/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-nested/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-nested/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | // win0
15 | ipcPlus.sendToWin(wins[0], 'app:hello', (err, msg) => {
16 | if ( err ) {
17 | global.ipcCalls.push(`${err}, ${msg}`);
18 | return;
19 | }
20 | global.ipcCalls.push(`${msg}`);
21 |
22 | ipcPlus.sendToWin(wins[0], 'app:hello-nested', (err, msg) => {
23 | if ( err ) {
24 | global.ipcCalls.push(`${err}, ${msg}`);
25 | return;
26 | }
27 | global.ipcCalls.push(`${msg}`);
28 | });
29 | });
30 | ipcPlus.sendToWin(wins[0], 'app:hello', 'alpha', (err, msg) => {
31 | if ( err ) {
32 | global.ipcCalls.push(`${err}, ${msg}`);
33 | return;
34 | }
35 | global.ipcCalls.push(`${msg}`);
36 |
37 | ipcPlus.sendToWin(wins[0], 'app:hello-nested', 'alpha-nested', (err, msg) => {
38 | if ( err ) {
39 | global.ipcCalls.push(`${err}, ${msg}`);
40 | return;
41 | }
42 | global.ipcCalls.push(`${msg}`);
43 | });
44 | });
45 |
46 | // win1
47 | setTimeout(() => {
48 | ipcPlus.sendToWin(wins[1], 'app:hello', (err, msg) => {
49 | if ( err ) {
50 | global.ipcCalls.push(`${err}, ${msg}`);
51 | return;
52 | }
53 | global.ipcCalls.push(`${msg}`);
54 |
55 | ipcPlus.sendToWin(wins[1], 'app:hello-nested', (err, msg) => {
56 | if ( err ) {
57 | global.ipcCalls.push(`${err}, ${msg}`);
58 | return;
59 | }
60 | global.ipcCalls.push(`${msg}`);
61 | });
62 | });
63 | ipcPlus.sendToWin(wins[1], 'app:hello', 'beta', (err, msg) => {
64 | if ( err ) {
65 | global.ipcCalls.push(`${err}, ${msg}`);
66 | return;
67 | }
68 | global.ipcCalls.push(`${msg}`);
69 |
70 | ipcPlus.sendToWin(wins[1], 'app:hello-nested', 'beta-nested', (err, msg) => {
71 | if ( err ) {
72 | global.ipcCalls.push(`${err}, ${msg}`);
73 | return;
74 | }
75 | global.ipcCalls.push(`${msg}`);
76 | });
77 | });
78 | }, 100);
79 |
80 | // win2
81 | setTimeout(() => {
82 | ipcPlus.sendToWin(wins[2], 'app:hello', (err, msg) => {
83 | if ( err ) {
84 | global.ipcCalls.push(`${err}, ${msg}`);
85 | return;
86 | }
87 | global.ipcCalls.push(`${msg}`);
88 |
89 | ipcPlus.sendToWin(wins[2], 'app:hello-nested', (err, msg) => {
90 | if ( err ) {
91 | global.ipcCalls.push(`${err}, ${msg}`);
92 | return;
93 | }
94 | global.ipcCalls.push(`${msg}`);
95 | });
96 | });
97 | ipcPlus.sendToWin(wins[2], 'app:hello', 'cell', (err, msg) => {
98 | if ( err ) {
99 | global.ipcCalls.push(`${err}, ${msg}`);
100 | return;
101 | }
102 | global.ipcCalls.push(`${msg}`);
103 |
104 | ipcPlus.sendToWin(wins[2], 'app:hello-nested', 'cell-nested', (err, msg) => {
105 | if ( err ) {
106 | global.ipcCalls.push(`${err}, ${msg}`);
107 | return;
108 | }
109 | global.ipcCalls.push(`${msg}`);
110 | });
111 | });
112 | }, 200);
113 | }
114 | }
115 |
116 | app.on('ready', function () {
117 | for ( let i = 0; i < maxWins; ++i ) {
118 | let win = new BrowserWindow({
119 | x: 100 + 210*i,
120 | y: 100,
121 | width: 200,
122 | height: 200
123 | });
124 | win.loadURL('file://' + __dirname + '/index.html');
125 | win.webContents.once('dom-ready', domReady);
126 |
127 | wins.push(win);
128 | }
129 | });
130 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-nested/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-timeout/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-timeout/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let win;
7 |
8 | function domReady () {
9 | ipcPlus.sendToWin(win, 'app:hello', (err, msg) => {
10 | if ( err && err.code === 'ETIMEDOUT' ) {
11 | global.ipcCalls.push('timeout');
12 | return;
13 | }
14 | global.ipcCalls.push(`${msg}`);
15 | }, 100);
16 |
17 | ipcPlus.sendToWin(win, 'app:hello', 'alpha', (err, msg) => {
18 | if ( err && err.code === 'ETIMEDOUT' ) {
19 | global.ipcCalls.push(`timeout alpha`);
20 | return;
21 | }
22 | global.ipcCalls.push(`${msg}`);
23 | }, 100);
24 |
25 | ipcPlus.sendToWin(win, 'app:hello', 'beta', (err, msg) => {
26 | if ( err && err.code === 'ETIMEDOUT' ) {
27 | global.ipcCalls.push(`timeout beta`);
28 | return;
29 | }
30 | global.ipcCalls.push(`${msg}`);
31 | }, 100);
32 |
33 | ipcPlus.sendToWin(win, 'app:hello', 'cell', (err, msg) => {
34 | if ( err && err.code === 'ETIMEDOUT' ) {
35 | global.ipcCalls.push(`timeout cell`);
36 | return;
37 | }
38 | global.ipcCalls.push(`${msg}`);
39 | }, 400);
40 | }
41 |
42 | app.on('ready', function () {
43 | win = new BrowserWindow({
44 | x: 100 + 210,
45 | y: 100,
46 | width: 200,
47 | height: 200
48 | });
49 | win.loadURL('file://' + __dirname + '/index.html');
50 | win.webContents.once('dom-ready', domReady);
51 | });
52 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply-timeout/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | // win0
15 | ipcPlus.sendToWin(wins[0], 'app:hello', (err, msg) => {
16 | if ( err ) {
17 | global.ipcCalls.push(`${err}, ${msg}`);
18 | return;
19 | }
20 | global.ipcCalls.push(`${msg}`);
21 | });
22 | ipcPlus.sendToWin(wins[0], 'app:hello', 'alpha', (err, msg) => {
23 | if ( err ) {
24 | global.ipcCalls.push(`${err}, ${msg}`);
25 | return;
26 | }
27 | global.ipcCalls.push(`${msg}`);
28 | });
29 |
30 | // win1
31 | setTimeout(() => {
32 | ipcPlus.sendToWin(wins[1], 'app:hello', (err, msg) => {
33 | if ( err ) {
34 | global.ipcCalls.push(`${err}, ${msg}`);
35 | return;
36 | }
37 | global.ipcCalls.push(`${msg}`);
38 | });
39 | ipcPlus.sendToWin(wins[1], 'app:hello', 'beta', (err, msg) => {
40 | if ( err ) {
41 | global.ipcCalls.push(`${err}, ${msg}`);
42 | return;
43 | }
44 | global.ipcCalls.push(`${msg}`);
45 | });
46 | }, 100);
47 |
48 | // win2
49 | setTimeout(() => {
50 | ipcPlus.sendToWin(wins[2], 'app:hello', (err, msg) => {
51 | if ( err ) {
52 | global.ipcCalls.push(`${err}, ${msg}`);
53 | return;
54 | }
55 | global.ipcCalls.push(`${msg}`);
56 | });
57 | ipcPlus.sendToWin(wins[2], 'app:hello', 'cell', (err, msg) => {
58 | if ( err ) {
59 | global.ipcCalls.push(`${err}, ${msg}`);
60 | return;
61 | }
62 | global.ipcCalls.push(`${msg}`);
63 | });
64 | }, 200);
65 | }
66 | }
67 |
68 | app.on('ready', function () {
69 | for ( let i = 0; i < maxWins; ++i ) {
70 | let win = new BrowserWindow({
71 | x: 100 + 210*i,
72 | y: 100,
73 | width: 200,
74 | height: 200
75 | });
76 | win.loadURL('file://' + __dirname + '/index.html');
77 | win.webContents.once('dom-ready', domReady);
78 |
79 | wins.push(win);
80 | }
81 | });
82 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win-reply/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToWin(wins[0], 'app:hello');
15 | ipcPlus.sendToWin(wins[0], 'app:hello', 'alpha');
16 |
17 | ipcPlus.sendToWin(wins[1], 'app:hello');
18 | ipcPlus.sendToWin(wins[1], 'app:hello', 'beta');
19 |
20 | ipcPlus.sendToWin(wins[2], 'app:hello');
21 | ipcPlus.sendToWin(wins[2], 'app:hello', 'cell');
22 | }
23 | }
24 |
25 | app.on('ready', function () {
26 | for ( let i = 0; i < maxWins; ++i ) {
27 | let win = new BrowserWindow({
28 | x: 100 + 210*i,
29 | y: 100,
30 | width: 200,
31 | height: 200
32 | });
33 | win.loadURL('file://' + __dirname + '/index.html');
34 | win.webContents.once('dom-ready', domReady);
35 |
36 | wins.push(win);
37 | }
38 | });
39 |
40 | ipcPlus.on('app:hello', (event, ...args) => {
41 | global.ipcCalls.push(`app:hello ${args}`);
42 | });
43 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2win/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2wins/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2wins/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToWins('app:hello');
15 | ipcPlus.sendToWins('app:hello', 'alpha');
16 | ipcPlus.sendToWins('app:hello', 'beta');
17 | ipcPlus.sendToWins('app:hello', 'cell');
18 | }
19 | }
20 |
21 | app.on('ready', function () {
22 | for ( let i = 0; i < maxWins; ++i ) {
23 | let win = new BrowserWindow({
24 | x: 100 + 210*i,
25 | y: 100,
26 | width: 200,
27 | height: 200
28 | });
29 | win.loadURL('file://' + __dirname + '/index.html');
30 | win.webContents.once('dom-ready', domReady);
31 |
32 | wins.push(win);
33 | }
34 | });
35 |
36 | ipcPlus.on('app:hello', (event, ...args) => {
37 | global.ipcCalls.push(`app:hello ${args}`);
38 | });
39 |
--------------------------------------------------------------------------------
/test/fixtures/app-main2wins/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all-exclude-self/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all-exclude-self/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToAll('app:all-windows-ready');
15 | }
16 | }
17 |
18 | app.on('ready', function () {
19 | let i = 0;
20 | let mainWin = new BrowserWindow({
21 | x: 100 + 210*i,
22 | y: 100,
23 | width: 200,
24 | height: 200
25 | });
26 | mainWin.loadURL('file://' + __dirname + '/send.html');
27 | mainWin.webContents.once('dom-ready', domReady);
28 |
29 | wins.push(mainWin);
30 |
31 | for ( i = 1; i < maxWins; ++i ) {
32 | let win = new BrowserWindow({
33 | x: 100 + 210*i,
34 | y: 100,
35 | width: 200,
36 | height: 200
37 | });
38 | win.loadURL('file://' + __dirname + '/index.html');
39 | win.webContents.once('dom-ready', domReady);
40 |
41 | wins.push(win);
42 | }
43 | });
44 |
45 | ipcPlus.on('app:hello', (event, ...args) => {
46 | global.ipcCalls.push(`app:hello ${args}`);
47 | });
48 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all-exclude-self/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all-exclude-self/send.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToAll('app:all-windows-ready');
15 | }
16 | }
17 |
18 | app.on('ready', function () {
19 | let i = 0;
20 | let mainWin = new BrowserWindow({
21 | x: 100 + 210*i,
22 | y: 100,
23 | width: 200,
24 | height: 200
25 | });
26 | mainWin.loadURL('file://' + __dirname + '/send.html');
27 | mainWin.webContents.once('dom-ready', domReady);
28 |
29 | wins.push(mainWin);
30 |
31 | for ( i = 1; i < maxWins; ++i ) {
32 | let win = new BrowserWindow({
33 | x: 100 + 210*i,
34 | y: 100,
35 | width: 200,
36 | height: 200
37 | });
38 | win.loadURL('file://' + __dirname + '/index.html');
39 | win.webContents.once('dom-ready', domReady);
40 |
41 | wins.push(win);
42 | }
43 | });
44 |
45 | ipcPlus.on('app:hello', (event, ...args) => {
46 | global.ipcCalls.push(`app:hello ${args}`);
47 | });
48 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2all/send.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-a-user-error/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-a-user-error/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | event.reply(new Error(`user`), `${args} failed`);
19 | });
20 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-a-user-error/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-cancel-request/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-cancel-request/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | setTimeout(() => {
19 | event.reply(null, `${args} received`);
20 | }, 200);
21 | });
22 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-cancel-request/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-error-no-callback/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-error-no-callback/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | if ( event.reply ) {
19 | global.ipcCalls.push(`${args} replied`);
20 | event.reply(null, `${args} received`);
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-error-no-callback/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-error-win-destroyed/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-error-win-destroyed/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-error-win-destroyed/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let win, win2;
7 | app.on('ready', function () {
8 | win = new BrowserWindow({
9 | x: 300,
10 | y: 100,
11 | width: 200,
12 | height: 200
13 | });
14 | win.loadURL('file://' + __dirname + '/index.html');
15 |
16 | win2 = new BrowserWindow({
17 | x: 300 + 210,
18 | y: 100,
19 | width: 200,
20 | height: 200
21 | });
22 | win2.loadURL('file://' + __dirname + '/index2.html');
23 | });
24 |
25 | ipcPlus.on('app:hello', (event, ...args) => {
26 | win.close();
27 |
28 | global.ipcCalls.push(`app:hello ${args}`);
29 | setTimeout(() => {
30 | event.reply(null, `${args} received`);
31 | }, 300);
32 | });
33 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-error-win-destroyed/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-invalid-first-arg/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-invalid-first-arg/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | event.reply(`${args} received`); // NOTE: first argument invalid
19 | });
20 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-invalid-first-arg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-more-than-once/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-more-than-once/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | event.reply(null, `${args} received`);
19 | event.reply(null, `${args} received 02`);
20 | });
21 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-more-than-once/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-nested/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-nested/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | event.reply(null, `${args} received`);
19 | });
20 |
21 | ipcPlus.on('app:hello-nested', (event, ...args) => {
22 | global.ipcCalls.push(`app:hello-nested ${args}`);
23 | event.reply(null, `${args} received`);
24 | });
25 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-nested/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-timeout/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-timeout/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | setTimeout(() => {
19 | event.reply(null, `${args} received`);
20 | }, 200);
21 | });
22 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply-timeout/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | event.reply(null, `${args} received`);
19 | });
20 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main-reply/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | app.on('ready', function () {
7 | let win = new BrowserWindow({
8 | x: 300,
9 | y: 100,
10 | width: 200,
11 | height: 200
12 | });
13 | win.loadURL('file://' + __dirname + '/index.html');
14 | });
15 |
16 | ipcPlus.on('app:hello', (event, ...args) => {
17 | global.ipcCalls.push(`app:hello ${args}`);
18 | });
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins-exclude-self/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins-exclude-self/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToAll('app:all-windows-ready');
15 | }
16 | }
17 |
18 | app.on('ready', function () {
19 | let i = 0;
20 | let mainWin = new BrowserWindow({
21 | x: 100 + 210*i,
22 | y: 100,
23 | width: 200,
24 | height: 200
25 | });
26 | mainWin.loadURL('file://' + __dirname + '/send.html');
27 | mainWin.webContents.once('dom-ready', domReady);
28 |
29 | wins.push(mainWin);
30 |
31 | for ( i = 1; i < maxWins; ++i ) {
32 | let win = new BrowserWindow({
33 | x: 100 + 210*i,
34 | y: 100,
35 | width: 200,
36 | height: 200
37 | });
38 | win.loadURL('file://' + __dirname + '/index.html');
39 | win.webContents.once('dom-ready', domReady);
40 |
41 | wins.push(win);
42 | }
43 | });
44 |
45 | ipcPlus.on('app:hello', (event, ...args) => {
46 | global.ipcCalls.push(`app:hello ${args}`);
47 | });
48 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins-exclude-self/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins-exclude-self/send.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron');
2 | const ipcPlus = require('../../../index.js');
3 |
4 | global.ipcCalls = [];
5 |
6 | let readyCnt = 0;
7 | let maxWins = 3;
8 | let wins = [];
9 |
10 | function domReady () {
11 | ++readyCnt;
12 |
13 | if ( readyCnt === maxWins ) {
14 | ipcPlus.sendToAll('app:all-windows-ready');
15 | }
16 | }
17 |
18 | app.on('ready', function () {
19 | let i = 0;
20 | let mainWin = new BrowserWindow({
21 | x: 100 + 210*i,
22 | y: 100,
23 | width: 200,
24 | height: 200
25 | });
26 | mainWin.loadURL('file://' + __dirname + '/send.html');
27 | mainWin.webContents.once('dom-ready', domReady);
28 |
29 | wins.push(mainWin);
30 |
31 | for ( i = 1; i < maxWins; ++i ) {
32 | let win = new BrowserWindow({
33 | x: 100 + 210*i,
34 | y: 100,
35 | width: 200,
36 | height: 200
37 | });
38 | win.loadURL('file://' + __dirname + '/index.html');
39 | win.webContents.once('dom-ready', domReady);
40 |
41 | wins.push(win);
42 | }
43 | });
44 |
45 | ipcPlus.on('app:hello', (event, ...args) => {
46 | global.ipcCalls.push(`app:hello ${args}`);
47 | });
48 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "main": "main.js"
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/app-win2wins/send.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron App
6 |
7 |
8 |
9 | Testing...
10 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/test/send-to-all.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const electron = require('electron');
5 | const {Application} = require('spectron');
6 | const assert = require('assert');
7 |
8 | describe('app-main2all', function () {
9 | this.timeout(0);
10 | let app = null;
11 |
12 | before(function () {
13 | app = new Application({
14 | path: electron,
15 | args: [path.join(__dirname, 'fixtures', 'app-main2all')]
16 | });
17 | return app.start();
18 | });
19 |
20 | after(function () {
21 | if (app && app.isRunning()) {
22 | return app.stop();
23 | }
24 | });
25 |
26 | it('should be ok', function () {
27 | return app.client
28 | .windowByIndex(0)
29 | .waitUntilWindowLoaded()
30 | .getRenderProcessLogs()
31 | .then(function (logs) {
32 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
33 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
34 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
35 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
36 |
37 | return app.client
38 | .windowByIndex(1)
39 | .waitUntilWindowLoaded()
40 | .getRenderProcessLogs()
41 | .then(function (logs) {
42 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
43 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
44 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
45 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
46 |
47 | return app.client
48 | .windowByIndex(2)
49 | .waitUntilWindowLoaded()
50 | .getRenderProcessLogs()
51 | .then(function (logs) {
52 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
53 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
54 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
55 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
56 |
57 | return app.electron.remote.getGlobal('ipcCalls')
58 | .then(function (ipcCalls) {
59 | assert.equal(ipcCalls.length, 4);
60 | assert.equal(ipcCalls[0], 'app:hello ');
61 | assert.equal(ipcCalls[1], 'app:hello alpha');
62 | assert.equal(ipcCalls[2], 'app:hello beta');
63 | assert.equal(ipcCalls[3], 'app:hello cell');
64 | });
65 | });
66 | });
67 | });
68 | });
69 | });
70 |
71 | describe('app-main2all-exclude-self', function () {
72 | this.timeout(0);
73 | let app = null;
74 |
75 | before(function () {
76 | app = new Application({
77 | path: electron,
78 | args: [path.join(__dirname, 'fixtures', 'app-main2all-exclude-self')]
79 | });
80 | return app.start();
81 | });
82 |
83 | after(function () {
84 | if (app && app.isRunning()) {
85 | return app.stop();
86 | }
87 | });
88 |
89 | it('should be ok', function () {
90 | return app.client
91 | .windowByIndex(0)
92 | .waitUntilWindowLoaded()
93 | .getRenderProcessLogs()
94 | .then(function (logs) {
95 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
96 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
97 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
98 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
99 |
100 | return app.client
101 | .windowByIndex(1)
102 | .waitUntilWindowLoaded()
103 | .getRenderProcessLogs()
104 | .then(function (logs) {
105 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
106 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
107 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
108 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
109 |
110 | return app.client
111 | .windowByIndex(2)
112 | .waitUntilWindowLoaded()
113 | .getRenderProcessLogs()
114 | .then(function (logs) {
115 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
116 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
117 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
118 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
119 |
120 | return app.electron.remote.getGlobal('ipcCalls')
121 | .then(function (ipcCalls) {
122 | assert.equal(ipcCalls.length, 0);
123 | });
124 | });
125 | });
126 | });
127 | });
128 | });
129 |
130 | describe('app-win2all', function () {
131 | this.timeout(0);
132 | let app = null;
133 |
134 | before(function () {
135 | app = new Application({
136 | path: electron,
137 | args: [path.join(__dirname, 'fixtures', 'app-win2all')]
138 | });
139 | return app.start();
140 | });
141 |
142 | after(function () {
143 | if (app && app.isRunning()) {
144 | return app.stop();
145 | }
146 | });
147 |
148 | it('should be ok', function () {
149 | return app.client
150 | .windowByIndex(0)
151 | .waitUntilWindowLoaded()
152 | .getRenderProcessLogs()
153 | .then(function (logs) {
154 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
155 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
156 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
157 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
158 |
159 | return app.client
160 | .windowByIndex(1)
161 | .waitUntilWindowLoaded()
162 | .getRenderProcessLogs()
163 | .then(function (logs) {
164 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
165 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
166 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
167 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
168 |
169 | return app.client
170 | .windowByIndex(2)
171 | .waitUntilWindowLoaded()
172 | .getRenderProcessLogs()
173 | .then(function (logs) {
174 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
175 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
176 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
177 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
178 |
179 | return app.electron.remote.getGlobal('ipcCalls')
180 | .then(function (ipcCalls) {
181 | assert.equal(ipcCalls.length, 4);
182 | assert.equal(ipcCalls[0], 'app:hello ');
183 | assert.equal(ipcCalls[1], 'app:hello alpha');
184 | assert.equal(ipcCalls[2], 'app:hello beta');
185 | assert.equal(ipcCalls[3], 'app:hello cell');
186 | });
187 | });
188 | });
189 | });
190 | });
191 | });
192 |
193 |
194 | describe('app-win2all-exclude-self', function () {
195 | this.timeout(0);
196 | let app = null;
197 |
198 | before(function () {
199 | app = new Application({
200 | path: electron,
201 | args: [path.join(__dirname, 'fixtures', 'app-win2all-exclude-self')]
202 | });
203 | return app.start();
204 | });
205 |
206 | after(function () {
207 | if (app && app.isRunning()) {
208 | return app.stop();
209 | }
210 | });
211 |
212 | it('should be ok', function () {
213 | return app.client
214 | .windowByIndex(0)
215 | .waitUntilWindowLoaded()
216 | .getRenderProcessLogs()
217 | .then(function (logs) {
218 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
219 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
220 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
221 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
222 |
223 | return app.client
224 | .windowByIndex(1)
225 | .waitUntilWindowLoaded()
226 | .getRenderProcessLogs()
227 | .then(function (logs) {
228 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
229 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
230 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
231 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
232 |
233 | return app.client
234 | .windowByIndex(2)
235 | .waitUntilWindowLoaded()
236 | .getRenderProcessLogs()
237 | .then(function (logs) {
238 | assert.equal(logs.length, 0);
239 |
240 | return app.electron.remote.getGlobal('ipcCalls')
241 | .then(function (ipcCalls) {
242 | assert.equal(ipcCalls.length, 4);
243 | assert.equal(ipcCalls[0], 'app:hello ');
244 | assert.equal(ipcCalls[1], 'app:hello alpha');
245 | assert.equal(ipcCalls[2], 'app:hello beta');
246 | assert.equal(ipcCalls[3], 'app:hello cell');
247 | });
248 | });
249 | });
250 | });
251 | });
252 | });
253 |
--------------------------------------------------------------------------------
/test/send-to-main-reply.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const electron = require('electron');
5 | const {Application} = require('spectron');
6 | const assert = require('assert');
7 |
8 | describe('app-win2main-reply', function () {
9 | this.timeout(0);
10 | let app = null;
11 |
12 | before(function () {
13 | app = new Application({
14 | path: electron,
15 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply')]
16 | });
17 | return app.start();
18 | });
19 |
20 | after(function () {
21 | if (app && app.isRunning()) {
22 | return app.stop();
23 | }
24 | });
25 |
26 | it('should be ok', function () {
27 | return app.client
28 | .windowByIndex(0)
29 | .waitUntilWindowLoaded()
30 | .getRenderProcessLogs()
31 | .then(function (logs) {
32 | assert.equal(logs.length, 4);
33 | assert.ok(logs[0].message.indexOf(' received') !== -1);
34 | assert.ok(logs[1].message.indexOf('alpha received') !== -1);
35 | assert.ok(logs[2].message.indexOf('beta received') !== -1);
36 | assert.ok(logs[3].message.indexOf('cell received') !== -1);
37 |
38 | return app.electron.remote.getGlobal('ipcCalls')
39 | .then(function (ipcCalls) {
40 | assert.equal(ipcCalls.length, 4);
41 | assert.equal(ipcCalls[0], 'app:hello ');
42 | assert.equal(ipcCalls[1], 'app:hello alpha');
43 | assert.equal(ipcCalls[2], 'app:hello beta');
44 | assert.equal(ipcCalls[3], 'app:hello cell');
45 | });
46 | });
47 | });
48 | });
49 |
50 | describe('app-win2main-reply-nested', function () {
51 | this.timeout(0);
52 | let app = null;
53 |
54 | before(function () {
55 | app = new Application({
56 | path: electron,
57 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-nested')]
58 | });
59 | return app.start();
60 | });
61 |
62 | after(function () {
63 | if (app && app.isRunning()) {
64 | return app.stop();
65 | }
66 | });
67 |
68 | it('should be ok', function () {
69 | return app.client
70 | .windowByIndex(0)
71 | .waitUntilTextExists('.label', 'Ready')
72 | .getRenderProcessLogs()
73 | .then(function (logs) {
74 | assert.equal(logs.length, 8);
75 | assert.ok(logs[0].message.indexOf(' received') !== -1);
76 | assert.ok(logs[1].message.indexOf('alpha received') !== -1);
77 | assert.ok(logs[2].message.indexOf('beta received') !== -1);
78 | assert.ok(logs[3].message.indexOf('cell received') !== -1);
79 | assert.ok(logs[4].message.indexOf(' received received') !== -1);
80 | assert.ok(logs[5].message.indexOf('alpha received received') !== -1);
81 | assert.ok(logs[6].message.indexOf('beta received received') !== -1);
82 | assert.ok(logs[7].message.indexOf('cell received received') !== -1);
83 |
84 | return app.electron.remote.getGlobal('ipcCalls')
85 | .then(function (ipcCalls) {
86 | assert.equal(ipcCalls.length, 8);
87 | assert.equal(ipcCalls[0], 'app:hello ');
88 | assert.equal(ipcCalls[1], 'app:hello alpha');
89 | assert.equal(ipcCalls[2], 'app:hello beta');
90 | assert.equal(ipcCalls[3], 'app:hello cell');
91 | assert.equal(ipcCalls[4], 'app:hello-nested received');
92 | assert.equal(ipcCalls[5], 'app:hello-nested alpha received');
93 | assert.equal(ipcCalls[6], 'app:hello-nested beta received');
94 | assert.equal(ipcCalls[7], 'app:hello-nested cell received');
95 | });
96 | });
97 | });
98 | });
99 |
100 | describe('app-win2main-reply-more-than-once', function () {
101 | this.timeout(0);
102 | let app = null;
103 |
104 | before(function () {
105 | app = new Application({
106 | path: electron,
107 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-more-than-once')]
108 | });
109 | return app.start();
110 | });
111 |
112 | after(function () {
113 | if (app && app.isRunning()) {
114 | return app.stop();
115 | }
116 | });
117 |
118 | it('should be ok', function () {
119 | return app.client
120 | .windowByIndex(0)
121 | .waitUntilWindowLoaded()
122 | .getRenderProcessLogs()
123 | .then(function (logs) {
124 | assert.equal(logs.length, 4);
125 | assert.ok(logs[0].message.indexOf(' received') !== -1);
126 | assert.ok(logs[1].message.indexOf('alpha received') !== -1);
127 | assert.ok(logs[2].message.indexOf('beta received') !== -1);
128 | assert.ok(logs[3].message.indexOf('cell received') !== -1);
129 |
130 | return app.electron.remote.getGlobal('ipcCalls')
131 | .then(function (ipcCalls) {
132 | assert.equal(ipcCalls.length, 4);
133 | assert.equal(ipcCalls[0], 'app:hello ');
134 | assert.equal(ipcCalls[1], 'app:hello alpha');
135 | assert.equal(ipcCalls[2], 'app:hello beta');
136 | assert.equal(ipcCalls[3], 'app:hello cell');
137 | });
138 | });
139 | });
140 | });
141 |
142 | describe('app-win2main-reply-error-timeout', function () {
143 | this.timeout(0);
144 | let app = null;
145 |
146 | before(function () {
147 | app = new Application({
148 | path: electron,
149 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-timeout')]
150 | });
151 | return app.start();
152 | });
153 |
154 | after(function () {
155 | if (app && app.isRunning()) {
156 | return app.stop();
157 | }
158 | });
159 |
160 | it('should be ok', function () {
161 | return app.client
162 | .windowByIndex(0)
163 | .waitUntilTextExists('.label', 'Ready')
164 | .getRenderProcessLogs()
165 | .then(function (logs) {
166 | assert.equal(logs.length, 4);
167 | assert.ok(logs[0].message.indexOf('timeout') !== -1);
168 | assert.ok(logs[1].message.indexOf('timeout alpha') !== -1);
169 | assert.ok(logs[2].message.indexOf('timeout beta') !== -1);
170 | assert.ok(logs[3].message.indexOf('cell received') !== -1);
171 |
172 | return app.electron.remote.getGlobal('ipcCalls')
173 | .then(function (ipcCalls) {
174 | assert.equal(ipcCalls.length, 4);
175 | assert.equal(ipcCalls[0], 'app:hello ');
176 | assert.equal(ipcCalls[1], 'app:hello alpha');
177 | assert.equal(ipcCalls[2], 'app:hello beta');
178 | assert.equal(ipcCalls[3], 'app:hello cell');
179 | });
180 | });
181 | });
182 | });
183 |
184 | describe('app-win2main-reply-cancel-request', function () {
185 | this.timeout(0);
186 | let app = null;
187 |
188 | before(function () {
189 | app = new Application({
190 | path: electron,
191 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-cancel-request')]
192 | });
193 | return app.start();
194 | });
195 |
196 | after(function () {
197 | if (app && app.isRunning()) {
198 | return app.stop();
199 | }
200 | });
201 |
202 | it('should be ok', function () {
203 | return app.client
204 | .windowByIndex(0)
205 | .waitUntilTextExists('.label', 'Ready')
206 | .getRenderProcessLogs()
207 | .then(function (logs) {
208 | assert.equal(logs.length, 0);
209 |
210 | return app.electron.remote.getGlobal('ipcCalls')
211 | .then(function (ipcCalls) {
212 | assert.equal(ipcCalls.length, 4);
213 | assert.equal(ipcCalls[0], 'app:hello ');
214 | assert.equal(ipcCalls[1], 'app:hello alpha');
215 | assert.equal(ipcCalls[2], 'app:hello beta');
216 | assert.equal(ipcCalls[3], 'app:hello cell');
217 | });
218 | });
219 | });
220 | });
221 |
222 | describe('app-win2main-reply-error-win-destroyed', function () {
223 | this.timeout(0);
224 | let app = null;
225 |
226 | before(function () {
227 | app = new Application({
228 | path: electron,
229 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-error-win-destroyed')]
230 | });
231 | return app.start();
232 | });
233 |
234 | after(function () {
235 | if (app && app.isRunning()) {
236 | return app.stop();
237 | }
238 | });
239 |
240 | it('should be ok', function () {
241 | return app.client
242 | .windowByIndex(0) // NOTE: yes, should be index-0, not index-1
243 | .waitUntilTextExists('.label', 'Ready')
244 | .then(function () {
245 | return app.electron.remote.getGlobal('ipcCalls')
246 | .then(function (ipcCalls) {
247 | assert.equal(ipcCalls.length, 1);
248 | assert.equal(ipcCalls[0], 'app:hello foobar');
249 | });
250 | });
251 | });
252 | });
253 |
254 | describe('app-win2main-reply-error-no-callback', function () {
255 | this.timeout(0);
256 | let app = null;
257 |
258 | before(function () {
259 | app = new Application({
260 | path: electron,
261 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-error-no-callback')]
262 | });
263 | return app.start();
264 | });
265 |
266 | after(function () {
267 | if (app && app.isRunning()) {
268 | return app.stop();
269 | }
270 | });
271 |
272 | it('should be ok', function () {
273 | return app.client
274 | .windowByIndex(0)
275 | .waitUntilWindowLoaded()
276 | .getRenderProcessLogs()
277 | .then(function (logs) {
278 | assert.equal(logs.length, 0);
279 |
280 | return app.electron.remote.getGlobal('ipcCalls')
281 | .then(function (ipcCalls) {
282 | assert.equal(ipcCalls.length, 4);
283 | assert.equal(ipcCalls[0], 'app:hello ');
284 | assert.equal(ipcCalls[1], 'app:hello alpha');
285 | assert.equal(ipcCalls[2], 'app:hello beta');
286 | assert.equal(ipcCalls[3], 'app:hello cell');
287 | });
288 | });
289 | });
290 | });
291 |
292 | describe('app-win2main-reply-a-user-error', function () {
293 | this.timeout(0);
294 | let app = null;
295 |
296 | before(function () {
297 | app = new Application({
298 | path: electron,
299 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-a-user-error')]
300 | });
301 | return app.start();
302 | });
303 |
304 | after(function () {
305 | if (app && app.isRunning()) {
306 | return app.stop();
307 | }
308 | });
309 |
310 | it('should be ok', function () {
311 | return app.client
312 | .windowByIndex(0)
313 | .waitUntilWindowLoaded()
314 | .getRenderProcessLogs()
315 | .then(function (logs) {
316 | assert.equal(logs.length, 4);
317 | assert.ok(logs[0].message.indexOf('Error: user, failed') !== -1);
318 | assert.ok(logs[1].message.indexOf('Error: user, alpha failed') !== -1);
319 | assert.ok(logs[2].message.indexOf('Error: user, beta failed') !== -1);
320 | assert.ok(logs[3].message.indexOf('Error: user, cell failed') !== -1);
321 |
322 | return app.electron.remote.getGlobal('ipcCalls')
323 | .then(function (ipcCalls) {
324 | assert.equal(ipcCalls.length, 4);
325 | assert.equal(ipcCalls[0], 'app:hello ');
326 | assert.equal(ipcCalls[1], 'app:hello alpha');
327 | assert.equal(ipcCalls[2], 'app:hello beta');
328 | assert.equal(ipcCalls[3], 'app:hello cell');
329 | });
330 | });
331 | });
332 | });
333 |
334 | describe('app-win2main-reply-error-invalid-first-arg', function () {
335 | this.timeout(0);
336 | let app = null;
337 |
338 | before(function () {
339 | app = new Application({
340 | path: electron,
341 | args: [path.join(__dirname, 'fixtures', 'app-win2main-reply-invalid-first-arg')]
342 | });
343 | return app.start();
344 | });
345 |
346 | after(function () {
347 | if (app && app.isRunning()) {
348 | return app.stop();
349 | }
350 | });
351 |
352 | it('should be ok', function () {
353 | return app.client
354 | .windowByIndex(0)
355 | .waitUntilWindowLoaded()
356 | .getRenderProcessLogs()
357 | .then(function (logs) {
358 | assert.equal(logs.length, 4);
359 | assert.ok(logs[0].message.indexOf('EINVALIDARGS, received') !== -1);
360 | assert.ok(logs[1].message.indexOf('EINVALIDARGS, alpha received') !== -1);
361 | assert.ok(logs[2].message.indexOf('EINVALIDARGS, beta received') !== -1);
362 | assert.ok(logs[3].message.indexOf('EINVALIDARGS, cell received') !== -1);
363 |
364 | return app.electron.remote.getGlobal('ipcCalls')
365 | .then(function (ipcCalls) {
366 | assert.equal(ipcCalls.length, 4);
367 | assert.equal(ipcCalls[0], 'app:hello ');
368 | assert.equal(ipcCalls[1], 'app:hello alpha');
369 | assert.equal(ipcCalls[2], 'app:hello beta');
370 | assert.equal(ipcCalls[3], 'app:hello cell');
371 | });
372 | });
373 | });
374 | });
375 |
376 | describe('app-main2main-reply', function () {
377 | this.timeout(0);
378 | let app = null;
379 |
380 | before(function () {
381 | app = new Application({
382 | path: electron,
383 | args: [path.join(__dirname, 'fixtures', 'app-main2main-reply')]
384 | });
385 | return app.start();
386 | });
387 |
388 | after(function () {
389 | if (app && app.isRunning()) {
390 | return app.stop();
391 | }
392 | });
393 |
394 | it('should be ok', function () {
395 | return app.electron.remote.getGlobal('ipcCalls')
396 | .then(function (ipcCalls) {
397 | assert.equal(ipcCalls.length, 10);
398 | assert.equal(ipcCalls[0], 'app:hello ');
399 | assert.equal(ipcCalls[1], ' received');
400 | assert.equal(ipcCalls[2], 'app:hello alpha');
401 | assert.equal(ipcCalls[3], 'alpha received');
402 | assert.equal(ipcCalls[4], 'app:hello beta');
403 | assert.equal(ipcCalls[5], 'beta received');
404 | assert.equal(ipcCalls[6], 'app:hello cell');
405 | assert.equal(ipcCalls[7], 'cell received');
406 | assert.equal(ipcCalls[8], 'app:hello foobar');
407 | assert.equal(ipcCalls[9], 'Error: user, foobar failed');
408 | });
409 | });
410 | });
411 |
--------------------------------------------------------------------------------
/test/send-to-main.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const electron = require('electron');
5 | const {Application} = require('spectron');
6 | const assert = require('assert');
7 |
8 | describe('app-win2main', function () {
9 | this.timeout(0);
10 | let app = null;
11 |
12 | before(function () {
13 | app = new Application({
14 | path: electron,
15 | args: [path.join(__dirname, 'fixtures', 'app-win2main')]
16 | });
17 | return app.start();
18 | });
19 |
20 | after(function () {
21 | if (app && app.isRunning()) {
22 | return app.stop();
23 | }
24 | });
25 |
26 | it('should be ok', function () {
27 | return app.client
28 | .windowByIndex(0)
29 | .waitUntilWindowLoaded()
30 | .getRenderProcessLogs()
31 | .then(function (logs) {
32 | assert.equal(logs.length, 0);
33 |
34 | return app.electron.remote.getGlobal('ipcCalls')
35 | .then(function (ipcCalls) {
36 | assert.equal(ipcCalls.length, 4);
37 | assert.equal(ipcCalls[0], 'app:hello ');
38 | assert.equal(ipcCalls[1], 'app:hello alpha');
39 | assert.equal(ipcCalls[2], 'app:hello beta');
40 | assert.equal(ipcCalls[3], 'app:hello cell');
41 | });
42 | });
43 | });
44 | });
45 |
46 | describe('app-main2main', function () {
47 | this.timeout(0);
48 | let app = null;
49 |
50 | before(function () {
51 | app = new Application({
52 | path: electron,
53 | args: [path.join(__dirname, 'fixtures', 'app-main2main')]
54 | });
55 | return app.start();
56 | });
57 |
58 | after(function () {
59 | if (app && app.isRunning()) {
60 | return app.stop();
61 | }
62 | });
63 |
64 | it('should be ok', function () {
65 | return app.electron.remote.getGlobal('ipcCalls')
66 | .then(function (ipcCalls) {
67 | assert.equal(ipcCalls.length, 4);
68 | assert.equal(ipcCalls[0], 'app:hello ');
69 | assert.equal(ipcCalls[1], 'app:hello alpha');
70 | assert.equal(ipcCalls[2], 'app:hello beta');
71 | assert.equal(ipcCalls[3], 'app:hello cell');
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/test/send-to-win-reply.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const electron = require('electron');
5 | const {Application} = require('spectron');
6 | const assert = require('assert');
7 |
8 | describe('app-main2win-reply', function () {
9 | this.timeout(0);
10 | let app = null;
11 |
12 | before(function () {
13 | app = new Application({
14 | path: electron,
15 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply')]
16 | });
17 | return app.start();
18 | });
19 |
20 | after(function () {
21 | if (app && app.isRunning()) {
22 | return app.stop();
23 | }
24 | });
25 |
26 | it('should be ok', function () {
27 | return app.client
28 | .windowByIndex(0)
29 | .waitUntilTextExists('.label', 'Ready')
30 | .getRenderProcessLogs()
31 | .then(function (logs) {
32 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
33 | assert.ok(logs[1].message.indexOf('app:hello cell') !== -1);
34 |
35 | return app.client
36 | .windowByIndex(1)
37 | .waitUntilTextExists('.label', 'Ready')
38 | .getRenderProcessLogs()
39 | .then(function (logs) {
40 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
41 | assert.ok(logs[1].message.indexOf('app:hello beta') !== -1);
42 |
43 | return app.client
44 | .windowByIndex(2)
45 | .waitUntilTextExists('.label', 'Ready')
46 | .getRenderProcessLogs()
47 | .then(function (logs) {
48 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
49 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
50 |
51 | return app.electron.remote.getGlobal('ipcCalls')
52 | .then(function (ipcCalls) {
53 | assert.equal(ipcCalls.length, 6);
54 | assert.equal(ipcCalls[0], ' received');
55 | assert.equal(ipcCalls[1], 'alpha received');
56 | assert.equal(ipcCalls[2], ' received');
57 | assert.equal(ipcCalls[3], 'beta received');
58 | assert.equal(ipcCalls[4], ' received');
59 | assert.equal(ipcCalls[5], 'cell received');
60 | });
61 | });
62 | });
63 | });
64 | });
65 | });
66 |
67 | describe('app-main2win-reply-nested', function () {
68 | this.timeout(0);
69 | let app = null;
70 |
71 | before(function () {
72 | app = new Application({
73 | path: electron,
74 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply-nested')]
75 | });
76 | return app.start();
77 | });
78 |
79 | after(function () {
80 | if (app && app.isRunning()) {
81 | return app.stop();
82 | }
83 | });
84 |
85 | it('should be ok', function () {
86 | return app.client
87 | .windowByIndex(0)
88 | .waitUntilTextExists('.label', 'Ready')
89 | .getRenderProcessLogs()
90 | .then(function (logs) {
91 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
92 | assert.ok(logs[1].message.indexOf('app:hello cell') !== -1);
93 | assert.ok(logs[2].message.indexOf('app:hello-nested ') !== -1);
94 | assert.ok(logs[3].message.indexOf('app:hello-nested cell-nested') !== -1);
95 |
96 | return app.client
97 | .windowByIndex(1)
98 | .waitUntilTextExists('.label', 'Ready')
99 | .getRenderProcessLogs()
100 | .then(function (logs) {
101 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
102 | assert.ok(logs[1].message.indexOf('app:hello beta') !== -1);
103 | assert.ok(logs[2].message.indexOf('app:hello-nested ') !== -1);
104 | assert.ok(logs[3].message.indexOf('app:hello-nested beta-nested') !== -1);
105 |
106 | return app.client
107 | .windowByIndex(2)
108 | .waitUntilTextExists('.label', 'Ready')
109 | .getRenderProcessLogs()
110 | .then(function (logs) {
111 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
112 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
113 | assert.ok(logs[2].message.indexOf('app:hello-nested ') !== -1);
114 | assert.ok(logs[3].message.indexOf('app:hello-nested alpha-nested') !== -1);
115 |
116 | return app.electron.remote.getGlobal('ipcCalls')
117 | .then(function (ipcCalls) {
118 | assert.equal(ipcCalls.length, 12);
119 | assert.equal(ipcCalls[ 0], ' received');
120 | assert.equal(ipcCalls[ 1], 'alpha received');
121 | assert.equal(ipcCalls[ 2], ' received');
122 | assert.equal(ipcCalls[ 3], 'alpha-nested received');
123 | assert.equal(ipcCalls[ 4], ' received');
124 | assert.equal(ipcCalls[ 5], 'beta received');
125 | assert.equal(ipcCalls[ 6], ' received');
126 | assert.equal(ipcCalls[ 7], 'beta-nested received');
127 | assert.equal(ipcCalls[ 8], ' received');
128 | assert.equal(ipcCalls[ 9], 'cell received');
129 | assert.equal(ipcCalls[10], ' received');
130 | assert.equal(ipcCalls[11], 'cell-nested received');
131 | });
132 | });
133 | });
134 | });
135 | });
136 | });
137 |
138 | describe('app-main2win-reply-more-than-once', function () {
139 | this.timeout(0);
140 | let app = null;
141 |
142 | before(function () {
143 | app = new Application({
144 | path: electron,
145 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply-more-than-once')]
146 | });
147 | return app.start();
148 | });
149 |
150 | after(function () {
151 | if (app && app.isRunning()) {
152 | return app.stop();
153 | }
154 | });
155 |
156 | it('should be ok', function () {
157 | return app.client
158 | .windowByIndex(0)
159 | .waitUntilWindowLoaded()
160 | .getRenderProcessLogs()
161 | .then(function (logs) {
162 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
163 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
164 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
165 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
166 |
167 | return app.electron.remote.getGlobal('ipcCalls')
168 | .then(function (ipcCalls) {
169 | assert.equal(ipcCalls.length, 4);
170 | assert.equal(ipcCalls[0], ' received');
171 | assert.equal(ipcCalls[1], 'alpha received');
172 | assert.equal(ipcCalls[2], 'beta received');
173 | assert.equal(ipcCalls[3], 'cell received');
174 | });
175 | });
176 | });
177 | });
178 |
179 | describe('app-main2win-reply-timeout', function () {
180 | this.timeout(0);
181 | let app = null;
182 |
183 | before(function () {
184 | app = new Application({
185 | path: electron,
186 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply-timeout')]
187 | });
188 | return app.start();
189 | });
190 |
191 | after(function () {
192 | if (app && app.isRunning()) {
193 | return app.stop();
194 | }
195 | });
196 |
197 | it('should be ok', function () {
198 | return app.client
199 | .windowByIndex(0)
200 | .waitUntilTextExists('.label', 'Ready')
201 | .getRenderProcessLogs()
202 | .then(function (logs) {
203 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
204 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
205 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
206 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
207 |
208 | return app.electron.remote.getGlobal('ipcCalls')
209 | .then(function (ipcCalls) {
210 | assert.equal(ipcCalls.length, 4);
211 | assert.equal(ipcCalls[0], 'timeout');
212 | assert.equal(ipcCalls[1], 'timeout alpha');
213 | assert.equal(ipcCalls[2], 'timeout beta');
214 | assert.equal(ipcCalls[3], 'cell received');
215 | });
216 | });
217 | });
218 | });
219 |
220 | describe('app-main2win-reply-cancel-request', function () {
221 | this.timeout(0);
222 | let app = null;
223 |
224 | before(function () {
225 | app = new Application({
226 | path: electron,
227 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply-cancel-request')]
228 | });
229 | return app.start();
230 | });
231 |
232 | after(function () {
233 | if (app && app.isRunning()) {
234 | return app.stop();
235 | }
236 | });
237 |
238 | it('should be ok', function () {
239 | return app.client
240 | .windowByIndex(0)
241 | .waitUntilTextExists('.label', 'Ready')
242 | .getRenderProcessLogs()
243 | .then(function (logs) {
244 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
245 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
246 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
247 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
248 |
249 | return app.electron.remote.getGlobal('ipcCalls')
250 | .then(function (ipcCalls) {
251 | assert.equal(ipcCalls.length, 0);
252 | });
253 | });
254 | });
255 | });
256 |
257 | describe('app-main2win-reply-error-win-destroyed', function () {
258 | this.timeout(0);
259 | let app = null;
260 |
261 | before(function () {
262 | app = new Application({
263 | path: electron,
264 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply-error-win-destroyed')]
265 | });
266 | return app.start();
267 | });
268 |
269 | after(function () {
270 | if (app && app.isRunning()) {
271 | return app.stop();
272 | }
273 | });
274 |
275 | it('should be ok', function () {
276 | return app.client
277 | .windowByIndex(0) // NOTE: yes, should be index-0, not index-1
278 | .waitUntilTextExists('.label', 'Ready')
279 | .then(function () {
280 | return app.electron.remote.getGlobal('ipcCalls')
281 | .then(function (ipcCalls) {
282 | assert.equal(ipcCalls.length, 1);
283 | assert.equal(ipcCalls[0], 'EWINCLOSED, undefined');
284 | });
285 | });
286 | });
287 | });
288 |
289 | describe('app-main2win-reply-a-user-error', function () {
290 | this.timeout(0);
291 | let app = null;
292 |
293 | before(function () {
294 | app = new Application({
295 | path: electron,
296 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply-a-user-error')]
297 | });
298 | return app.start();
299 | });
300 |
301 | after(function () {
302 | if (app && app.isRunning()) {
303 | return app.stop();
304 | }
305 | });
306 |
307 | it('should be ok', function () {
308 | return app.client
309 | .windowByIndex(0)
310 | .waitUntilWindowLoaded()
311 | .getRenderProcessLogs()
312 | .then(function (logs) {
313 | assert.equal(logs.length, 4);
314 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
315 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
316 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
317 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
318 |
319 | return app.electron.remote.getGlobal('ipcCalls')
320 | .then(function (ipcCalls) {
321 | assert.equal(ipcCalls.length, 4);
322 | assert.equal(ipcCalls[0], 'Error: user, failed');
323 | assert.equal(ipcCalls[1], 'Error: user, alpha failed');
324 | assert.equal(ipcCalls[2], 'Error: user, beta failed');
325 | assert.equal(ipcCalls[3], 'Error: user, cell failed');
326 | });
327 | });
328 | });
329 | });
330 |
331 | describe('app-main2win-reply-error-invalid-first-arg', function () {
332 | this.timeout(0);
333 | let app = null;
334 |
335 | before(function () {
336 | app = new Application({
337 | path: electron,
338 | args: [path.join(__dirname, 'fixtures', 'app-main2win-reply-error-invalid-first-arg')]
339 | });
340 | return app.start();
341 | });
342 |
343 | after(function () {
344 | if (app && app.isRunning()) {
345 | return app.stop();
346 | }
347 | });
348 |
349 | it('should be ok', function () {
350 | return app.client
351 | .windowByIndex(0)
352 | .waitUntilWindowLoaded()
353 | .getRenderProcessLogs()
354 | .then(function (logs) {
355 | assert.equal(logs.length, 8);
356 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
357 | assert.ok(logs[1].message.indexOf('Invalid argument for event.reply') !== -1);
358 | assert.ok(logs[2].message.indexOf('app:hello alpha') !== -1);
359 | assert.ok(logs[3].message.indexOf('Invalid argument for event.reply') !== -1);
360 | assert.ok(logs[4].message.indexOf('app:hello beta') !== -1);
361 | assert.ok(logs[5].message.indexOf('Invalid argument for event.reply') !== -1);
362 | assert.ok(logs[6].message.indexOf('app:hello cell') !== -1);
363 | assert.ok(logs[7].message.indexOf('Invalid argument for event.reply') !== -1);
364 |
365 | return app.electron.remote.getGlobal('ipcCalls')
366 | .then(function (ipcCalls) {
367 | assert.equal(ipcCalls.length, 4);
368 | assert.equal(ipcCalls[0], 'EINVALIDARGS, received');
369 | assert.equal(ipcCalls[1], 'EINVALIDARGS, alpha received');
370 | assert.equal(ipcCalls[2], 'EINVALIDARGS, beta received');
371 | assert.equal(ipcCalls[3], 'EINVALIDARGS, cell received');
372 | });
373 | });
374 | });
375 | });
376 |
--------------------------------------------------------------------------------
/test/send-to-win.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const electron = require('electron');
5 | const {Application} = require('spectron');
6 | const assert = require('assert');
7 |
8 | describe('app-main2win', function () {
9 | this.timeout(0);
10 | let app = null;
11 |
12 | before(function () {
13 | app = new Application({
14 | path: electron,
15 | args: [path.join(__dirname, 'fixtures', 'app-main2win')]
16 | });
17 | return app.start();
18 | });
19 |
20 | after(function () {
21 | if (app && app.isRunning()) {
22 | return app.stop();
23 | }
24 | });
25 |
26 | it('should be ok', function () {
27 | return app.client
28 | .windowByIndex(0)
29 | .waitUntilWindowLoaded()
30 | .getRenderProcessLogs()
31 | .then(function (logs) {
32 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
33 | assert.ok(logs[1].message.indexOf('app:hello cell') !== -1);
34 |
35 | return app.client
36 | .windowByIndex(1)
37 | .waitUntilWindowLoaded()
38 | .getRenderProcessLogs()
39 | .then(function (logs) {
40 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
41 | assert.ok(logs[1].message.indexOf('app:hello beta') !== -1);
42 |
43 | return app.client
44 | .windowByIndex(2)
45 | .waitUntilWindowLoaded()
46 | .getRenderProcessLogs()
47 | .then(function (logs) {
48 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
49 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
50 |
51 | return app.electron.remote.getGlobal('ipcCalls')
52 | .then(function (ipcCalls) {
53 | assert.equal(ipcCalls.length, 0);
54 | });
55 | });
56 | });
57 | });
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/test/send-to-wins.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const electron = require('electron');
5 | const {Application} = require('spectron');
6 | const assert = require('assert');
7 |
8 | describe('app-main2wins', function () {
9 | this.timeout(0);
10 | let app = null;
11 |
12 | before(function () {
13 | app = new Application({
14 | path: electron,
15 | args: [path.join(__dirname, 'fixtures', 'app-main2wins')]
16 | });
17 | return app.start();
18 | });
19 |
20 | after(function () {
21 | if (app && app.isRunning()) {
22 | return app.stop();
23 | }
24 | });
25 |
26 | it('should be ok', function () {
27 | return app.client
28 | .windowByIndex(0)
29 | .waitUntilWindowLoaded()
30 | .getRenderProcessLogs()
31 | .then(function (logs) {
32 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
33 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
34 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
35 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
36 |
37 | return app.client
38 | .windowByIndex(1)
39 | .waitUntilWindowLoaded()
40 | .getRenderProcessLogs()
41 | .then(function (logs) {
42 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
43 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
44 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
45 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
46 |
47 | return app.client
48 | .windowByIndex(2)
49 | .waitUntilWindowLoaded()
50 | .getRenderProcessLogs()
51 | .then(function (logs) {
52 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
53 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
54 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
55 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
56 |
57 | return app.electron.remote.getGlobal('ipcCalls')
58 | .then(function (ipcCalls) {
59 | assert.equal(ipcCalls.length, 0);
60 | });
61 | });
62 | });
63 | });
64 | });
65 | });
66 |
67 | describe('app-win2wins', function () {
68 | this.timeout(0);
69 | let app = null;
70 |
71 | before(function () {
72 | app = new Application({
73 | path: electron,
74 | args: [path.join(__dirname, 'fixtures', 'app-win2wins')]
75 | });
76 | return app.start();
77 | });
78 |
79 | after(function () {
80 | if (app && app.isRunning()) {
81 | return app.stop();
82 | }
83 | });
84 |
85 | it('should be ok', function () {
86 | return app.client
87 | .windowByIndex(0)
88 | .waitUntilWindowLoaded()
89 | .getRenderProcessLogs()
90 | .then(function (logs) {
91 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
92 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
93 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
94 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
95 |
96 | return app.client
97 | .windowByIndex(1)
98 | .waitUntilWindowLoaded()
99 | .getRenderProcessLogs()
100 | .then(function (logs) {
101 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
102 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
103 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
104 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
105 |
106 | return app.client
107 | .windowByIndex(2)
108 | .waitUntilWindowLoaded()
109 | .getRenderProcessLogs()
110 | .then(function (logs) {
111 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
112 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
113 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
114 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
115 |
116 | return app.electron.remote.getGlobal('ipcCalls')
117 | .then(function (ipcCalls) {
118 | assert.equal(ipcCalls.length, 0);
119 | });
120 | });
121 | });
122 | });
123 | });
124 | });
125 |
126 |
127 | describe('app-win2wins-exclude-self', function () {
128 | this.timeout(0);
129 | let app = null;
130 |
131 | before(function () {
132 | app = new Application({
133 | path: electron,
134 | args: [path.join(__dirname, 'fixtures', 'app-win2wins-exclude-self')]
135 | });
136 | return app.start();
137 | });
138 |
139 | after(function () {
140 | if (app && app.isRunning()) {
141 | return app.stop();
142 | }
143 | });
144 |
145 | it('should be ok', function () {
146 | return app.client
147 | .windowByIndex(0)
148 | .waitUntilWindowLoaded()
149 | .getRenderProcessLogs()
150 | .then(function (logs) {
151 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
152 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
153 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
154 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
155 |
156 | return app.client
157 | .windowByIndex(1)
158 | .waitUntilWindowLoaded()
159 | .getRenderProcessLogs()
160 | .then(function (logs) {
161 | assert.ok(logs[0].message.indexOf('app:hello ') !== -1);
162 | assert.ok(logs[1].message.indexOf('app:hello alpha') !== -1);
163 | assert.ok(logs[2].message.indexOf('app:hello beta') !== -1);
164 | assert.ok(logs[3].message.indexOf('app:hello cell') !== -1);
165 |
166 | return app.client
167 | .windowByIndex(2)
168 | .waitUntilWindowLoaded()
169 | .getRenderProcessLogs()
170 | .then(function (logs) {
171 | assert.equal(logs.length, 0);
172 |
173 | return app.electron.remote.getGlobal('ipcCalls')
174 | .then(function (ipcCalls) {
175 | assert.equal(ipcCalls.length, 0);
176 | });
177 | });
178 | });
179 | });
180 | });
181 | });
182 |
--------------------------------------------------------------------------------