I'm building an electron app to run on Windows to move a robot. (The controlling device must be windows as requested by the customer) It's built in Ionic with typescript and I'm attempting to create a desktop solution with a feature that runs some .exe files to move the robot in some predefined movements. They are a group of .exe files made by another guy, and they work fine when run from the shell. Those exe files do turn off/on lights, move an arm, etc... Right now those exe files work and they are all in a C:\botcontrol\ folder.
I've been struggling with node's child_process to be able to run all those exe files from a simple interface. My problem is that I'm not able to include child_process in any manner without getting the console error t(...).execFile is not a function.
I've been digging two days through the internet in various languages on how to include the child_process node module in a typescript angular 7 app, but I'm really stuck with this and I don't know how to solve it without getting the cursed error above. I tried adding to tsconfig:
"map": {
"child_process": "#node/child_process"
}
and many other webpack config attempts, but none of them worked for me.
Right now what I need is to add 'child_process' so when I run a service I can import it like:
import { exec,execFile} from "child_process";
...
or
import * as cp from "child_process";
...
I read somehwere that I need to add the module to be exported at SystemJS, or at the webpack config, but I don't find any config like that in my project.
My dependencies are these in the package.json:
...
"dependencies": {
"#angular/common": "~8.1.2",
"#angular/compiler": "~8.1.2",
"#angular/core": "~8.1.2",
"#angular/forms": "~8.1.2",
"#angular/platform-browser": "~8.1.2",
"#angular/platform-browser-dynamic": "~8.1.2",
"#angular/router": "~8.1.2",
"#ionic-native/core": "^5.14.0",
"#ionic-native/splash-screen": "^5.14.0",
"#ionic-native/status-bar": "^5.14.0",
"#ionic/angular": "^4.9.1",
"#ionic/storage": "^2.2.0",
"#ngx-translate/core": "^11.0.1",
"#ngx-translate/http-loader": "^4.0.0",
"cordova-sqlite-storage": "^3.3.0",
"core-js": "^2.5.4",
"electron-json-storage": "^4.1.8",
"ng-connection-service": "^1.0.4",
"rxjs": "^6.5.3",
"simple-keyboard": "^2.26.4",
"tslib": "^1.9.0",
"wine": "^0.9.8",
"zone.js": "~0.9.1"
},
"devDependencies": {
"#angular-devkit/architect": "~0.801.2",
"#angular-devkit/build-angular": "~0.801.2",
"#angular-devkit/core": "~8.1.2",
"#angular-devkit/schematics": "~8.1.2",
"#angular/cli": "~8.1.2",
"#angular/compiler": "~8.1.2",
"#angular/compiler-cli": "~8.1.2",
"#angular/language-service": "~8.1.2",
"#ionic/angular-toolkit": "~2.0.0",
"#types/jasmine": "~3.3.8",
"#types/jasminewd2": "~2.0.3",
"#types/node": "^8.10.54",
"codelyzer": "^5.1.1",
"electron": "^6.0.10",
"electron-installer-dmg": "^3.0.0",
"electron-packager": "^14.0.6",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
},
...
Can anyone help me, please? It would be much appreciated :)
If you're using the #capacitor-community/electron platform to build your app you must consider the following:
Electron works with two process types, the main process and the renderer process (find more info here)
The renderer process is basically a web browser, that cannot use native capabilities such as child_process while the main process can
The renderer process can communicate with the main process, more info here
Your ionic/angular app is built to run as a renderer process
A possible solution:
Send a message from the renderer process (angular) to the main process (electron) in order to make the main process run the executable file and get back the output to the renderer process (aka black magic).
How?
Create the main process handler
Modify the file YOUR_PROJECT_DIR/electron/src/index.ts
add the import:
import {ipcMain} from 'electron';
import {execFileSync} from 'child_process';
add the event handler at the and of the file:
ipcMain.handle('execCommand', (event, arg) => {
//this will handle an event called 'execCommand' triggered from the renderer process
let cmdRes = execFileSync("./path-to-file.exe", [], {encoding: 'utf8'});
//cmdRes contains the file execution output
return cmdRes;
});
Expose ipcRenderer to the main context of the ionic/angular application
Modify the file YOUR_PROJECT_DIR/electron/src/preload.ts
add this:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
ipc: { ...ipcRenderer }
});
A security concern: a user could open the developer tools and modify the javascript code to invoke events with malicious content, it's not a real problem in a local environment but it could be with remote resources.
Communicate with the main process from the angular app
Let's say you're inside a ionic/angular page:
callExecutable() {
(<any>window).electron.ipc.invoke('execCommand').then(
res=>console.log(res) //res contains the command output from the main process
);
}
In this way you can create an event handler for each .exe files you have to call and create an angular service to .invoke() each handler.
Just tested and it's working with ionic version 6.18.1 and #capacitor-community/electron version 7.5.6
Related
I'm working on an VS Code extension that's using a bunch of modules, some of which are needed in release.
I need to be able to install this extension in a new environnement without any internet connexion. How would I go about that since I assume my released extension will need to fetch its modules? Is there a way to bundle my extension with a "local version" of the needed modules?
I tried using VS Code's vsce. It created a .vscx file from my extension so that I could install it from my extension manager inside VS. Sadly some extensions wouldn't load and it crashed...
I also tried to manually install my extension globaly, by taking the "/dist" and "/out" files from my dev project and put them in a common file in my "${USER}/.vscode/extensions" folder.
I'm not 100% sure but I believe it crashed to because it couldn't load the modules.
Here is the module list (all of them, dev and realease) :
"#rollup/plugin-commonjs": "^22.0.2",
"#rollup/plugin-node-resolve": "^14.1.0",
"#rollup/plugin-typescript": "^8.5.0",
"#tsconfig/svelte": "^3.0.0",
"#types/glob": "^7.2.0",
"#types/jquery": "^3.5.14",
"#types/mocha": "^9.1.1",
"#types/node": "16.x",
"#types/vscode": "^1.71.0",
"#typescript-eslint/eslint-plugin": "^5.31.0",
"#typescript-eslint/parser": "^5.31.0",
"#vscode/test-electron": "^2.1.5",
"rollup": "^2.3.4",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-svelte": "^6.0.0",
"rollup-plugin-terser": "^7.0.2",
"ts-loader": "^9.3.1",
"typescript": "^4.7.4",
"eslint": "^8.20.0",
"glob": "^8.0.3",
"mocha": "^10.0.0",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"concurrently": "^7.4.0",
"devextreme": "17.2.18",
"svelte": "^3.31.0",
"svelte-check": "^1.1.23",
"svelte-preprocess": "^4.6.1",
"jquery": "^3.6.1",
"trash": "^8.1.0"
Thank you
You could store all installed extensions in a zip file with this (batch)script, and copy that to any location you need:
#echo off
for /F "usebackq tokens=2 delims='" %%f in (`code --list-extensions --verbose ^| findstr /c:"Started watching" ^| findstr "extensions"`) do ( set u=%%f)
echo VSCode extensions are installed in: %u%
"c:\Program Files\7-Zip\7z.exe" a %TEMP%\extensions.7z %u%
echo All currently installed extensions are compressed in the file: %TEMP%\extensions.7z
In pretest command "tsc" i have used so that file should convert to js. This command changes ts code to js. Now in config js file specs extension remains TS example- file "testspec.ts" not js. extension is not changing. Due to this i am facing specs not found. Extension should also change in configuration.js of specs file.How can i resolve this, so that i just have to give command npm run test and my testcase executes.
Code of package.json
{
"name": "protractortypescriptcucumberframework",
"version": "1.0.0",
"description": "cucumberframework",
"main": "index.js",
"scripts": {
"test": "protractor JSFiles/configuration.js",
"pretest": "tsc",
"protractor":"./node_modules/protractor/built/cli.js",
"webdriver-update":"./node_modules/.bin/webdriver-manager update"
},
"author": "ak",
"license": "ISC",
"dependencies": {
"protractor": "^7.0.0",
"typescript": "~3.9.6",
"jasmine": "~3.5.0",
"#types/jasmine": "~3.5.11",
"#types/jasminewd2": "~2.0.8",
"ts-node": "~8.10.2",
"#types/node": "~14.0.23"
}
}
Error i face is below after command > npm run test
> protractortypescriptcucumberframework#1.0.0 pretest C:\Users\Admin\Eclipse Protractor JavaScript\ProtractorTypeScriptCucumberFramework
> tsc
> protractortypescriptcucumberframework#1.0.0 test C:\Users\Admin\Eclipse Protractor JavaScript\ProtractorTypeScriptCucumberFramework
> protractor JSFiles/configuration.js
[13:49:25] I/launcher - Running 1 instances of WebDriver
[13:49:25] I/direct - Using ChromeDriver directly...
DevTools listening on ws://127.0.0.1:49802/devtools/browser/9a9f6b17-da99-4aec-993c-a265017ee716
> protractortypescriptcucumberframework#1.0.0 test C:\Users\Admin\Eclipse Protractor JavaScript\ProtractorTypeScriptCucumberFramework
> protractor JSFiles/configuration.js
[13:49:25] I/launcher - Running 1 instances of WebDriver
[13:49:25] I/direct - Using ChromeDriver directly...
DevTools listening on ws://127.0.0.1:49802/devtools/browser/9a9f6b17-da99-4aec-993c-a265017ee716
Started
No specs found
Finished in 0.218 seconds
[13:49:49] I/launcher - 0 instance(s) of WebDriver still running
[13:49:49] I/launcher - chrome #01 passed
Config file code.
import {Config} from "protractor";
// An example configuration file
export let config: Config = {
// The address of a running selenium server.
//seleniumAddress: 'http://localhost:4444/wd/hub',
directConnect:true,
// Capabilities to be passed to the webdriver instance.
capabilities: {
browserName: 'chrome'
},
// Spec patterns are relative to the configuration file location passed
// to protractor (in this example conf.js).
// They may include glob patterns.
specs: ['testspec.ts'],
// Options to be passed to Jasmine-node.
jasmineNodeOpts: {
showColors: true, // Use colors in the command line report.
}
};
I figured out the error.
In config file of ts extension need to pass spec with js extension only. scripts tag will not automatically change spec extension. It has to be passed by user in config ts file.
I am new to Angular testing, and I want to perform 2 kinds of test for my application:
Unit Test - I choose to use Jest since I can run my test without opening the browser, and it also supports testing for specific cases with --testNamePatern.
End to end test - I want to try out Protractor since it is available in Angular and also has a big Angular community to work with.
My question is, can I use both Jest and Protractor in my application? If yes, do I need to configure anything to use both of them in my application.
You can use both jest and protractor in your application. By default the new angular cli release gives you a karma runner for unit tests and a protractor runner for end to end tests inside the same application. You are just changing Karma with Jest.
Can I run protractor tests (end to end) with jest?
No you cannot.
Can I run unit tests using protractor?
No you cannot.
Can I run protractor for end to end tests and jest for unit tests in the same application?
Yes you can. You will just need to tell jest which files to pick up and the same with protractor.
Can I get both the reports in a single file or a single run?
No you cannot. You will have to configure your jest runner to print reports which will be different from the protractor reports.
You can use both jest and protractor without configuring anything special. Here is a snippet of the package.json I am using for running e2e tests with protractor and lighthouse tests with jest.
{
"name": "performance-tests",
"version": "1.0.0",
"description": "Performance tests and end to end tests.",
"main": "jest.js",
"scripts": {
"debug": "node --inspect-brk ./node_modules/.bin/protractor protractor.conf.js",
"pretest": "npm run tsc && npm run webdriver-update",
"e2e": "npm run tsc && ./node_modules/protractor/bin/protractor protractor/compiled-js-files/protractor.conf.js",
"grid": "sh run-grid.sh && npm run e2e",
"tsc": "./node_modules/typescript/bin/tsc",
"webdriver-update": "./node_modules/protractor/bin/webdriver-manager update --standalone --versions.standalone=3.8.0 --chrome --versions.chrome=78.0.3904.97",
"lighthouse": "./node_modules/jest/bin/jest.js --verbose -t=lighthouse",
"lighthouse-reports": "./node_modules/jest/bin/jest.js --verbose -t=lighthouse && node ./lighthouse/db.js"
},
"repository": {
"type": "",
"url": ""
},
"author": "Sankalan Parajuli",
"license": "ISC",
"bugs": {
"url": ""
},
"homepage": "",
"dependencies": {
"#types/jasmine": "^3.3.12",
"#types/jasminewd2": "^2.0.6",
"#types/node": "^12.12.14",
"jasmine": "^3.3.1",
"lighthouse": "^4.0.0-beta",
"protractor": "5.4.2",
"protractor-beautiful-reporter": "^1.3.3"
},
"devDependencies": {
"#types/request": "^2.48.3",
"#types/selenium-webdriver": "^4.0.0",
"csvtojson": "^2.0.8",
"jest": "^23.4.1",
"moment": "^2.24.0",
"mongodb": "^3.1.13",
"puppeteer": "^1.6.0",
"request-promise": "^4.2.5",
"ts-node": "^8.5.2",
"typescript": "2.8.1"
}
}
Hope it helps.
I know that you could use browserstack before protractor 3.0 but they have added official support in 3.0 . I haven't been able to try that out yet so maybe that's the answer to my question? Currently don't have the ability to upgrade so if that's my answer then I will go through the proper channels to make that happen.
The problem I'm having with browser stack is that it names all the sessions random hashes. So I may have a suite of tests and I have no way to tell which session is which test. You can manually change the capabilities.name but then everything will have that name.
I know when I use saucelabs it will name everything after the name of the test file. so for example it will be sometest.js . I don't know if this is something protractor is doing or some node-module we're using that is doing the work behind the scenes and I don't know?
I just want to know how I can run my suite that runs all my smoketests and names each browserstack session after the actual name of the tests in the suite.
here is an example of what things look like on browserstack. I did manually set the capabilities.name to mytest for a few sessions.
here is how they look with sauce labs
Also here is my package.json file in case any of these modules might be making saucelabs name the jobs after the test file name
{
"name": "protractor",
"version": "1.0.0",
"description": "Protractor protype",
"main": "Gruntfile.js",
"dependencies": {
"grunt-sauce-tunnel": "^0.2.1",
"jasmine-reporters": "^2.1.1",
"lodash": "^3.2.0",
"protractor": "2.5.1",
"require-all": "2.0.0"
},
"devDependencies": {
"chalk": "^1.1.1",
"grunt": "^0.4.5",
"grunt-env": "^0.4.2",
"grunt-protractor-runner": "3.0.0",
"grunt-sauce-connect-launcher": "^0.3.0",
"jasmine-spec-reporter": "^2.2.3",
"request-promise": "^1.0.2",
"selenium-webdriver": "2.48.2",
"xlsx-json": "^0.1.0"
},
"scripts": {
"install": "node node_modules/protractor/bin/webdriver-manager update",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git:user/rwolfe/protractor"
},
"author": "",
"license": "ISC"
}
So I don't know if this was due to upgrading to protractor 3.0 or not but apparently in the conf.js I saw that for sauce labs we were setting
exports.config.capabilities.name = " ";
and when I did that for browserstack now the names of the test show up how I would expect as below.
I am new to NodeJS so this is probably an easy fix. However I was not able to find the solution with my current comprehension level.
I cloned the heroku-facebook-nodejs template from Github, and am trying to do some testing in the development environment on my local server. I am using nodejs version 0.6.10
https://github.com/heroku/facebook-template-nodejs
I modified the package.json as the following:
{
"name": "facebook-template-node",
"version": "0.0.1",
"description": "Template app for Heroku / Facebook integration, Node.js language",
"dependencies": {
"ejs": "0.4.3",
"everyauth": "0.2.18",
"express": "2.5.2",
"facebook-client": "1.3.0",
"facebook": "0.0.3",
"node-uuid": "1.2.0",
"socket.io": "0.8.7",
"connect": "1.8.5"
}
}
I removed the following:
require.paths.unshift(__dirname + '/lib');
and replaced
var socket_manager = require('socket_manager').create(io);
with
var socket_manager = require('lib/socket_manager').create(io);
and then ran npm-install -d to install the modules locally.
When I run node web.js, I get the following error:
system-process:hollow-cloud-1974 ericjang2004$ node web.js
The "sys" module is now called "util". It should have a similar interface.
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot find module 'oauth'
at Function._resolveFilename (module.js:332:11)
at Function._load (module.js:279:25)
at Module.require (module.js:354:17)
at require (module.js:370:17)
at Object.<anonymous> (/Users/Eric/Desktop/hollow-cloud-1974/node_modules/everyauth/lib/modules/oauth2.js:2:13)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Module.require (module.js:354:17)
I am not sure how to proceed. Any tips? I read that Heroku uses an older version of Node, if that helps.
Add "oauth": "0.9.5" to your dependencies in package.json, so it looks like:
"dependencies": {
"ejs": "0.4.3",
"oauth": "0.9.5",
"everyauth": "0.2.18",
"express": "2.5.2",
"facebook-client": "1.3.0",
"facebook": "0.0.3",
"node-uuid": "1.2.0",
"socket.io": "0.8.7",
"connect": "1.8.5"
}
and run npm install -d
running npm install whatever --save instead will ensure that the module gets added to the package json.
you would do npm install whatever --save-dev if its a development dependency...