test: 💍 add IPCRequest and service tests
This commit is contained in:
4 files changed
+1127
-846
No files matched your search
@@ -3,16 +3,33 @@ import { getOwner } from '@ember/application';
|
||||
import { Promise } from 'rsvp';
|
||||
|
||||
/**
|
||||
* An IPC request is a promise-like object that invokes a method on the main
|
||||
* process and resolves to its response.
|
||||
* An IPC request is a promise-like object that issues a request via
|
||||
* `window.postMessage` and resolves to a response received via a response
|
||||
* port. IPC requests are used by an Electron renderer process to communicate
|
||||
* with the main process when the renderer is fully isolated.
|
||||
*
|
||||
* Some of the design goals of this "safe IPC" are to respect the confines
|
||||
* of process isolation and security, without using node integration, while
|
||||
* retaining the ability to run the Ember application within a browser
|
||||
* (sans main process) by using only APIs available in a standard
|
||||
* (non-Electron) browser. This is achieved by posting messages to the
|
||||
* renderer's `window.postMessage` interface. Thus, the renderer requires no
|
||||
* knowledge of the main process or the Electron context. In fact a main
|
||||
* process and Electron context are not even required.
|
||||
*
|
||||
* In an Electron context, the `preload.js` script sets up a `window.postMessge`
|
||||
* listener which acts as proxy layer between the main and renderer processes.
|
||||
* It waits for incoming requests from the renderer and forwards them to main.
|
||||
* Responses from the main process are forwarded back via a response port
|
||||
* (sent with the original request).
|
||||
*
|
||||
* Local development in a non-Electron browser context or test context should
|
||||
* mock up IPC requests. See the ipc service test for an example of mocking.
|
||||
*
|
||||
* TODO: add utilities for quickly mocking IPC responses (like Mirage)
|
||||
*
|
||||
* Since the renderer cannot call into main or access Node modules directly,
|
||||
* it uses the window's `postMessage` interface. Messages posted in this manner
|
||||
* are proxied by `preload.js` to the main process. This ensures that main
|
||||
* and renderer remain isolated and frontend code does not rely on the presence
|
||||
* of a main process.
|
||||
*/
|
||||
class IPCRequest {
|
||||
export class IPCRequest {
|
||||
|
||||
// =attributes
|
||||
|
||||
@@ -122,10 +139,11 @@ export default class IpcService extends Service {
|
||||
* Creates a new IPC request and returns it.
|
||||
* @param {string} method
|
||||
* @param {object} payload
|
||||
* @param {object} origin
|
||||
* @return {IPCRequest}
|
||||
*/
|
||||
invoke(method, payload) {
|
||||
return new IPCRequest(method, payload, this.window);
|
||||
invoke(method, payload, origin) {
|
||||
return new IPCRequest(method, payload, this.window, origin);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,7 +74,8 @@
|
||||
"prettier": "^2.1.2",
|
||||
"qunit-dom": "^1.2.0",
|
||||
"rose": "*",
|
||||
"sass": "^1.29.0"
|
||||
"sass": "^1.29.0",
|
||||
"sinon": "^9.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "10.* || >= 12"
|
||||
|
||||
@@ -1,12 +1,57 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { IPCRequest } from 'desktop/services/ipc';
|
||||
import sinon from 'sinon';
|
||||
|
||||
module('Unit | Service | ipc', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let service = this.owner.lookup('service:ipc');
|
||||
assert.ok(service);
|
||||
hooks.beforeEach(() => {
|
||||
// IPC requests talk to the main process indirectly via window.postMessage
|
||||
// proxy layer (mediated by preload.js).
|
||||
// But we don't want to send these for real in tests.
|
||||
window.postMessage = sinon.fake();
|
||||
});
|
||||
|
||||
hooks.afterEach(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
test('it creates new IPCRequest objects', function(assert) {
|
||||
assert.expect(1);
|
||||
const service = this.owner.lookup('service:ipc');
|
||||
const request = service.invoke('hello', 'world', 'http://localhost:4200');
|
||||
assert.equal(request.constructor, IPCRequest);
|
||||
});
|
||||
|
||||
test('IPCRequest objects post messages on their window objects', function(assert) {
|
||||
assert.expect(2);
|
||||
const myWindow = {
|
||||
postMessage(request) {
|
||||
assert.equal(request.method, 'hello');
|
||||
assert.equal(request.payload, 'world');
|
||||
}
|
||||
};
|
||||
const request = new IPCRequest('hello', 'world', myWindow);
|
||||
});
|
||||
|
||||
test('IPCRequest objects resolve to the value posted via a response message port', async function(assert) {
|
||||
assert.expect(1);
|
||||
const myWindow = {
|
||||
postMessage(request, origin, [ port ]) {
|
||||
// Post back a message on the response port
|
||||
port.postMessage({
|
||||
foo: 'bar',
|
||||
test: 42
|
||||
});
|
||||
}
|
||||
};
|
||||
const request = new IPCRequest('hello', 'world', myWindow);
|
||||
const response = await request;
|
||||
assert.deepEqual(response, {
|
||||
foo: 'bar',
|
||||
test: 42
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
Reference in new issue
Block a user