I am trying to disable all keyboard shortcuts in my electron.js app.
I tried the following ways (spoiler alert: they didn't work):
globalShortcut.unregisterAll()
and
globalShortcut.register('Alt+CommandOrControl+A', () => {
console.log('not allowed')
})
globalShortcut.register('Alt+CommandOrControl+B', () => {
console.log('not allowed')
})
globalShortcut.register('Alt+CommandOrControl+C', () => {
console.log('not allowed')
})
globalShortcut.register('Alt+CommandOrControl+D', () => {
console.log('not allowed') // and so on
})
(i did this ^ for all the keys (from A to Z, 1 to 9, etc).
By the way, all of the code I tried I put into the app.whenReady() function.
Well, none of this worked. I saw a lot of articles with other more abstract ways, but they didn't work either.
I actually tried searching for an npm package too, but I didn't find any that would solve my problem.
I just need to completely disable all keyboard shortcuts from my electron app. Is there any other way (that actually works)?
Here is simple solution ::
app.on('browser-window-focus', function () {
globalShortcut.register("CommandOrControl+R", () => {
console.log("CommandOrControl+R is pressed: Shortcut Disabled");
});
globalShortcut.register("F5", () => {
console.log("F5 is pressed: Shortcut Disabled");
});
});
app.on('browser-window-blur', function () {
globalShortcut.unregister('CommandOrControl+R');
globalShortcut.unregister('F5');
});
You can disable all the shortcuts by registering while the window is focused and unregister when blur.
See this question right here Disable reload via keyboard shortcut electron app
EDIT : disable all shortcuts globalShortcut.registerAll(accelerators, callback)
accelerators String[] - an array of Accelerators.
callback Function
Registers a global shortcut of all accelerator items in accelerators. The callback is called when any of the registered shortcuts are pressed by the user.
globalShortcut.unregisterAll()
Unregisters all of the global shortcuts.
Related
I am trying to intercept some requests in cypress following the click of a button, and I am trying to check if requests were or not made to a list of urls. I used a variable ( isRequestCalled ) as you can see, to update it if the requests were made, but the updates don't seem to be reach the test. When a request is intercepted, the value of isRequestCalled is updated in that scope but it doesn't seem to be visible in the scope of the test. Has anyone encoutered this type of issue, I appreciate all suggestions. Thanks!
describe('E2E test', () => {
let isRequestCalled
beforeEach(() => {
isRequestCalled=false
cy.origin(Cypress.env('ORIGIN_URL'), () => {
localStorage.clear();
})
cy.wait(1000);
cy.visit(Cypress.env('BASE_URL'));
cy.intercept('*', (req) => {
isRequestCalled=Cypress.env("REQUEST_URLS").some((url)=>{
return req.url.includes(url)
}) || isRequestCalled
// cy.wrap(isRequestCalled).as('isRequestCalled')
if (isRequestCalled) {
req.alias = 'apiRequests'
}
}
)
})
it('Consent test: Deny', () => {
cy.get(`[data-testid='${Cypress.env('BANNER_TEST_ID')}']`).should('be.visible').log("Banner visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).should('be.visible').log("Button 1 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).should('be.visible').log("Button 2 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).click();
cy.reload();
// cy.get('#isRequestCalled').then(isRequestCalled => {
// console.log(isRequestCalled)
// expect(isRequestCalled).to.eq(false)
// })
cy.wrap(isRequestCalled).should('eq',false)
});
it('Consent test: Allow', () => {
cy.get(`[data-testid='${Cypress.env('BANNER_TEST_ID')}']`).should('be.visible').log("Banner visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).should('be.visible').log("Button 1 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).should('be.visible').log("Button 2 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).click();
cy.wait('#apiRequests')
});
});
When using cy.intercept() you actually have no need for any variable counters if you want to ensure that the intercepted call has been made. I would recommend aliasing all of the requests you want to intercept and simply waiting (cy.wait()) for them after you perform the action that should trigger the call
describe('E2E test', () => {
beforeEach(() => {
cy.intercept('firstRequestUrl').as('firstCall');
cy.intercept('secondRequestUrl').as('secondCall')
cy.intercept('thirdRequestUrl').as('thirdCall')
cy.origin(Cypress.env('ORIGIN_URL'), () => {
localStorage.clear();
})
cy.wait(1000); // look into removing that as it looks kinda redundant
cy.visit(Cypress.env('BASE_URL'));
})
it('Does smth', () => {
cy.wait('#firstCall')
cy.get('#secondCallTrigger').click()
cy.wait('#secondCall')
})
it('Does smth else', () => {
cy.get('#thirdCallTrigger').click()
cy.wait('#thirdCall')
})
})
P.S.Keep in mind that js/ts variables in Cypress are very tricky to use between contexts, when you declare something in the describe, hooks, or a different it, you can't easily reach it from another it block.
People use env variables or hacks like this for that, although as you can see there is a native functionality for it.
Be sure to check the rest of intercept documentation and aliases documentation for more information.
It could be that the cy.reload() is resetting isRequestCalled.
If any commands such as visit or reload causes the browser to reset, you will notice the browser clearing and it's a sure sign that variables set up globally such as isRequestCalled are also cleared.
I can't really tell the intention of some of the commands, but perhaps you could test the value before the reload.
cy.wrap(isRequestCalled).should('eq',false)
cy.reload()
Also, what is the trigger for requests intercepted by Cypress.env("REQUEST_URLS")?
If it's cy.visit(Cypress.env('BASE_URL')) then the intercept needs to come before the visit.
If it's cy.get(```[data-testid='${Cypress.env("BUTTON1_TESTID")}']```).click() then the ordering is ok.
I'm learning how to build chrome extensions with manifest v3, what I'm trying to do is the following
In my extension background.js (service worker) I want to do this:
connect to WebSocket to get data updates
reconnect to the Websocket when service-worker wake up
those are tasks to get data updates from a WebSocket and update the badge text and send notifications.
I need to do these tasks while not relying on having a port open with the popup or a content script.
I'm using Chrome Alarms to wake up the service worker
it may sound weird that I have to reconnect every time the service worker wakes up considering chrome is shutting the service worker down like every 15s or less once I close the extensions dev tools (which makes me cry blood) but it is better than sending XHR periodically using Chrome alarms, which would result in a lot more requests being sent to an API, so reconnecting to the Websocket seems less problematic.
I'm having a super hard time debugging my service worker (background script) in my chrome extension. The problem is when I have dev tools open the service worker will NEVER go inactive, and what I'm trying to do is identify when the SW wakes up to perform tasks, super-duper weird because I need dev tools open to debugging...
how do you debug an extension SW without devtools open?
do you/anyone reading this have any recommendations/thoughts on what I want to do with this extension and the pain process for debugging the SW?
here is the code I have for the background.js
const extension = {
count: 0,
disconnected: false,
port: {} as any,
ws: null,
};
const fetchData = () => {
return fetch(
'https://api.coingecko.com/api/v3/coins/ethereum?localization=incididuntelit&tickers=false&market_data=true&community_data=true&developer_data=true&sparkline=true'
).then((res) => res.json());
};
// Chrome Alarms
const setupAlarms = () => {
console.log('###ALARMS-SETUP');
chrome.alarms.get('data-fetch', (alarm) => {
if (!alarm) {
chrome.alarms.create('data-fetch', { periodInMinutes: 0.1 });
}
});
chrome.alarms.get('client-pinging-server', (alarm) => {
if (!alarm) {
chrome.alarms.create('client-pinging-server', { periodInMinutes: 0.1 });
}
});
chrome.alarms.onAlarm.addListener((e) => {
if (e.name === 'data-fetch') {
fetchData()
.then((res) => {
// console.log('###ETHEREUM:', res.market_data.current_price.cad);
chrome.action.setBadgeText({ text: `${Math.round(Math.random() * 100)}` });
})
.catch((error) => console.error('###ERROR', error));
}
if (e.name === 'client-pinging-server') {
if (!extension.ws || !extension.ws.getInstance()) {
console.log('\n');
console.log('###reconnecting...', extension.ws);
console.log('\n');
extension.ws = WebSocketClient();
extension.ws.connect();
}
if (extension.ws.getInstance()) {
console.log('###sending-client-ping');
extension.ws.getInstance().send(JSON.stringify({ message: 'client ping - keep alive' }));
}
}
});
};
// ON INSTALL
chrome.runtime.onInstalled.addListener(async (e) => {
const API_URL = 'ws://localhost:8080';
chrome.storage.local.set({ apiUrl: API_URL, count: 0 });
console.info('###Extension installed', e);
chrome.action.setBadgeText({ text: '0' });
chrome.action.setBadgeBackgroundColor({ color: '#FF9900' });
});
// ON SUSPEND
chrome.runtime.onSuspend.addListener(() => {
console.log('Unloading.');
chrome.action.setBadgeText({ text: `off` });
});
function WebSocketClient() {
let instance = null;
const connect = () => {
return new Promise((resolve, reject) => {
const ws = new WebSocket('ws://localhost:8080');
const onOpen = () => {
instance = ws;
console.log('###websocket:connected', instance);
return resolve(ws);
};
const onError = (event) => {
console.log('###INIT-FAILED', event);
ws.close(1000, 'closing due to unknown error');
return reject('failed to connect to websocket');
};
const onClose = () => {
console.log('###websocket:disconnected');
instance = null;
// reconnect is happening in the alarm callback
};
ws.onopen = onOpen;
ws.onerror = onError;
ws.onclose = onClose;
});
};
const getInstance = () => {
return instance;
};
return {
connect,
getInstance,
};
}
self.addEventListener('install', async (event) => {
console.log('====install', event);
chrome.action.setBadgeBackgroundColor({ color: '#a6e22e' });
});
self.addEventListener('activate', async (event) => {
console.log('====activate', event);
chrome.action.setBadgeBackgroundColor({ color: '#FF9900' });
extension.ws = WebSocketClient();
extension.ws.connect();
setupAlarms();
});
self.addEventListener('push', function (event) {
// Keep the service worker alive until the notification is created.
event.waitUntil(
self.registration.showNotification('Testing PUSH API', {
body: 'coming from push event',
})
);
});
Since Devtools can attach to multiple contexts at once, you can open it for another context so the SW will be secondary and thus will be able to unload normally.
Debugging
Open any visible page of the extension or, if there are none, its manifest.json URL:
chrome-extension://ID/manifest.json where ID is the extension's id
Open devtools and switch to its Application tab, then choose Service worker on the left.
Click start (if shown) to start the service worker, click the background script name to open it in the Sources panel, set breakpoints, etc.
Click stop to stop the service worker, optionally click Update at the top, and skip waiting in the middle (if shown) to force an update.
Click start again - your breakpoints will trigger.
Performance profiling
Open any visible page of the extension or, if there are none, its manifest.json URL:
chrome-extension://ID/manifest.json where ID is the extension's id
Open devtools and switch to its Application tab, then choose Service worker on the left.
Duplicate the tab, open devtools there, go to Performance tab, click "Start" or press Ctrl-E
Switch back to the first tab and click the start button (or stop first, then start). In certain cases you may also see skip waiting in the middle, click it then.
Switch to the second tab, wait for a while and click the recording button again or press Ctrl-E.
Notes
When the service worker is started you can see its context in the Sources panel on the left (in the files panel), on the top-right (in the threads panel), in the console toolbar (the context selector).
This may seem unwieldy at first, but once you try and get the knack of it, it's quite trivial and can even beat devtools that's shown when clicking the "service worker" link in chrome://extensions page because this one a) shows extension's localStorage/IndexedDB in devtools, b) provides control over service worker lifetime/execution, c) supports performance profiling of SW startup.
Note that the ManifestV3 documentation's claims about benefits provided by service workers for extensions are largely exaggerated or completely false, e.g. in your case it's clear that restarting the service worker is bad, so you should use a port to prolong the SW's lifetime as much as possible.
I would like to disable shortcut "CTRL+A" on specific window,
Here is my current code based on Electron docs
electronLocalshortcut.register(app..main, 'Ctrl+A', () => {
console.log('prevent ctrl+a');
});
I'm able to catch "CTRL+A" event but I'm not able to prevent select all effect it still select all items on the page, app bar etc
In your renderer (window) process, add a keydown listener, it will allow you to prevent any shortcuts you want:
document.addEventListener('keydown', keyDownHandler)
function keyDownHandler (event) {
if (event.ctrlKey && event.code === 'KeyA') {
event.preventDefault()
}
}
I'm making an electron desktop app. I want to disable windows key and function keys while the app is on
I tried using the following code ... it registers the event but the windows menu opens anyways
$(document).keydown(function(e){
if (e.keyCode == 37) {
alert( "windows key pressed" );
return false;
}
});
Any help?
You can try this, but unforunately it will become a global shortcut, meaning when the window doesn't have focus it will still be registered. Try putting a console.log() to see when it fires. win is your electron window variable
const {app, globalShortcut} = require('electron');
win = new BrowserWindow();
globalShortcut.register('Super', () => {
if (win.isFocused()) {
// do something
}
});
You can check the docs here: docs
Or try to use this module here: electron-localshortcut
electronLocalshortcut.register(win, 'Super', () => {
console.log('Windows Button pressed');
return false;
});
I'm not using Node as a server; I'm using it for CLI apps. This one though needs to run in the background and listen for global key press events (without having focus).
I don't see any obvious way of doing this in Node. What are my options?
Looks like you need global hook for all keyboard events.
You can try iohook module. Using is pretty simple:
const ioHook = require('iohook');
ioHook.on("keypress", event => {
console.log(event);
// {keychar: 'f', keycode: 19, rawcode: 15, type: 'keypress'}
});
ioHook.start();
I just do it with iohook. You could do something like this...
const ioHook = require('./node_modules/iohook-master/index.js');
ioHook.on("keyup",function(keyPress){
if(keyPress.keycode == CTRLIZQ){
//do something
}
});
ioHook.start();
Seems like a combination of daemon and keypress could do you want. I've only ever used keypress in a node script, not a daemon, so I have no idea if it will work the same way. But it might! At the very worst, you'll discover one solution that doesn't solve your problem.
I said use keypress in a comment above, but seeing the lib is 8 years old and abandoned, someone wrote the following as a replacement of the lib, it just does exactly the same thing, and yet it's a nodejs internal dependency.
let readline = require('readline');
readline.emitKeypressEvents(process.stdin);
process.stdin.on('keypress', (ch, key) => {
console.log('got "keypress"', ch, key);
if (key && key.ctrl && key.name == 'c') {
console.log('ctrl+c was pressed');
// do something usefull
}
});
process.stdin.setRawMode(true);
process.stdin.resume();
I tried ioHook, But it was not working, I guess because of the mac m1 chip.
I found another package, It is working as expected: https://www.npmjs.com/package/gkm
I also found one simple app, you can run on the terminal: https://github.com/KleoPetroff/node-global-key-listener
gkm works on Linux
https://www.npmjs.com/package/gkm
const gkm = require('gkm');
gkm.events.on('key.*', data => {
const button = data[0];
if (button === 'Escape') {
console.log('Escaped');
process.exit();
}
});