How to connect to an existing Electron app using Spectron - webdriver

Is it possible to connect to an existing Electron application using Spectron? I am not particularly sure on how to go about implementing this..
I'd like to be able to do something like:
import { Application } from 'spectron';
import electronPath from 'electron';
import path from 'path';
// but don't spawn new electron application
new Application({
path: electronPath,
args: [path.join(__dirname, '..', '..', 'app')],
});
There are some documentation out there for using debuggerAddress option in Spectron, but I'm not really sure on whether that is what I am looking for, since the arguments for debuggerAddress is url, like so: '127.0.0.1:1234'.

I struggled making this work for Electron 6, was able to in the end, here is a working repo (made changes on top of an older one)
https://github.com/florin05/electron-spectron-example

Please make sure that you have created Test Folder in the same directory and create spectron file in this file.
Json File Changes:
"scripts": {"test": "mocha"}
const app = new Application({path: electronPath,args:[path.join(__dirname,'..')],})
beforeEach(function () {return app.start()})
afterEach(function () {if (app && app.isRunning()) {return app.stop()}})

Related

File path in NextJS api route not resolving

I'm trying to resolve a file path in NextJS.
I understand that API routes are working a little bit differently when deployed to Vercel. In order to create a correct path to the file I assumed I had to do this:
const svg = fs.readFileSync(
path.join(process.cwd(), "img", "file.svg"),
"utf-8",
);
// ENOENT: no such file or directory
But I cannot make it work. The file cannot be found under that path.
How can I find the correct path for a file in NextJS api routes?
I've followed the documentation of this.
Next version is: 11.1.3
When logging the path, it is giving /var/task/packages/project-root/img/file.svg
Try using
path.resolve("img", "file.svg")
Maybe it should help.
Pretty sure you'll find the file if you serve it as a static file - Next.js documentation here
I'm thinking it's not bundled in the deployment, but whatever you have in /public will definitely be deployed.
Good luck 💪🏻
I manage to create a small sandbox that will clarify your issue. Open it using StackBlitz
Project Structure
.
├── pages
| ├── api
| | ├── hello.js
| ├── _app.js
| ├── index.js
├── public
| ├── 1.txt --> this is a demonstration file
I reproduce your code in the hello api for testing purposes
const { readFileSync } = require('fs');
const { join } = require('path');
export default (req, res) => {
const path = join(process.cwd(), '/public/1.txt');
const value = readFileSync(path, { encoding: 'utf-8' });
res.status(200).json({ value });
};
This API entry is called from the index.js file
import Head from 'next/head';
import { useEffect, useState } from 'react';
export default function Home() {
const [value, setValue ] = useState('');
useEffect(() => {
fetch('/api/hello')
.then((res) => res.json())
.then(data => setValue(data.value));
});
return (
<div>
<Head>
<title>Create Next App</title>
</Head>
<main>
<h1>{value}</h1>
</main>
</div>
);
}
Yes, this is a very simplified version (for testing purposes only.. I assume we won't use readFileSync in production) - but - it reproduces your code.
Unfortunately, it works perfectly fine in dev mode and in production mode (npm run build + npm start), which means:
You either misconfigured your img folder
Perhaps you are lacking read permissions for the path you are using. For instance if you deploy your work to a remote machine, most directories will have limited access and therefore prevent you from reading the file (for testing this theory please read this post and execute it on your deployed machine)
For anyone coming across this, I actually opened a ticket at Vercel to ask them about this.
It turns out it was a caching issue that is caused by using Yarn 3
The support redirected me to this page explaining that they would have issues with anything above Yarn 1.
According to them there is nothing really they can do about right now but suggest us to use a different package manager.
I'm using Yarn 1.22, but still have this issue. The reason is because files are not generated during build and run times, so they are never found. The way to get around this is to create a separate .js file that to wrap around the said static files (html, txt, etc). Export this JS object which contains the files, and Vercel will generate them. I'm using this to generate email templates.
//account_verify.js
import path from 'path';
import { promises as fs } from 'fs';
import { prefixPath } from './constants';
// TODO: force this to conform to a typescript type
export default {
subject: 'Confirm Your Account',
data: {
email_verification_link: '{{email_verification_link}}',
first_name: '{{first_name}}'
},
templates: {
txt: fs.readFile(path.join(process.cwd(), prefixPath, 'account_verify.txt'), 'utf8'),
html: fs.readFile(path.join(process.cwd(), prefixPath, 'account_verify.html'), 'utf8'),
}
};

SQL with Prisma under Electron

My Main goal is to create an Electron App (Windows) that locally stores data in an SQLite Database. And because of type safety I choose to use the Prisma framework instead of other SQLite Frameworks.
I took this Electron Sample Project and now try to include Prisma. Depending on what I try different problems do arrise.
1. PrismaClient is unable to be run in the Browser
I executed npx prisma generate and then try to execute this function via a button:
import { PrismaClient } from '#prisma/client';
onSqlTestAction(): void {
const prisma = new PrismaClient();
const newTestObject = prisma.testTable.create(
{
data: {
value: "TestValue"
}
}
);
}
When executing this in Electron I get this:
core.js:6456 ERROR Error: PrismaClient is unable to be run in the browser.
In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues
at new PrismaClient (index-browser.js:93)
at HomeComponent.onSqlTestAction (home.component.ts:19)
at HomeComponent_Template_button_click_7_listener (template.html:7)
at executeListenerWithErrorHandling (core.js:15281)
at wrapListenerIn_markDirtyAndPreventDefault (core.js:15319)
at HTMLButtonElement.<anonymous> (platform-browser.js:568)
at ZoneDelegate.invokeTask (zone.js:406)
at Object.onInvokeTask (core.js:28666)
at ZoneDelegate.invokeTask (zone.js:405)
at Zone.runTask (zone.js:178)
It somehow seems logical that Prisma cannot run in a browser. But I actually build a native app - with Electron that embeds a Browser. It seems to be a loophole.
2. BREAKING CHANGE: webpack < 5 used to include polyfills
So i found this Question: How to use Prisma with Electron
Seemed to be exactly what I looked for. But the error message is different (Debian binaries were not found).
The solution provided is to generate the prisma artifacts into the src folder instead of node_modules - and this leads to 19 polyfills errors. One for example:
./src/database/generated/index.js:20:11-26 - Error: Module not found: Error: Can't resolve 'path' in '[PATH_TO_MY_PROJECT]\src\database\generated'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
And this repeats with 18 other modules. Since the error message to begin with was different I also doubt that this is the way to go.
I finally figured this out. What I needed to understand was, that all Electron apps consist of 2 parts: The Frontend Webapp (running in embedded Chromium) and a Node backend server. Those 2 parts are called IPC Main and IPC Renderer and they can communicate with each other. And since Prisma can only run on the main process which is the backend I had to send my SQL actions to the Electron backend and execute them there.
My minimal example
In the frontend (I use Angular)
// This refers to the node_modules folder of the Electron Backend, the folder where the main.ts file is located.
// I just use this import so that I can use the prisma generated classes for type safety.
import { TestTable } from '../../../app/node_modules/.prisma/client';
// Button action
onSqlTestAction(): void {
this.electronService.ipcRenderer.invoke("prisma-channel", 'Test input').then((value) => {
const testObject: TestTable = JSON.parse(value);
console.log(testObject);
});
The sample project I used already had this service to provide the IPC Renderer:
#Injectable({
providedIn: 'root'
})
export class ElectronService {
ipcRenderer: typeof ipcRenderer;
webFrame: typeof webFrame;
remote: typeof remote;
childProcess: typeof childProcess;
fs: typeof fs;
get isElectron(): boolean {
return !!(window && window.process && window.process.type);
}
constructor() {
// Conditional imports
if (this.isElectron) {
this.ipcRenderer = window.require('electron').ipcRenderer;
this.webFrame = window.require('electron').webFrame;
this.childProcess = window.require('child_process');
this.fs = window.require('fs');
// If you want to use a NodeJS 3rd party deps in Renderer process (like #electron/remote),
// it must be declared in dependencies of both package.json (in root and app folders)
// If you want to use remote object in renderer process, please set enableRemoteModule to true in main.ts
this.remote = window.require('#electron/remote');
}
}
And then in the Electron backend I first added "#prisma/client": "^3.0.1" to the package.json (for the Electron backend not the frontend). Then I added to the main.ts this function to handle the requests from the renderer:
// main.ts
ipcMain.handle("prisma-channel", async (event, args) => {
const prisma = new PrismaClient();
await prisma.testTable.create(
{
data: {
value: args
}
}
);
const readValue = await prisma.testTable.findMany();
return JSON.stringify(readValue);
})
This way of simply adding the IPC Main handler in the main.ts file of course is a big code smell but usefull as minimal example. I think I will move on with the achitecture concept presented in this article.

How to serve next js app using keystonejs?

I have a problem with serving next js app using keystonejs. I want to achive something similar like in to do nuxt example which you can choose while creating keystone project. I used code from this link https://www.keystonejs.com/keystonejs/app-next/ in index.js file, but I get an error while trying to run the app:
ReferenceError: distDir is not defined at Object.
What should I do?
this is what I have in my setup
create app folder (next js code)
create next.config.js in app folder
my next.js.config file has following code
const distDir = 'dist';
module.exports = {
distDir: `../${distDir}/www`,
env: {
SERVER_URL: process.env.SERVER_URL || 'http://localhost:4000',
},
publicRuntimeConfig: {
// Will be available on both server and client
// staticFolder: '/static',
},
};
it export the build artifacts in dist/www in root of keystone project.

How to prevent loading Sharp module on Meteor client?

I used npm pkg Sharp on server's picture collection to transform imgs. The server code is like this:
import * as sharp from 'sharp';
export const Pictures = new Mongo.Collection('pictures');
export const PicturesStore = new UploadFS.store.GridFS({
collection: Pictures,
name: 'pictures',
filter: new UploadFS.Filter({
contentTypes: [ 'image/*' ],
}),
transformWrite(from, to, fileId, file) {
const transform = sharp().resize(300, 300).min().crop().toFormat('jpeg', { quality });
from.pipe(transform).pipe(to);
},
})
However on the client, it reports error:
cannot load native .node modules on the client.
The client doesn't run sharp functions actually. It only refers to PicturesStore and also create a minimongo collection for Pictures.
In another project, it uses webpack on the client. It can be configured to resolve sharp with an empty dummy object.
But how to create an empty dummy Sharp object to prevent loading Sharp module on Meteor client without webpack?
It turns out you have to write a Meteor package to define different files loaded on client and server. In you package.js, it's like this:
Package.onUse(function (api) {
api.mainModule('sharp-client.js', 'client');
api.mainModule('sharp-server.js', 'server');
});
In sharp-client.js, it's like this:
export var Sharp = {};
In sharp-server.js, it's like this:
import {
checkNpmVersions
} from 'meteor/tmeasday:check-npm-versions';
checkNpmVersions({
'sharp': '^0.20.5'
}, 'my:awesome-package');
export var Sharp = require('sharp');
done.

Where to put native code for push notifications with new Meteor Mobile platform

After reviewing the new Mobile features with latest 1.0 version of Meteor, I'm not seeing where I would modify the Cordova code to add custom capabilities. For instance, I want to implement push notifications for my application on both iOS and Android. In both cases I would need to write some native code so that I could get devices registered and accept push notification messages.
Currently, I'm using MeteorRider to accomplish this and it works great. I have 3 separate projects for Meteor, Android and iOS. In the latter 2, I put the native code there necessary to accomplish this. One thing is for certain, you have to update the bootstrap classes in Cordova to allow registrations to work.
In Meteor 1.0, how would I go about accomplishing this with the out-of-the-box mobile feature?
Here's the objective-C code for accepting push notification registration responses that is required in Cordova's AppDelegate:
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog( #"Device token is: %#", deviceToken);
// Convert to string that can be stored in DB
NSString *regId = [[deviceToken description] stringByReplacingOccurrencesOfString:#"<" withString:#""];
regId = [regId stringByReplacingOccurrencesOfString:#">" withString:#""];
regId = [regId stringByReplacingOccurrencesOfString: #" " withString: #""];
[[ApplePushNotificationService sharedInstance] application:application uploadDeviceToken:regId];
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
NSLog(#"Failed to get token, error: %#", error);
}
TL;DR : The cordova project is in the .meteor/local/cordova-build subfolder.
The default AppDelegate.m gets created in the .meteor/local/cordova-build/platforms/ios/***YOUR_APP_NAME***/Classes subfolder.
If you add a top-level folder called cordova-build-override to your meteor project, the directory tree that it contains will be added to the .meteor/local/cordova-build folder just before the build and compilation step.
So, put your custom AppDelegate.m in a new folder called cordova-build-override/platforms/ios/***YOUR_APP_NAME***/Classes .
mkdir -p cordova-build-override/platforms/ios/foo/Classes
cp .meteor/local/cordova-build/platforms/ios/foo/Classes/AppDelegate.m cordova-build-override/platforms/ios/foo/Classes
The Meteor-Cordova integration page on the GitHub Meteor wiki is the best place (so far) to find the details of cordova development with meteor.
You put your cordova-specific code in plain javascript. It's best not to modify the native code if at all possible; instead, see if you can write your own cordova plugin and use it from your meteor app. The cordova PushPlugin plugin might do what you're looking for, but if not, you can use it as a reference.
This example below will create a new iOS app that uses a non-meteor cordova plugin, from scratch.
NOTE: This is a bare minimum example. Look at the meteor cordova camera plugin for a full example. The code below is based on that plugin.
# create a meteor app foo
meteor create foo
cd foo
# add the iOS cordova platform
meteor add-platform ios
# create a new meteor package, foo-camera.
# NOTE: You need to substitute your own meteor.com developer ID here
meteor create --package your_meteor_developer_id:foo-camera
Now, edit the packages/your_meteor_developer_id:foo-camera/package.js file to add the following:
// Add the camera cordova plugin at version 0.3.3
Cordova.depends({
'org.apache.cordova.camera': '0.3.3'
});
EDIT 1: This causes the plugin to be downloaded to your cordova plugins folder.
You can refer to a git tarball instead of a version number e.g. :
Cordova.depends({
'com.phonegap.plugins.facebookconnect': 'https://github.com/Wizcorp/phonegap-facebook-plugin/tarball/0e61babb65bc1716b957b6294c7fdef3ce6ace79'
});
source: meteor cordova wiki
While we're at it, limit our code to run only on the client, and export our FooCamera object so it can be used in the rest of our meteor javascript:
Package.onUse(function(api) {
api.versionsFrom('1.0');
api.export('FooCamera');
api.addFiles('your_meteor_developer_id:foo-camera.js','client');
});
Edit 2:
If your cordova plugin needs special configuration, you can define this in your meteor app's
mobile configuration file. It will get copied into
your app's config.xml .
E.g.
// ===== mobile-config.js ======
// Set PhoneGap/Cordova preferences
App.setPreference('SOME_SPECIFIC_PLUGIN_KEY','SOME_SPECIFIC_PLUGIN_VAL');
Your app's config.xml will then eventually result in the following:
<preference name="SOME_SPECIFIC_PLUGIN_KEY" value="SOME_SPECIFIC_PLUGIN_VAL"/>
Next, edit the JavaScript file in your package ( packages/your_meteor_developer_id:foo-camera/your_meteor_developer_id:foo-camera.js ) to expose the cordova functionality in a meteor-like manner. Use the official meteor mobile package examples as a reference.
(the code below is stolen shamelessly from the meteor github repo ) :
FooCamera = {};
FooCamera.getPicture = function (options, callback) {
// if options are not passed
if (! callback) {
callback = options;
options = {};
}
var success = function (data) {
callback(null, "data:image/jpeg;base64," + data);
};
var failure = function (error) {
callback(new Meteor.Error("cordovaError", error));
};
// call the cordova plugin here, and pass the result to our callback.
navigator.camera.getPicture(success, failure,
_.extend(options, {
quality: options.quality || 49,
targetWidth: options.width || 640,
targetHeight: options.height || 480,
destinationType: Camera.DestinationType.DATA_URL
})
);
};
Now, add your new (local) package to your meteor app.
meteor add your_meteor_developer_id:foo-camera
Edit your application's main HTML and JS to use your new meteor package.
In your foo.html , replace the hello template with this:
<template name="hello">
<button>Take a Photo</button>
{{#if photo}}
<div>
<img src={{photo}} />
</div>
{{/if}}
</template>
In your foo.js , replace the button click event handler with this:
Template.hello.helpers({
photo: function () {
return Session.get("photo");
}
});
Template.hello.events({
'click button': function () {
var cameraOptions = {
width: 800,
height: 600
};
FooCamera.getPicture(cameraOptions, function (error, data) {
Session.set("photo", data);
});
}
});
Now, plug your device in, make sure it's on the same network as your computer, and start both the meteor server and the ios app.
meteor run ios-device
# If you want to just use the emulator, use the following instead.
# but of course the camera won't work on the emulator.
#meteor run ios
XCode will open. You may need to set up your certificates and provisioning profiles before running your app (from XCode).
In another terminal, tail the logs:
tail -f .meteor/local/cordova-build/platforms/ios/cordova/console.log
Finally, publish your excellent meteor cordova plugin so that everyone else can use it. Edit package.js as per the meteor docs. Then:
cd packages/your_meteor_developer_id\:foo-camera
meteor publish

Resources