Closed trusktr closed 6 years ago
I've been able to get this all working with karma-webpack
, but using karma-webpack
has the downside that it compiles a bundle of every single test file in the project which is sloooooooooooooooow, and complete defeats the purpose of concurrent tests. I may as well just compile a single test bundle myself and feed that to Karma, which is what I'm thinking of doing if I can't get karma-electron
to work.
I'm running electron with xvfb-maybe
so that I can run it headless.
That initial config does look correct. We dealt with a similar multi-layer compilation recently in https://github.com/twolfson/karma-electron/issues/30 but it looks like this config is fine as is
Is there a test repo you can point me to to reproduce? (minimal size would be ideal)
Maybe what's causing issues is that my configs and scripts are outside of my project? Here's a gist of what it looks like:
shared_build_stuff
config
karma.config.js
node_modules
my_project
tests/...
node_modules
where shared_build_stuff
is npm link
ed into my_project
.
Here's how to reproduce:
❯ git clone -b karma-electron-issue-32 git@github.com:trusktr/builder-js-package.git
❯ git clone -b karma-electron-issue-32 git@github.com:trusktr/infamous.git
❯ cd builder-js-package
❯ npm i && npm link # might need sudo
❯ cd ../infamous
❯ npm i && npm link builder-js-package
❯ ELECTRON_BIN=./node_modules/.bin/electron ./node_modules/.bin/xvfb-maybe ./node_modules/.bin/karma start --single-run --browsers Electron ./node_modules/builder-js-package/config/karma.config.js
That should get you the error
Electron 1.8.6 (Node 8.2.1) ERROR
{
"message": "Uncaught SyntaxError: Unexpected token import\nat /home/trusktr/Downloads/src/trusktr+infamous/src/core/Mixin.test.js:1:156\n\nundefined",
"str": "Uncaught SyntaxError: Unexpected token import\nat /home/trusktr/Downloads/src/trusktr+infamous/src/core/Mixin.test.js:1:156\n\nundefined"
}
Inside of the linked ./node_modules/builder-js-package/config/karma.config.js
config, you'll see
preprocessors: {
'@(src|test)/**/*.js': ['babel', 'electron'],
//'@(src|test)/**/*.js': ['babel'],
},
The first one, which includes 'electron'
, seems to cause the babel preprocessor not to work, because it results in import
statements causing syntax errors.
If you use the second line instead, the import statement errors go away, but then it seems that require
is not able to find modules, which give you a different error:
Electron 1.8.6 (Node 8.2.1) ERROR
{
"message": "Uncaught Error: Cannot find module './Mixin'\nat module.js:487:5\n\nundefined",
"str": "Uncaught Error: Cannot find module './Mixin'\nat module.js:487:5\n\nundefined"
}
Electron 1.8.6 (Node 8.2.1): Executed 0 of 0 ERROR (0.24 secs / 0 secs)
If you have a desktop so that you can see the electron window, you don't need the xvfb-maybe
part of the command for running electron headless, so:
❯ ELECTRON_BIN=./node_modules/.bin/electron ./node_modules/.bin/karma start --single-run --browsers Electron ./node_modules/builder-js-package/config/karma.config.js
There's only a couple .test.js
files inside infamous/src/core
, which is where the import
syntax errors happen when electron
preprocessor is enabled.
Great, thanks. I'll try to take a shot at reproducing by the end of the week
Awesome! Thanks for being so responsive!
For now, I'm using karma-webpack
with a single test entry point, and running it in Chrome headless with karma-chrome-launcher
. The electron approach is cleaner and more performant, without sacrificing features that are lost when using a single test entry point.
Going to take a shot at reproducing this now
Going to side-step the npm link
by updating the git
reference in infamous' package.json
to use the karma-electron-issue-32
branch
Okay, I understand what's going on now. We're using babel-karma
to transpile files. This is normally for transpiling hosted files -- not transpiling files that are loaded via require
/similar. As a result, once we require
our first file, it's no transpiling the second file
I believe a solution to this would be to use babel-node
or whatever the Electron equivalent is. Going to try out a couple things and report back
One more option is to avoid needing Babel and sticking to what the platform supports directly but you might be too indebted to turn around at this point =/
I'm having trouble wrangling this repo but I'm pretty sure we want to do the following changes:
babel-karma
from build chain and keep on using loadScriptsViaRequire
(this will enable us to use babel-runtime
for all files instead of one-off replacementsfiles
which loads babel-runtime
.babelrc
/similar properly configured with babel-runtime
(this is what I was struggling with)I'm going to try to get a quick and dirty example running in karma-electron
that handles the import
syntax
Yep, got it running in this repo. The steps I enumerated in the last comment will get it working. Here's the diff of changes I had to do in karma-electron
to verify it works:
https://github.com/twolfson/karma-electron/compare/7422c3900ef5bc5d37b8800e13978c4284300225
Awesome, thanks for looking at it! I'd tried babel-register, but I didn't see how to get it to have my own Babel config rather than it looking for babelrc. The problem is, I don't want to to load all babelrc files that it may find inside node_modules. I'll let you know how it works after I give it a shot this weekend.
I'm pretty sure that Babel only transpiles for the local directory unless you tell it explicitly to do a global search. Otherwise, that would make everything very very slow ._.
Their docs seem to agree with me -- node_modules
are ignored by default
https://babeljs.io/docs/usage/babel-register/#ignores-node_modules-by-default
I gave it a shot, but it says it can not find the @babel/register
module. My config now looks like this now:
const CWD = process.cwd()
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
reporters: ['spec'],
port: 9876, // karma web server port
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
concurrency: Infinity,
basePath: CWD,
browsers: ['Electron'],
files: [
'node_modules/builder-js-package/babel-register.js',
{ pattern: 'src/**/!(*.test).js', included: false },
'src/**/*.test.js',
'tests/**/*.js',
],
preprocessors: {
'@(src|test)/**/*.js': ['electron'],
},
client: {
// otherwise "require is not defined"
useIframe: false,
loadScriptsViaRequire: true,
},
})
}
where babel-register.js
looks like
require('@babel/register')({
presets: [
['@babel/preset-env', {
targets: {
node: 6,
},
}],
],
})
and when I try to run it:
❯ ELECTRON_BIN=./node_modules/.bin/electron ./node_modules/.bin/xvfb-maybe ./node_modules/.bin/karma start --single-run --browsers Electron ./node_modules/builder-js-package/config/karma.config.js
05 05 2018 00:23:47.119:INFO [karma]: Karma v2.0.2 server started at http://0.0.0.0:9876/
05 05 2018 00:23:47.122:INFO [launcher]: Launching browser Electron with unlimited concurrency
05 05 2018 00:23:47.129:INFO [launcher]: Starting browser Electron
05 05 2018 00:23:50.051:INFO [Electron 2.0.0 (Node 8.9.3)]: Connected on socket kNhMblLY4L_4katwAAAA with id 94279078
Electron 2.0.0 (Node 8.9.3) ERROR
{
"message": "Uncaught Error: Cannot find module '@babel/register'\nat module.js:545:5\n\nundefined",
"str": "Uncaught Error: Cannot find module '@babel/register'\nat module.js:545:5\n\nundefined"
}
Electron 2.0.0 (Node 8.9.3): Executed 0 of 0 ERROR (0.262 secs / 0 secs)
Failed with exit code: 1
Output:
It seems, no matter what I try to require()
inside my babel-register.js file, it can not be found.
Just thinking out loud: if I console.log(__dirname)
in my babel-register.js file, I see /home/trusktr/Downloads/src/trusktr+lowclass/node_modules/electron/dist/resources/electron.asar/renderer
which is not the babel-register.js file. Maybe that's causing my issue. hmmmm...
Hmmmmm, changing the import line to
require(process.cwd() + '/node_modules/@babel/register')({
works, but then I get a bunch of syntax errors on the import
statements of all my test files again. Hmmmm....
We need to preprocess node_modules/builder-js-package/babel-register.js
via electron
(our karma-electron
wrapper) for it to get the proper filepath/require path bindings. Although, I guess that process.cwd()
is a nice trick to get it to work for now
preprocessors: {
'node_modules/builder-js-package/babel-register.js': ['electron'],
'@(src|test)/**/*.js': ['electron'],
},
I'm not sure what babel-register
is doing when it crosses between node_modules/builder-js-package
and process.cwd()
. I suggest simplifying the repo (e.g. colocating files, reducing amount of files) or starting with a proof of concept (e.g. copy/paste core contents and get import
working like in the karma-electron
branch). We do know that this will work though via the work here:
https://github.com/twolfson/karma-electron/compare/7422c3900ef5bc5d37b8800e13978c4284300225
That indeed fixes the import path problem. Thanks!
I noticed in your example you're require
ing the to-be-transpiled module from the same file as where you required babel-register, but in my case I am not doing that, I'm only listing the entry points in the tests
option. Maybe that's the difference, let me see...
Ah, yep that's exactly it! So if I change my config to
browsers: ['Electron'],
files: [
'node_modules/builder-js-package/babel-register.js',
],
preprocessors: {
'node_modules/builder-js-package/babel-register.js': ['electron'],
},
client: {
useIframe: false,
loadScriptsViaRequire: true,
},
then have node_modules/builder-js-package/babel-register.js
import one of the test files, it works great:
require('@babel/register')({
presets: [ ['@babel/preset-env', { targets: { node: 6 } }] ],
})
require(process.cwd() + '/tests/basics.test.js') // this works, like in your example.
So the question is, how do I force the babel-register import to happen for each test entry point listed in files
?
Alright, I got it to work with all my test files using the following hack, but it doesn't seem ideal:
const CWD = process.cwd()
const glob = require('globby')
const fs = require('fs')
/*
* Generate an entry point that imports all test files:
*/
let testFiles = null
glob([
CWD+'/src/**/*.test.js',
CWD+'/tests/**/*.js',
]).then(paths => testFiles = paths)
// wait for glob call to finish
while (!testFiles) {
require('deasync').sleep(100)
}
testFiles = testFiles.map(file => {
return `require('${ file }')`
}).join('\n')
fs.writeFileSync(CWD+'/.test-entry.js', testFiles)
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
reporters: ['spec'],
port: 9876, // karma web server port
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
concurrency: Infinity,
basePath: CWD,
/*
* karma-electron + babel-register
*/
browsers: ['Electron'],
files: [
'node_modules/builder-js-package/babel-register.js',
],
preprocessors: {
'node_modules/builder-js-package/babel-register.js': ['electron'],
},
client: {
useIframe: false,
loadScriptsViaRequire: true,
},
})
}
where node_modules/builder-js-package/babel-register.js
has
require('@babel/register')({
presets: [ ['@babel/preset-env', { targets: { node: 6 } }] ],
})
require(process.cwd() + '/.test-entry.js')
If I do it this way, there's a single entry point in the same file that import babel-register, similar to your example.
Do I lose out on features like concurrent tests if I do it this way? I was hoping to list my entry points in the files
option and gain concurrency (or, at least allowed for this possibility if it isn't the case yet). Having a single entry point negates the possibility, and also negates other features like running specific tests (this always runs all tests).
I'm going to roll with this for now, because at least it is much cleaner/faster than bundling with Webpack or Browserify. I still wonder if it is possible to use files
instead of my generated entry point.
I wonder if there's some way to plug into Electron's (/Node's) require
to make it always import certain files without my files having to explicitly require
them?
Plus, with the single-entry-point approach, now the tests are failing because they change the same global state (f.e. customElements.define calls try to register the same element name, which fails).
Maybe I can do another even uglier hack: generate a folder of entry points, where each entry point imports babel-register
and a corresponding test file! Yaaaaaaaaaaas, that will get me to where I need to beeeeeeeee. 🤣
Ah, that is quite clarifying. Glad to hear you got it working =)
To answer your question about concurrency, Karma doesn't really care about concurrency -- it will run entire test suite at a time. It's the job of the test runner (e.g. Mocha, Jasmine, tape) to handle concurrency. There's often plugins for this or separate testers which are parallel out of the box (e.g. ava, vows)
Here's a handful of alternatives for loading the babel-register
call:
glob
+ require
calls inside of a single file that is loaded by karma.files
glob
has a glob.sync
so no need for the deasync
callcompiled/
) and load those inside of karma (e.g. files: ['compiled/test/**/*.js']
)
babel-register
to reparse files when using its require
on every test run since it sounds like the require
cache gets blown awayBrowserWindow
options, then use Electron's preload
support to preload the babel-register
callsWait for #31 to turn around and add generic BrowserWindow options, then use Electron's preload support to preload the babel-register calls
That's sounds like the best solution. Would it preload the file in every child window that karma opens?
Nvm, I see, for each new-window
we'd pass the option in.
I have this config (relevant parts):
but when I run karma, I get an error on files that have
import
syntax:But, if I remove
electron
from preprocessors, so that I only havebabel
,then that syntax error goes away which makes me think now Babel is working, but then I get these errors:
So, it seems that when I add
electron
to preprocessors,babel
stops working. But when I don't haveelectron
, then it doesn't know how to resolve the modules.Any ideas how to make it work with karma-babel?