Skip to content

Commit b5ee735

Browse files
bendemboskiBen Demboski
authored andcommitted
Simplify test setup
In ember-electron 2.x, the `electron:test` command would build the Ember app into a temp location somewhere, so we'd have to jump through a bunch of hoops to pass that location to the Electron app so it could load the Ember app into the browser window. In 3.x, we changed it so that the Ember app is always built into `electron-app/ember-test`, so we don't need to pass this extra information around at runtime. So we're now simplifying and better organizing the test infrastructure: * Remove the code that handles passing the Ember app location through to the test main process * Put the `electron-protocol-serve` setup code into a blueprint-generated utility function. This way the production and test versions of the app use the same code, and it's easier to see the correspondence between the two different usages * Reorganize the test support code a bit for better flexibility -- rather than the test `index.js` just requiring a single file that runs all the test main process code, it imports some functions and calls them. This makes it a bit easier to read and understand, makes it easier to share code between the production and test main processes, and also allows more flexibility for users to customize, since the functionality that `ember-electron` supplies isn't all-or-nothing * await the Ember Inspector installation before opening the browser window, to make sure we don't have a race condition. This should fix the intermittent failures we've seen in the test that verifies that we install Ember Inspector. * Make end-to-end test more robust -- in this test we paste some code into the test `index.js` to verify some functionality, so rather than putting it at module scope, we put it in a function that we call so we don't have to worry about naming conflicts/duplicate imports between the blueprint-generated code and the test code we paste into the same file
1 parent 339d94f commit b5ee735

12 files changed

Lines changed: 189 additions & 177 deletions

File tree

