My electron app works as expected but it keeps on opening new windows when I run spectron test that tests for number of windows opened.
Electron version - v8.0.2 , "spectron": "^10.0.1" . I am not sure how to check exact version of spectron.
I am just running a small demo, below I will give code snippet
Note: I am running the spectron test pointing to .exe file generated by electron-packager.
Does Anyone have an idea what is the issue if possible how to solve it?
main.js
const { app, BrowserWindow } = require('electron')
function createWindow () {
let win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
win.loadFile('index.html')
}
app.whenReady().then(createWindow)
index.js
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
test.js
const assert = require('assert');
const path = require('path');
const Application = require('spectron').Application;
const electronPath = require('electron');
const app = new Application({
path: 'C:/demoElectronApp/winx64/demoelectronapp-win32-x64/demoelectronapp.exe',
});
describe('client_settings_app', function () {
this.timeout(10000);
beforeEach(() => {
return app.start();
});
it('shows only one initial window', async () => {
const count = await app.client.getWindowCount();
return assert.equal(count, 1);
});
})
package.json
{
"name": "demoelectronapp",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"electronpackage": "electron-packager . --out winx64"
},
"author": "",
"license": "ISC",
"dependencies": {
"path": "^0.12.7"
},
"devDependencies": {
"electron": "^8.2.2",
"electron-packager": "^14.2.1",
"assert": "^2.0.0",
"mocha": "^7.1.1",
"spectron": "^10.0.1"
}
}
Okay, I was having the same issue. And solved by setting remote debugging port.
chromeDriverArgs: ['remote-debugging-port=9222']
Full test code:
const Application = require('spectron').Application
const assert = require('assert')
const electronPath = require('electron') // Require Electron from the binaries included in node_modules.
const path = require('path')
describe('Application launch', function () {
this.timeout(10000)
beforeEach(function () {
this.app = new Application({
// Your electron path can be any binary
// i.e for OSX an example path could be '/Applications/MyApp.app/Contents/MacOS/MyApp'
// But for the sake of the example we fetch it from our node_modules.
path: path.join(__dirname, '..', 'node_modules', '.bin', 'electron' + (process.platform === 'win32' ? '.cmd' : '')),
// Assuming you have the following directory structure
// |__ my project
// |__ ...
// |__ main.js
// |__ package.json
// |__ index.html
// |__ ...
// |__ test
// |__ spec.js <- You are here! ~ Well you should be.
// The following line tells spectron to look and use the main.js file
// and the package.json located 1 level above.
args: [path.join(__dirname, '..')],
env: {
ELECTRON_ENABLE_LOGGING: true,
ELECTRON_ENABLE_STACK_DUMPING: true,
NODE_ENV: 'test'
},
waitTimeout: 10e3,
requireName: 'electronRequire',
chromeDriverLogPath: '../chromedriverlog.txt',
chromeDriverArgs: ['remote-debugging-port=9222']
})
return this.app.start()
})
afterEach(function () {
if (this.app && this.app.isRunning()) {
return this.app.stop()
}
})
it('shows an initial window', function () {
return this.app.client.getWindowCount().then(function (count) {
assert.equal(count, 1)
// Please note that getWindowCount() will return 2 if `dev tools` are opened.
// assert.equal(count, 2)
})
})
})
I was having a similar issue, and changed the version of the packages I was working with.
This page has the version map:
https://github.com/electron-userland/spectron#version-map
It seems like this isn't your problem byt may help others.
I FINALLY found a way to fix this that worked for me. I found it in a comment by #wburgess-invision on this gitHub thread here: https://github.com/electron-userland/spectron/issues/60
Navigate to node_modules -> spectron -> lib -> launcher.js
after the line "const args = appArgs.concat(chromeArgs);" add a new line and copy and paste this: args.splice(args.indexOf('--enable-logging'), 1)
npm run build
To quote the thread: "I noticed that it was happening because of the --enable-logging parameter being passed in. So I made a modified version of launcher.js which purposely didn't include --enable-logging and it worked fine for us. I couldn't find where the spectron launcher was managing to pick up the --enable-logging flag from as I thought a better solution would have been just not providing it to launcher.js in the first place but I couldn't find out so I just went with the solution that worked for now. My assumption was that the --enable-logging flag was being picked up somewhere inside of webdriverio when the client is initialized and thus calls launcher.bat but I couldn't find out where."
Related
I have a Rails 7 app with esbuild :
esbuild.config.js :
#!/usr/bin/env node
const watch = process.argv.includes("--watch");
const esbuild = require('esbuild')
const coffeeScriptPlugin = require('esbuild-coffeescript');
const esbuildSvelte = require('esbuild-svelte');
const sveltePreprocess = require('svelte-preprocess');
esbuild
.build({
entryPoints: ["app/javascript/all.js"],
bundle: true,
outfile: "app/assets/builds/all.js",
// outdir: "app/assets/builds/",
plugins: [
esbuildSvelte({
preprocess: sveltePreprocess({coffeescript: { bare: true }}),
}),
// coffeeScriptPlugin({bare: true}), I TRIED THIS TOO...
],
logLevel: "debug",
watch: watch
})
.catch(() => process.exit(1));
my.svelte :
<script lang="coffee">
test = ->
console.log 'test coffee'
test()
</script>
got an error :
$ yarn build --watch yarn run v1.22.19 $ node ./esbuild.config.js
--watch ✘ [ERROR] [plugin esbuild-svelte] Unexpected token
app/javascript/all.js:3:3:
3 │ 1:
╵ ^
2: <script lang="coffee">
3: test = ->
^
4: console.log 'test coffee'
5: test()
The plugin "esbuild-svelte" was triggered by this import
app/javascript/svelte_src.js:6:32:
6 │ import DemoSvelteComponent from './svelte/DemoSvelteComponent.svelte'
╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error [watch] build finished, watching for changes... error Command
failed with exit code 1. info Visit
https://yarnpkg.com/en/docs/cli/run for documentation about this
command.
$ node -v
v18.4.0
package.json :
{
"name": "app",
"private": "true",
"dependencies": {
"#hotwired/stimulus": "^3.0.1",
"#hotwired/turbo-rails": "^7.1.3",
"esbuild": "^0.14.43",
"esbuild-coffeescript": "^2.1.0",
"esbuild-svelte": "^0.7.1",
"sass": "^1.52.3",
"svelte": "^3.48.0",
"svelte-preprocess": "^4.10.7"
},
"scripts": {
"build": "node ./esbuild.config.js"
}
}
How add coffeescript in svelte with Rails ?
This setup works with node v18.4.0 v16.15.1 v14.19.3. It turned out pretty much identical to what you have, except I don't know what's in your all.js file.
// package.json
{
"name": "app",
"private": "true",
"dependencies": {
"#hotwired/stimulus": "^3.0.1",
"#hotwired/turbo-rails": "^7.1.3",
"esbuild": "^0.14.43",
"esbuild-coffeescript": "^2.0.0",
"esbuild-svelte": "^0.7.1",
"svelte": "^3.48.0",
"svelte-preprocess": "^4.10.7"
},
"scripts": {
"build": "node ./esbuild.config.js"
}
}
// esbuild.config.js
const watch = process.argv.includes("--watch");
const esbuild = require("esbuild");
const esbuildSvelte = require("esbuild-svelte");
const sveltePreprocess = require("svelte-preprocess");
esbuild
.build({
entryPoints: ["app/javascript/all.js"],
outdir: "app/assets/builds/",
bundle: true,
sourcemap: true,
plugins: [
esbuildSvelte({
preprocess: sveltePreprocess(),
}),
],
logLevel: "debug",
watch: watch,
})
.catch(() => process.exit(1));
// app/javascript/all.js
import App from "./my.svelte";
new App({ target: document.body });
<!-- app/javascript/my.svelte -->
<script lang="coffee">
test = ->
console.log 'test coffee'
test()
</script>
Compiles:
$ yarn build --watch
yarn run v1.22.19
$ node ./esbuild.config.js --watch
[watch] build finished, watching for changes...
[watch] build started (change: "app/javascript/my.svelte")
[watch] build finished
and shows up in the browser console:
test coffee my.svelte:1
This is a smaller working example, maybe it'll help eliminate the source of the error. It compiles my.svelte file directly and prints out the source.
// package.json
{
"dependencies": {
"esbuild": "^0.14.43",
"esbuild-coffeescript": "^2.1.0",
"esbuild-svelte": "^0.7.1",
"svelte": "^3.48.0",
"svelte-preprocess": "^4.10.7"
}
}
// esbuild.config.js
require("esbuild").build({
entryPoints: ["app/javascript/my.svelte"],
plugins: [require("esbuild-svelte")({ preprocess: require("svelte-preprocess")() })],
}).catch(() => process.exit(1));
$ node --version
v18.4.0
$ node ./esbuild.config.js
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function instance($$self) {
var test;
test = function() {
return console.log("test coffee");
};
test();
return [];
}
class My extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, null, safe_not_equal, {});
}
}
export default My;
I don't find the problem, I make new app and copy files, I don't see when exactly that works, but that works ... ^^
May be a bad invisible character was in file?
So It's works fine with include esbuild-coffeescript ...
That stay a mystery for my use case (I try checkout from git and bug don't come back.... realy strange)
I've met the same problem in my rails app and my solution was using another build script
old:
"build-es": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets"
new:
"build": "node ./esbuild.config.js",
I'm facing the issue where I'm not able to call any kotlin js function and getting 'something' is not defined.
I've tried compiling project with gradle but ended up following this tutorial and compiling with npm.
I attached my project here
EDIT: tested with maven and worked. However since maven is deprecated, I would like to use gradle or npm.
html code:
<body>
<script src="test.js"></script> //file generated in bin/bundle
<script>
(function() {
let a = new test.Test(); //test - module, Test - my class, error occurrs at this line
a.test(); //test - method in class Test
})()
</script>
</body>
however it always results in
Uncaught ReferenceError: test is not defined
package.json:
{
"name": "test",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "shx rm -rf bin && webpack && shx rm -rf bin/build/kotlin-test*",
"test": "mocha bin/test"
},
"author": "",
"license": "ISC",
"dependencies": {
"kotlin": "^1.3.70"
},
"devDependencies": {
"#jetbrains/kotlin-webpack-plugin": "^3.0.2",
"kotlin-test": "^1.3.70",
"mocha": "^7.1.0",
"shx": "^0.3.2",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11"
},
"description": ""
}
webpack.config.js:
const KotlinWebpackPlugin = require('#jetbrains/kotlin-webpack-plugin');
module.exports = {
entry: 'test', // This tells webpack where to begin for bundling
resolve: {
modules: ['bin/build', 'node_modules'], // Webpack will use this to look for anything that is required
},
output: {
path: __dirname + '/bin/bundle', // This is where the bundle will go
filename: 'test.js', // The bundle will be called vectron.js
},
plugins: [
//Step one - Create a test build
new KotlinWebpackPlugin({
src: __dirname, // Build Everything
output: 'bin/test', // Output to bin/test
moduleName: 'test', // Will create vectron.js
moduleKind: 'commonjs', // Create commonjs modules
librariesAutoLookup: true, // Uses node_modules for libraries
packagesContents: [require('./package.json')], // Points to package.json for dependencies
}),
// Step two - Create a production build
new KotlinWebpackPlugin({
src: __dirname + '/src', // Build only what is in src
output: 'bin/build', // Output to bin/build
moduleName: 'test', // Create a file called vectron.js
moduleKind: 'commonjs', // Create commonjs modules
metaInfo: true, // Include .meta.js files
sourceMaps: true, // Include Source mappings
librariesAutoLookup: true, // Uses node_modules for libraries
packagesContents: [require('./package.json')], // Points to package.json for dependencies
}),
],
};
my class:
class Test {
fun test() {
println("test")
}
}
EDIT: npm compilation of test.js:
(function (_, Kotlin) {
'use strict';
var println = Kotlin.kotlin.io.println_s8jyv4$;
var Kind_CLASS = Kotlin.Kind.CLASS;
function Test() {
}
Test.prototype.test = function () {
println('test');
};
Test.$metadata$ = {
kind: Kind_CLASS,
simpleName: 'Test',
interfaces: []
};
_.Test = Test;
Kotlin.defineModule('test', _);
return _;
}(module.exports, require('kotlin'))); //error: module is not defined
//# sourceMappingURL=test.js.map
maven kotlin plugin output of test.js:
if (typeof kotlin === 'undefined') {
throw new Error("Error loading module 'test'. Its dependency 'kotlin' was not found. Please, check whether 'kotlin' is loaded prior to 'test'.");
}var test = function (_, Kotlin) {
'use strict';
var println = Kotlin.kotlin.io.println_s8jyv4$;
var Kind_CLASS = Kotlin.Kind.CLASS;
function Test() {
}
Test.prototype.test = function () {
println('test');
};
Test.$metadata$ = {
kind: Kind_CLASS,
simpleName: 'Test',
interfaces: []
};
_.Test = Test;
Kotlin.defineModule('test', _);
return _;
}(typeof test === 'undefined' ? {} : test, kotlin);
moduleKind was wrong in the build.gradle. Setting it to 'plain' fixed the issue.
Thank you
I wanted to build trading apps using electron JS and using the TradingView library but I got stuck on how to implement it. I also created a new blank project to implement it, but still result.
Is there anyone has ever implement TradingView to electron and could give me solution to this?
Blank app result
Error in console
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://demo_chart.tradingview.com/charting_library/charting_library.min.js"></script>
<script type="text/javascript" src="https://demo_chart.tradingview.com/datafeeds/udf/dist/polyfills.js"></script>
<script type="text/javascript" src="https://demo_chart.tradingview.com/datafeeds/udf/dist/bundle.js"></script>
<script type="text/javascript">
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
TradingView.onready(function()
{
var widget = window.tvWidget = new TradingView.widget({
// debug: true, // uncomment this line to see Library errors and warnings in the console
fullscreen: true,
symbol: 'AAPL',
interval: 'D',
container_id: "tv_chart_container",
// BEWARE: no trailing slash is expected in feed URL
datafeed: new Datafeeds.UDFCompatibleDatafeed("https://demo_feed.tradingview.com"),
library_path: "charting_library/",
locale: getParameterByName('lang') || "en",
disabled_features: ["use_localstorage_for_settings"],
enabled_features: ["study_templates"],
charts_storage_url: 'http://saveload.tradingview.com',
charts_storage_api_version: "1.1",
client_id: 'tradingview.com',
user_id: 'public_user_id',
theme: getParameterByName('theme'),
});
});
</script>
</head>
<body style="margin:0px;">
<div id="tv_chart_container"></div>
</body>
</html>
Never used electron so using this opportunity to try it out myself. This is the steps I took.
Look at the documentation https://electronjs.org/docs/tutorial/first-app
1) created a folder called electron-test
2) run command npm init in that folder
3) modify package.json to be this
{
"name": "electron-test",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "",
"license": "ISC",
"dependencies": {}
}
4) create main.js
const { app, BrowserWindow } = require("electron");
function createWindow() {
// Create the browser window.
win = new BrowserWindow({ width: 800, height: 600 });
// and load the index.html of the app.
win.loadFile("index.html");
}
app.on("ready", createWindow);
5) create index.html
6) run command npm install electron
7) run command npm start
This is the result
Is there anyone has ever implement TradingView to electron and could give me solution to this?
Well I don't have solution to the error, but I have found my way out to build an tradingView+ElectronJs app.
In short, you can start with TradingView Charting Library Integration Examples, from which you can choose the kind of integration template to start with(I just use react-javascript to start my app).And I just take this template for example.
After follow How to start to configure the app properly. You should first install election to your app(which means run npm install --save electron in the directory root), then add the main.js to your directory root, and configure your package.json and main.js properly. Blow is my package.json and main.js code.
package.json
{
"name": "knownsec-fed",
"version": "0.1.0",
"private": true,
"main": "main.js", // 配置启动文件
"homepage":".", //
"dependencies": {
"electron": "^1.7.10",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.1.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"electron-start": "electron ." // start electron app
}
}
main.js
const {app, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')
let mainWindow
function createWindow () {
mainWindow = new BrowserWindow({width: 800, height: 600})
/**
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
*/
mainWindow.loadURL('http://localhost:3000/');
// mainWindow.webContents.openDevTools()
mainWindow.on('closed', function () {
mainWindow = null
})
}
app.on('ready', createWindow)
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
if (mainWindow === null) {
createWindow()
}
})
to start the electron app, first run npm start to start react then run npm run electron-start to start electron app and then all thing done.
When you open Object with widget, your widget trying to find static folder from wrong path. Just connect library in widget option like this:
I'm learning electron and I've made an electron app that read and create files.
When I start the application with npm start or electron . it works as intended:
But when I use npm run build or build -w commands, the application built just shows a white screen
Is there something wrong with my code or something wrong with the commands I'm using?
This is my package.json
{
"name": "prova",
"version": "1.1.3",
"description": "Prova electron",
"main": "index.js",
"scripts": {
"start": "electron .",
"dist" : "build"
},
"author": "Randy",
"license": "ISC",
"devDependencies": {
"electron": "^2.0.2",
"electron-packager": "^12.1.0"
},
"build": {
"appId": "prova",
"win":{
"target" : "nsis",
"icon" : "icon.ico"
}
}
}
This is my main js page:
const {app, BrowserWindow} = require('electron')
const url = require('url')
function boot(){
win = new BrowserWindow()
win.loadURL(url.format({
pathname: 'index.html',
slashes: true
}))
}
app.on('ready', boot);
and there is my functions js page:
var app = require("electron").remote;
var dialog = app.dialog;
var fs = require("fs");
var i = 0;
var stringaLetta = "";
document.getElementById("bottone").onclick = function(){
dialog.showSaveDialog((fileName) => {
if(fileName === undefined){
alert("errore")
return
}
var content = document.getElementById("testo").value;
fs.writeFile(fileName, content, (err) => {
if (err == undefined) {
dialog.showMessageBox({
message: "the file has been saved",
buttons: ["OK"]
});
}
else dialog.showMessageBox({
message: err
})
})
})
}
document.getElementById("bottone2").onclick = function(){
dialog.showOpenDialog((fileNames) => {
if(fileNames === undefined){
dialog.showMessageBox({
message: "errore durante l'apertura",
buttons: ["OK"]
})
return
} else{
readFile(fileNames[0]);
}
})
}
function readFile(fP){
fs.readFile(fP, 'utf-8', (err, data) => {
if(err){
alert(err)
return
}
var textArea = document.getElementById("rtesto")
textArea.innerHTML = "";
i = 0;
do{
if(data.charAt(i) == "\n"){
stringaLetta += "<br\>";
}else{
stringaLetta += data.charAt(i);
}
i++;
}while(data.charAt(i) != "")
textArea.innerHTML = stringaLetta;
stringaLetta = " ";
})
}
I had a similar problem when I tried to build for windows.
While the win.loadURL(...) seems to work like that in development, maybe try to change it to this when building:
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}));
This makes sure it definitly gets the right path to your index.html file.
For the path.join(...) and url.format(...) to work you need to require them first:
const path = require('path');
const url = require('url');
i recently face white screen issue in my case little bit difference
i used vue framework with router(router must be hash)
1.for vue.js with vue router in electron
const router = new VueRouter({
mode: 'hash',
routes: [...]
})
2.for react.js with react router in electron
hashrouter
instead of
BrowserRouter
without any framework
check entry point url placed correctly
win.loadURL('app://./index.html')
In my case the build showed a white site as well. For those who are using React Router in their project this solution might be helpful.
My startUrl variable looks like this:
const startUrl = process.env.ELECTRON_START_URL || url.format(
{
pathname: path.join(__dirname, '/../build/index.html'),
protocol: 'file:',
slashes: true
});
For me the solution was to move from BrowserRouter to HashRouter in your App.js as mentioned in this thread
render()
{
return (
<Provider store = { store }>
<HashRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/Login' component={Login} />
</Switch>
</HashRouter>
</Provider>
);
}
I don't know about the build process especially, I had also the same problem on development, that electron shows nothing but a blank screen (probably because I clicked a link that was not available earlier).
After rebuilding and what else nothing was shown on the screen.
The final hack that worked for me was
clearning my Appdata from system.
In my case I had linux I cleared the app data by going to ~/.config/myApp
Windows: C:\Users\<user>\AppData\Roaming\<yourAppName>\Cache
OSX: /Users/<user>/Library/Application Support/<yourAppName>/Cache
Hope it will help someone in need.
Maybe you used a template of code that uses 2 cases,
one for development mode and one for production. At least that was my problem, so for dev mode I used to use a URL to localhost, and in production, it points to the build dir: like so:
const prodPath = format({
pathname: resolve('renderer/out/start/index.html'),
protocol: 'file:',
slashes: true
})
check how you set the title for the main window.
I tried to use 'process.env.npm_package_productName'
mainWindow.setTitle(process.env.npm_package_productName);
works fine locally, but fails when packed!
I had the same problem. I think it is a miss understanding, that the electron app will be bundled into a single .exe file.
I used the electron-react-boilerplate template from GitHub. If you
are going to package your application, all it does is generating
a directory with your bundled code (the electron-react-boilerplate template does that for you) and a bunch of other files with the generated .exe file in release/build/win-unpacked in the release/build folder.
What you have to do to get it to work:
Get the absolute path code wise of your index.html file in release/app/dist/renderer/index.html (CODEWISE) (Get this path in your main.ts/js)
Then use this file url in your mainWindow.loadUrl() function.
Then you will realize, that the whole release folder is actually your app to distribute to your clients.
Note: For launch the app with electron, follow these steps:(forgive my
English I am native french speaker)
First of all forget about the electron folder, do not touch it.
In your react/ion directory which is your root directory for the app,add the following in your package.json file: "homepage": "./"
Now from your react/ionic directory which is your root directory for the app, navigate to the "public" directory and update your index.html file replacing <base href="/" /> with: <base href="./" />
Now run the following command and that is it...
ionic build && npx cap copy
npx cap open electron
Short version:
I am unable to see the code coverage from my tests that I have written using nightmare.js and mocha. I have already tried to use istanbul and _mocha with no luck so far.
Big version:
I have a little project:
/public/index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Website</title>
<script src="./js/hello.js"></script>
</head>
<body>
<h1>My Website</h1>
</body>
</html>
/public/js/hello.js
window.hello = function hello(){
return 'world';
};
The site is running using express and forever.
When I am trying to test it using nightmare.js.
/test/test.js
var path = require('path');
var Nightmare = require('nightmare');
var should = require('should');
/*global describe */
/*global it */
describe('Simple demo', function () {
this.timeout(15000);
var url = 'http://localhost:9000';
it('check hello world result', function (done) {
new Nightmare()
.goto(url)
.evaluate(function () {
/*global hello */
return hello();
}, function (value) {
var expected = "world";
if (value === null) {
false.should.equal(true);
return done();
}
value.should.equal(expected);
return done();
})
.run();
});
it('should get the index title', function (done) {
var expected = 'My Website';
new Nightmare()
.goto(url)
.title(function (title) {
title.should.equal(expected);
done();
})
.run();
});
});
The tests are passing
$ mocha
Simple demo
✓ check hello world result (2089ms)
title = Alexandria
✓ should get the index title (1947ms)
2 passing (4s)
But, I am unable to get code coverage reports from my tests.
I have already tried some commands like:
$ istanbul cover _mocha -- test/test.js -u exports -R spec
No coverage information was collected, exit without writing coverage information
$ istanbul cover --hook-run-in-context _mocha -- -R spec
No coverage information was collected, exit without writing coverage information
So, someone was able to create code coverage reports of nightmare.js tests? If no, there is something close to that using another tools?
I had exactly this same problem in my project. I couldn't find any libraries or configurations which solve this problem in simply way but with some implementation and Grunt configuration you can get code coverage from Grunt process.
Dependencies which I used in my project:
"chai": "^3.5.0",
"grunt": "^0.4.5",
"grunt-contrib-clean": "^0.7.0",
"grunt-contrib-copy": "^0.8.2",
"grunt-express-server": "^0.5.3",
"grunt-istanbul": "^0.7.1",
"grunt-mocha-test": "^0.12.7",
"istanbul": "^0.4.4",
"nightmare": "^2.2.0"
My project structure:
public/ -- public folder for index.html
src/ -- source folder for hello.js
test/ -- mocha tests implementation
server/ -- express implementation for server.js
coverage/ -- HTML report from code coverage
report/ -- HTML report from mocha tests
dist/ -- folder which is used by express server to get content, generated by Grunt
Steps which you have to run from Grunt:
grunt.registerTask('test_hello', [
'clean', // clean dist/ folder
'copy:public', // copy public files from public/ (ex. index.html) to dist/
'instrument', // generate instruments (ex. hello.js) for code coverage from src/ by istanbul
'express', // run express server from dist/ folder
'mochaTest', // run mocha tests with nightmare and generate HTML report to report/ folder
'report_coverage' // generate HTML report from code coverage to coverage/ folder
]);
Grunt configurations:
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-express-server');
grunt.loadNpmTasks('grunt-istanbul');
grunt.loadNpmTasks('grunt-mocha-test');
grunt.initConfig({
clean: {
dist: ['dist/', 'report/', 'coverage/']
},
copy: {
public: {
expand: true,
cwd: 'public/',
src: ['**'],
dest: 'dist/'
}
},
instrument: {
files: ['**/*.js'],
options: {
cwd: 'src/',
lazy: true,
basePath: 'dist/'
}
},
express: {
dev: {
options: {
port: 9000,
script: 'server/server.js',
background: true
}
}
},
mochaTest: {
hello: {
options: {
timeout: 10000,
captureFile: 'report/results.txt', // Optionally capture the reporter output to a file
quiet: false, // Optionally suppress output to standard out (defaults to false)
clearRequireCache: false // Optionally clear the require cache before running tests (defaults to false)
},
src: ['test/*.js']
},
}
});
grunt.registerTask('report_coverage', function () {
var coverage = require('./test/utils/coverage');
coverage.generateReport();
});
grunt.registerTask('test_hello', [
'clean', // clean dist/ folder
'copy:public', // copy public files from public/ (ex. index.html) to dist/
'instrument', // generate instruments (ex. hello.js) for code coverage from src/ by istanbul
'express', // run express server from dist/ folder
'mochaTest:hello', // run mocha tests with nightmare and generate HTML report to report/ folder
'report_coverage' // generate HTML report from code coverage to coverage/ folder
]);
}
I created also class which allows me collect code coverage from each Nightmare instance:
var istanbul = require('istanbul');
var reporter = new istanbul.Reporter(),
sync = true;
var Coverage = function () {
this.loadCodeCoverage = function (dom, done) {
dom
.evaluate(function () {
return window.__coverage__; // this variable store all information about code coverage
})
.then(function (coverageDate) {
if (coverageDate) {
this.getCollector().add(coverageDate);
}
done();
}.bind(this))
.catch(function (error) {
done(error);
});
}
// load page by nightmare
this.getCollector = function () {
if (!this.collector) {
this.collector = new istanbul.Collector();
}
return this.collector;
}
this.generateReport = function () {
reporter.add('text');
reporter.addAll(['lcov', 'clover']);
reporter.write(this.collector, sync, function () {
console.log('All reports generated');
});
}
}
module.exports = new Coverage();
For the above configuration file test/test.js should has the below structure:
var should = require('should');
var coverage = require('./utils/coverage');
/*global describe */
/*global it */
describe('Simple demo', function () {
this.timeout(15000);
var url = 'http://localhost:9000';
before(function (done) {
global.dom = new Nightmare()
.goto(url)
.evaluate(function () {
return 'test';
})
.then(function(result) {
done();
})
.catch(function(error) {
done(error);
});
});
after(function (done) {
coverage.loadCodeCoverage(dom, done);
});
it('check hello world result', function (done) {
dom.evaluate(function () {
/*global hello */
return hello();
})
.then(function(value) {
var expected = "world";
if (value === null) {
false.should.equal(true);
return done();
}
value.should.equal(expected);
return done();
})
.catch(function(error) {
done(error);
});
});
it('should get the index title', function (done) {
var expected = 'My Website';
dom.title(function (title) {
title.should.equal(expected);
done();
});
});
});
If everything works fine you should get on the console information and report from code coverage in HTML form should be generated in folder coverage/
Running "report_coverage" task
------------------------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
------------------------------------|----------|----------|----------|----------|----------------|
js/ | 100 | 100 | 100 | 100 | |
hello.js | 100 | 100 | 100 | 100 | |
------------------------------------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
------------------------------------|----------|----------|----------|----------|----------------|
Problem which I still have is that for each test description I have to add method before and after. Could be nice to have these implementations only in one place, example somewhere in Grunt configuration that i needn't remember about them when I'm doing new test description.
It will be very appreciated if someone find more general solution for before and after methods.
I want to propose another approach. It basically boils down to
Instrument the application code with Istanbul
Fetch the coverage data from the browser and pass it to the test process (so we can extract it)
Run the reports
Here's a code snippet that does this
nightmare
.evaluate(() => window.__coverage__) // this executes in browser scope
.end() // terminate the Electron (browser) process
.then((cov) => {
// this executes in Node scope
// handle the data passed back to us from browser scope
const strCoverage = JSON.stringify(cov);
const hash = require('crypto').createHmac('sha256', '')
.update(strCoverage)
.digest('hex');
const fileName = `/tmp/coverage-${hash}.json`;
require('fs').writeFileSync(fileName, strCoverage);
done(); // the callback from the test
})
.catch(err => console.log(err));
for detailed information and links to actual commits please check my blog:
http://atodorov.org/blog/2017/08/12/code-coverage-from-nightmarejs-tests/