test: 💍 add IPCRequest and service tests

This commit is contained in:
Randall Morey committed 2020-11-09 18:18:15 -05:00
1 parent 32f986afcf
commit 66aeb805f0
4 files changed
+1127 -846

No files matched your search

+28 -10
View File
@@ -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);
}
}
+2 -1
View File
@@ -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"
+49 -4
View File
@@ -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
});
});
});
+1048 -831
View File
File diff suppressed because it is too large. Load diff