.eslintrc.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,29 @@ module.exports = {
6363
'ember/avoid-leaking-state-in-ember-objects': 'off',
6464
})
6565
},
66-
// forge template files
66+
// test runner
6767
{
6868
files: [
69-
'forge/files/**/*.js'
69+
'lib/test-runner.js'
70+
],
71+
env: {
72+
browser: false,
73+
node: true
74+
},
75+
plugins: ['node'],
76+
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
77+
'node/no-missing-require': ['error', {
78+
'allowModules': [
79+
'ember-electron'
80+
],
81+
}]
82+
})
83+
},
84+
// Electon runtime files
85+
{
86+
files: [
87+
'forge/files/**/*.js',
88+
'lib/test-support/**/*.js'
7089
],
7190
env: {
7291
browser: false,

blueprints/ember-electron/files/testem-electron.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module.exports = {
22
test_page: 'tests/index.html?hidepassed',
33
disable_watching: true,
44
launchers: {
5-
Electron: require('ember-electron/lib/test-support/test-runner'),
5+
Electron: require('ember-electron/lib/test-runner'),
66
},
77
launch_in_ci: [
88
'Electron',

forge/files/src/index.js

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,16 @@
11
/* eslint-disable no-console */
2-
const { app, BrowserWindow, protocol } = require('electron');
3-
const { dirname, join, resolve } = require('path');
4-
const protocolServe = require('electron-protocol-serve');
5-
const isDev = require('electron-is-dev');
62
const { default: installExtension, EMBER_INSPECTOR } = require('electron-devtools-installer');
3+
const { app, BrowserWindow } = require('electron');
4+
const path = require('path');
5+
const isDev = require('electron-is-dev');
6+
const setupServeProtocol = require('./setup-serve-protocol');
7+
8+
const emberAppDir = path.resolve(__dirname, '..', 'ember-dist');
9+
const emberAppURL = 'serve://dist';
710

811
let mainWindow = null;
912

10-
// Registering a protocol & schema to serve our Ember application
11-
if (typeof protocol.registerSchemesAsPrivileged === 'function') {
12-
// Available in Electron >= 5
13-
protocol.registerSchemesAsPrivileged([{
14-
scheme: 'serve',
15-
privileges: {
16-
secure: true,
17-
standard: true
18-
}
19-
}]);
20-
}
21-
else {
22-
// For compatibility with Electron < 5
23-
protocol.registerStandardSchemes(['serve'], { secure: true });
24-
}
25-
protocolServe({
26-
cwd: join(__dirname || resolve(dirname('')), '..', 'ember-dist'),
27-
app,
28-
protocol,
29-
});
13+
setupServeProtocol(emberAppDir);
3014

3115
// Uncomment the lines below to enable Electron's crash reporter
3216
// For more information, see http://electron.atom.io/docs/api/crash-reporter/
@@ -43,11 +27,18 @@ app.on('window-all-closed', () => {
4327
}
4428
});
4529

46-
app.on('ready', () => {
30+
app.on('ready', async () => {
4731
if (isDev) {
48-
require('devtron').install();
49-
installExtension(EMBER_INSPECTOR)
50-
.catch((err) => console.log('An error occurred: ', err));
32+
try {
33+
require('devtron').install();
34+
} catch (err) {
35+
console.log('Failed to install Devtrom: ', err);
36+
}
37+
try {
38+
await installExtension(EMBER_INSPECTOR);
39+
} catch (err) {
40+
console.log('Failed to install Ember Inspector: ', err)
41+
}
5142
}
5243

5344
mainWindow = new BrowserWindow({
@@ -58,15 +49,13 @@ app.on('ready', () => {
5849
// If you want to open up dev tools programmatically, call
5950
// mainWindow.openDevTools();
6051

61-
const emberAppLocation = 'serve://dist';
62-
63-
// Load the ember application using our custom protocol/scheme
64-
mainWindow.loadURL(emberAppLocation);
52+
// Load the ember application
53+
mainWindow.loadURL(emberAppURL);
6554

6655
// If a loading operation goes wrong, we'll send Electron back to
6756
// Ember App entry point
6857
mainWindow.webContents.on('did-fail-load', () => {
69-
mainWindow.loadURL(emberAppLocation);
58+
mainWindow.loadURL(emberAppURL);
7059
});
7160

7261
mainWindow.webContents.on('crashed', () => {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const { app, protocol } = require('electron');
2+
const protocolServe = require('electron-protocol-serve');
3+
const path = require('path');
4+
5+
module.exports = function setupServeProtocol(emberAppDir) {
6+
if (typeof protocol.registerSchemesAsPrivileged === 'function') {
7+
protocol.registerSchemesAsPrivileged([{
8+
scheme: 'serve',
9+
privileges: {
10+
secure: true,
11+
standard: true,
12+
},
13+
}]);
14+
}
15+
else {
16+
protocol.registerStandardSchemes(['serve'], { secure: true });
17+
}
18+
protocolServe({
19+
cwd: emberAppDir,
20+
app,
21+
protocol,
22+
});
23+
24+
process.env.ELECTRON_PROTOCOL_SERVE_INDEX = path.join(emberAppDir, 'index.html');
25+
}

forge/files/tests/index.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
const { default: installExtension, EMBER_INSPECTOR } = require('electron-devtools-installer');
2+
const path = require('path');
3+
const { app } = require('electron');
4+
const setupServeProtocol = require('../src/setup-serve-protocol');
5+
const { setupTestem, openTestWindow } = require('ember-electron/lib/test-support');
26

3-
require('electron').app.on('ready', function onReady() {
4-
require('devtron').install();
5-
installExtension(EMBER_INSPECTOR)
6-
.catch((err) => console.log('An error occurred: ', err));
7+
const emberAppDir = path.resolve(__dirname, '..', 'ember-test');
8+
9+
setupServeProtocol(emberAppDir);
10+
11+
app.on('ready', async function onReady() {
12+
try {
13+
require('devtron').install();
14+
} catch (err) {
15+
console.log('Failed to install Devtrom: ', err);
16+
}
17+
try {
18+
await installExtension(EMBER_INSPECTOR);
19+
} catch (err) {
20+
console.log('Failed to install Ember Inspector: ', err)
21+
}
22+
23+
setupTestem();
24+
openTestWindow();
725
});
826

9-
require('ember-electron/lib/test-support/test-index');
27+
app.on('window-all-closed', function onWindowAllClosed() {
28+
if (process.platform !== 'darwin') {
29+
app.quit();
30+
}
31+
});
Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// // testem.js
66
// module.exports = {
77
// "launchers": {
8-
// "Electron": require("ember-electron/lib/test-support/test-runner")
8+
// "Electron": require("ember-electron/lib/test-runner")
99
// },
1010
// "launch_in_ci": [
1111
// "Electron"
@@ -21,50 +21,30 @@
2121

2222
module.exports = {
2323
'exe': 'node',
24-
'args': [__filename, '<cwd>', '<baseUrl>', '<testPage>', '<id>'],
24+
// These arguments are used in `lib/test-support/index.js`, which is called
25+
// from the test main process (via the blueprint-generated
26+
// `electron-app/tests/index.js`)
27+
'args': [__filename, '<testPage>', '<baseUrl>', '<id>'],
2528
'protocol': 'browser',
2629
};
2730

2831
async function main() {
2932
const path = require('path');
30-
const url = require('url');
31-
const fileUrl = require('file-url');
3233
const treeKill = require('tree-kill');
3334
const { api } = require('ember-electron/lib/utils/forge-core');
3435
const { electronProjectPath } = require('ember-electron/lib/utils/build-paths');
3536

36-
let [, , emberAppDir, testemUrl, testPageUrl, id] = process.argv;
37-
38-
// The testPageUrl points to the testem server, so we need to turn it into a
39-
// file URL and add the testem ID to the query params.
40-
let {
41-
pathname: testPagePath,
42-
query: testPageQuery,
43-
} = url.parse(testPageUrl, true);
44-
let indexPath = path.resolve(emberAppDir, path.join.apply(null, testPagePath.split('/')));
45-
let indexObj = url.parse(fileUrl(indexPath));
46-
indexObj.query = testPageQuery;
47-
48-
// https://github.com/nodejs/node/issues/9500
49-
for (let [ key, value ] of Object.entries(indexObj.query)) {
50-
if ([ null, undefined, '' ].includes(value)) {
51-
indexObj.query[key] = 'true';
52-
}
53-
}
54-
55-
// Set this so the script in shim-test-head.js can expose it to testem
56-
indexObj.query.testemId = id;
57-
58-
let testUrl = url.format(indexObj);
37+
let [, , testPageUrl, testemUrl, testemId] = process.argv;
5938

6039
// Start electron
6140
let { pid } = await api.start({
6241
dir: path.join(process.cwd(), electronProjectPath),
6342
appPath: './tests',
6443
args: [
6544
'--', // needed because https://github.com/electron/electron/pull/13039
66-
testUrl,
45+
testPageUrl,
6746
testemUrl,
47+
testemId
6848
],
6949
});
7050
// Clean up when we're killed

lib/test-support/index.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const { session, BrowserWindow } = require('electron');
2+
const { URL } = require('url');
3+
4+
// These are the command-line arguments passed to us by test-runner.js
5+
const [ , , , testPageURL, testemUrl, testemId ] = process.argv;
6+
7+
// Set up communication with the testem server
8+
//
9+
// We broccoli'ed the src attribute in the script tag that loads testem.js to be
10+
// http://testemserver/testem.js, so it and every resource it loads will have a
11+
// base URL of http://testemserver. The test runner passes us the URL to the
12+
// actual testem server, so we can intercept any requests to http://testemserver
13+
// and redirect them to the actual testem server.
14+
function setupTestem() {
15+
let { host: testemHost } = new URL(testemUrl);
16+
17+
session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
18+
let urlObj = new URL(details.url);
19+
let { hostname } = urlObj;
20+
21+
if (hostname === 'testemserver') {
22+
urlObj.host = testemHost;
23+
callback({ redirectURL: urlObj.toString() });
24+
} else {
25+
callback({});
26+
}
27+
});
28+
}
29+
30+
// Open the test window
31+
function openTestWindow() {
32+
let window = new BrowserWindow({
33+
width: 800,
34+
height: 600,
35+
webPreferences: {
36+
backgroundThrottling: false,
37+
nodeIntegration: true
38+
}
39+
});
40+
41+
delete window.module;
42+
43+
// Combine the test page URL with our root serve://dist URL
44+
let url = new URL(testPageURL, 'serve://dist');
45+
46+
// We need to set this query param so the script in shim-test-head.js can
47+
// expose it to testem to use when communicating with the testem server
48+
url.searchParams.set('testemId', testemId);
49+
50+
// https://github.com/nodejs/node/issues/9500
51+
for (let [ key, value ] of url.searchParams.entries()) {
52+
if ([ null, undefined, '' ].includes(value)) {
53+
url.searchParams.set(key, 'true');
54+
}
55+
}
56+
57+
window.loadURL(url.toString());
58+
}
59+
60+
module.exports = {
61+
setupTestem,
62+
openTestWindow
63+
}

0 commit comments

Comments
 (0)