In an electron app, I need to save Map object in local storage.
I tried this:
// main.js
app.on('ready', createWindow);
const createWindow = ) => {
mainBrowserWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});
const testMap = new Map([
['word1', 1],
['word2', 2],
['word3', 3],
]);
const jsonTestMap = JSON.stringify(Array.from(testMap.entries()));
mainBrowserWindow.webContents
.executeJavaScript(`localStorage.setItem("testMap",${jsonTestMap});`)
.then(() => {
console.log('OK localStorage.setItem()');
mainBrowserWindow.webContents
.executeJavaScript('localStorage.getItem("testMap");')
.then((val) => {
console.log(val);
// const res = new Map(JSON.parse(val));
});
});
};
but this store testmap as
'word1,1,word2,2,word3,3'
instead of
'[["word1",1],["word2",2],["word3",3]]'
which makes it not possible to make a JSON.parse with localStorage.getItem("testMap")
Could someone please help me and let me know what is the mistake I am making in the above.
Find it with :
mainBrowserWindow.webContents
.executeJavaScript(`localStorage.setItem("testMap",'${jsonTestMap}');`)
.then(() => { ... }
Related
I have created a small React app and I want to test it using Playwright component testing
I have 3 components: App -> ChildComponent -> ChildChildComponent
I want to render (mount) the ChildComponent directly, and make assertions on it, but when I do that, some ContextApi functions that are defined in the App in the normal flow, are now undefined as the App component is not part of the component test.
So i'v trying to render the ChildComponent together with a face ContextApi Provider and pass mocks of those undefined functions, and then I get an infinite render loop for some reason.
How can I go about this, as this use case is typical in react component test.
Here is the test with all my failed mocking attempts separated:
test.only("validate CharacterModal", async ({ page, mount }) => {
const data = ['some-mocked-irrelevant-data']
// const setCurrentCharacter = () => {};
// const setIsCharacterModalOpen = () => {};
// const setCurrentCharacterMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setIsCharacterModalOpenMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setCurrentCharacter = jest.fn();
// const setIsCharacterModalOpen = jest.fn();
// const setCurrentCharacter = (): void => {};
// const setIsCharacterModalOpen = (): void => {};
// const setIsCharacterModalOpen = (isCharacterModalOpen: boolean): void => {};
const AppContext = React.createContext<any>(null);
await page.route("**/users*", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(data),
});
});
const component = await mount(
<AppContext.Provider value={{ setCurrentCharacterMocked, setIsCharacterModalOpenMocked }}>
<CharacterModal />
</AppContext.Provider>
);
expect(await component.getByRole("img").count()).toEqual(4);
});
The beforeMount hook can be used for this. I recently added docs about this: https://github.com/microsoft/playwright/pull/20593/files.
// playwright/index.jsx
import { beforeMount, afterMount } from '#playwright/experimental-ct-react/hooks';
// NOTE: It's probably better to use a real context
const AppContext = React.createContext(null);
beforeMount(async ({ App, hooksConfig }) => {
if (hooksConfig?.overrides) {
return (
<AppContext.Provider value={hooksConfig.overrides}>
<App />
</AppContext.Provider>
);
}
});
// src/CharacterModal.test.jsx
import { test, expect } from '#playwright/experimental-ct-react';
import { CharacterModal } from './CharacterModal';
test('configure context through hooks config', async ({ page, mount }) => {
const component = await mount(<CharacterModal />, {
hooksConfig: { overrides: 'this is given to the context' },
});
});
const [image, setImage] = useState(null);
const pickImage = async () => {
// No permissions request is necessary for launching the image library
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result.uri);
if (!result.cancelled) {
setImage(result);
}
};
I study React-Native and Django and I know about FormData, but I don't understand how put into my code.
Try the below code
const pickImage = async () => {
// No permissions request is necessary for launching the image library
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result.uri);
if (!result.cancelled) {
postToServer(result)// must be a object not array, if its array find first element if selecting only one image
setImage(result);
}
};
//just function to upload to server
const postToServer = (img)=>{
const formData = new FormData();
formData.append('photo', {
uri: img.uri,
type: 'image/jpeg', // or photo.type
name: 'testPhotoName'
});
const options = {
method: 'POST',
body: formData,
// If you add this, upload won't work
// headers: {
// 'Content-Type': 'multipart/form-data',
// }
};
fetch('your-upload-url', options);
}
Is it possible to define a browser with Javascript disabled to simulate how a crawler would view a page? There should be an option that can be passed.
You can pass javaScriptEnabled in the BrowserContext options:
const playwright = require("playwright");
(async () => {
const browser = await playwright.chromium.launch();
const context = await browser.newContext({
javaScriptEnabled: false
});
const page = await context.newPage();
// ...
await browser.close();
})();
In the case of #playwright/test (where you don't define browser.newContext yourself) you can set javaScriptEnabled in testOptions.
(1) In the playwright.config.js file:
const config = {
use: {
headless: false,
javaScriptEnabled: false
},
};
module.exports = config;
(2) or with test.use:
const { test, expect } = require('#playwright/test');
test.use({ javaScriptEnabled: false });
test('website without JavaScript', async ({ page }) => {
// ...
});
I have implemented this class in workbox 2, now I have upgraded to version 3 but workbox.runtimeCaching.Handler is deprecated.
Can someone help me on how to develop it in workbox 3?*
importScripts('workbox-sw.prod.v2.1.2.js');
importScripts('workbox-runtime-caching.prod.v2.0.3.js');
importScripts('workbox-cache-expiration.prod.v2.0.3.js');
const workboxSW = new self.WorkboxSW();
class AlwaysNetworkWithCacheUpdateHandler extends workbox.runtimeCaching.Handler{
setCacheOptions(cacheOptions){
this.cacheOptions = cacheOptions;
}
handle({event}){
let requestWrapper = new workbox.runtimeCaching.RequestWrapper({
cacheName: this.cacheOptions.cacheName,
plugins:[
new workbox.cacheExpiration.CacheExpirationPlugin(this.cacheOptions.expirationOptions)
]
});
return (
requestWrapper
.fetchAndCache({
request: event.request,
waitOnCache: true
})
);
}
}
I'm not sure what you are going to reach, however, I used runtimeCaching for third-party requests (CDN), so now it is handling by regular way:
https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests
Strategies now do the job of RequestWrapper, choose one and use like so:
const strategy = workbox.strategies.networkFirst({
cacheName,
plugins: [
new workbox.expiration.Plugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 *7,
})
],
});
const handler = async ({event}) => {
const request = new Request(event.request, {
mode: 'cors',
credentials: 'omit',
});
const cachedResponse = await caches.match(request, {
cacheName,
});
return cachedResponse || strategy.makeRequest({
event,
request,
});
}
router.registerRoute(
({ url }) => url.origin === 'http://example.com',
handler,
)
Example comes directly from this issue
I am going through the Electron Fundamentals course on Pluralsight (Trent, 2016). I can't get the accelerator to work on my "Quit" menu item. Below is my entire main.js file. The menu is created successfully from what I can tell (picture below), and clicking directly on the Quit menu item does shut down the application, but the Alt+W key combination does not. I am on Windows 10. What am I missing?
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
app.on('ready', _ => {
new BrowserWindow();
const template = [
{
label: "File",
submenu: [{
label: 'About',
click: _ => {
console.log('clicked');
}
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Alt+W',
click: _ => {
app.quit();
}
}]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
});
The accelerator String is no longer supported. The documentation was updated in v1.4.5 to clarify how to define shortcuts using globalShortcut.
Accelerator Documentation: Shortcuts are registered with the globalShortcut module using the register method, i.e.
const {app, globalShortcut} = require('electron')
app.on('ready', () => {
// Register a 'CommandOrControl+Y' shortcut listener.
globalShortcut.register('CommandOrControl+Y', () => {
// Do stuff when Y and either Command/Control is pressed.
})
})
So change your code to this
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
const globalShortcut = electron.globalShortcut;
app.on('ready', _ => {
new BrowserWindow();
// Declare shortcuts
globalShortcut.register('Alt+W', () => app.quit());
const template = [
{
label: "File",
submenu: [{
label: 'About',
click: _ => {
console.log('clicked');
}
},
{
type: 'separator'
},
{
label: 'Quit',
click: _ => {
app.quit();
}
}]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
});
looks like on Windows it needs something loaded on the BrowserWindow. Placing the loadURL make it work. Need to try something besides loadURL though..
app.on('ready', _ => {
mainWindow = new BrowserWindow()
mainWindow.loadURL('https://github.com')
const name = electron.app.getName()
const template = [
{
label: name,
submenu: [{
label: `About ${name}` ,
click: console.log('clicked!')
},{
type:'separator'
},{
label:'Quit',
click: _ =>{
app.quit()
},
accelerator:'CmdOrCtrl+Q'
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
})