question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[BUG] ipcRenderer SendSync returning null / undefined - Electron

See original GitHub issue

Context:

  • Playwright Version: 1.16.2
  • Operating System: macOS BigSur 11.6
  • Node.js version: v16.11.1
  • Browser: Electron v12.0.18 || v12.2.2 || v13.6.1
  • Build tools: Webpack v4.46.0 and Nuxt v2.14.12

Code Snippet

import path from 'path';
import { ElectronApplication, _electron, Page } from 'playwright';
import { test, expect } from '@playwright/test';

/**
 * Using test.describe.serial make the test execute step by step, as described on each `test()` order
 * Playwright executes test in parallel by default and it will not work for our app backend loading process.
 * */

test.describe.serial('POC Playwright - RD, () => {
  let page: Page;
  let electronApp: ElectronApplication;
  const mainTitleSelector = '[data-test="mainTitle"]';

  test.beforeAll(async({ browser }) => {
    electronApp = await _electron.launch({ args: [path.join(__dirname, '../')] });
    const appPath = await electronApp.evaluate(async({ app }) => {
      return await app.getAppPath();
    });

    app = await browser.newPage();
    console.log('Log from appPath ---> ', appPath);
  });

  test.afterAll(async() => {
    await electronApp.close();
  });

  test('should open the main app', async() => {
    page = await electronApp.firstWindow();
    await page.waitForSelector('.progress', { state: 'visible' });
    await delay(20000); // Wait a bit

    const versionApp = await app.$eval('.versionInfo', el => el.textContent);

    expect(versionApp).toBe('Version: (checking...)');
  });

  test('should get General page content', async() => {
    const generalGreetings = await page.$eval(mainTitleSelector, el => el.textContent.trim());

    expect(generalGreetings).toBe('Welcome to RD');
  });

  test('should navigate to K Settings', async() => {
    const kVersionDndSelector = '.labeled-input';
    const kMemorySliderSelector = '[id="memoryInGBWrapper"]';

    try {
      page.click(`.nav li[item="/Page2"] a`);
      await page.waitForSelector('.contents');
    } catch (err) {
      console.log('Error during K Settings navigation. Error --> ', err);
    }

    const kSettingsTitle = await page.$eval(mainTitleSelector, el => el.textContent.trim());
    const kVersionDnd = await page.$eval(kVersionDndSelector, el => el.textContent.trim());

    expect(k8sVersionDnd).toBe('K version');
    expect(k8sSettingsTitle).toBe('K Settings');
  });
});

function delay(time: number | undefined) {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
  });
}

Create Window config

 createWindow('preferences', `${ webRoot }/index.html`, {
    width:          940,
    height:         600,
    webPreferences: {
      devTools:           !app.isPackaged,
      nodeIntegration:    true,
      contextIsolation:   false,
      enableRemoteModule: process.env?.NODE_ENV === 'test'
    },
  });

Log playwight_debug_ipcnull.log

Describe the bug

There’s intermittence during the E2E tests where the main app hangs during the ipcRenderer.sendSync() process, returning Cannot read property '[item]' of null. It happens quite often, I’d say 70% of the time, executing it through playwright. The weird behaviour is: running it on debug mode PWDEBUG=1 (without any page.pause()), the error does not shows up on the same frequency. Tried to include some delays after the app start the first page, but it does not work. I thought it could be related with some racing conditions between the app vs playwright or something like that. I haven’t seen any output from the logs that could assist to find out what’s going on 😕

This is the first time running E2E testing on Electron + Playwright.

Any thoughts will be very appreciated.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
pavelfeldmancommented, Nov 9, 2021

You get the idea, use locators 😃 This will get rid of various races and unhandled exceptions, but i don’t think it’ll fix your issue. Once the script is good we can look further.

1reaction
pavelfeldmancommented, Nov 9, 2021

Let’s fix your script a little before we get to the actual issue.

test.describe.serial('POC Playwright - RD, () => {
  let page: Page;
  let electronApp: ElectronApplication;
  // const mainTitleSelector = '[data-test="mainTitle"]'; // <-- Use locators, not selectors for your page object model
  const mainTitle: Locator;

  test.beforeAll(async({ browser }) => { // <-- REVIEW when you say `browser` here, we actually run separate chrome for your, remove it
    electronApp = await _electron.launch({ args: [path.join(__dirname, '../')] });
    const appPath = await electronApp.evaluate(async({ app }) => {
      return await app.getAppPath();
    });

    // app = await browser.newPage(); <-- REVIEW this was creating a page in that separate chrome, unrelated to Electron.
    console.log('Log from appPath ---> ', appPath);
  });

  test.afterAll(async() => {
    await electronApp.close();
  });

  test('should open the main app', async() => {
    page = await electronApp.firstWindow();
    mainTitle = page.locator('[data-test="mainTitle"]');
    // await page.waitForSelector('.progress', { state: 'visible' }); // <-- visible is default, also you don't seem to need it
    // await delay(20000); // <-- REVIEW never ever do that
    // const versionApp = await app.$eval('.versionInfo', el => el.textContent); // <-- REVIEW never use ElementHandles
    // expect(versionApp).toBe('Version: (checking...)'); // <-- REVIEW don't expect 

    // REVIEW These two lines will wait for version info to become Version (checking)
    const versionInfo = page.locator('.versionInfo');
    await expect(versionInfo).toHaveText('Version: (checking...)');

  });

  test('should get General page content', async() => {
    // const generalGreetings = await page.$eval(mainTitleSelector, el => el.textContent.trim());
    // expect(generalGreetings).toBe('Welcome to RD');
    // REVIEW This one line will wait for expected value
    await expect(mainTitle).toHaveText('Welcome to RD');
  });

  test('should navigate to K Settings', async() => {
    const kVersionDndSelector = '.labeled-input';
    const kMemorySliderSelector = '[id="memoryInGBWrapper"]';

    try {
      page.click(`.nav li[item="/Page2"] a`); // <-- always await promises, otherwise you have unhandled promise rejection
      await page.waitForSelector('.contents');
    } catch (err) {
      console.log('Error during K Settings navigation. Error --> ', err);
    }

//    const kSettingsTitle = await page.$eval(mainTitleSelector, el => el.textContent.trim());
//    const kVersionDnd = await page.$eval(kVersionDndSelector, el => el.textContent.trim());
    await expect(page.locator('.labeled-input')).toHaveText('K version');
    await expect(mainTitle).toHaveText('K Settings');
 
//    expect(k8sVersionDnd).toBe('K version');
//    expect(k8sSettingsTitle).toBe('K Settings');
  });
});

function delay(time: number | undefined) {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
  });
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

[Bug]: ipc Renderer SendSync returning null #31733 - GitHub
I have searched the issue tracker for a feature request that matches the one I want to file, without success. Electron Version. 12.0.18....
Read more >
Electron ipcRenderer.sendSync() returning undefined
I realised what I did wrong. In preload.ts I exposed the function sendSync like below: sendSync(channel: Channels, args: unknown[]) ...
Read more >
ipcRenderer - Electron
The ipcRenderer module is an EventEmitter. It provides a few methods so you can send synchronous and asynchronous messages from the render process...
Read more >
How to use the electron.ipcRenderer.send function in electron
To help you get started, we've selected a few electron.ipcRenderer.send examples, based on popular ways it is used in public projects.
Read more >
electron.IpcRenderer.sendSync JavaScript and Node.js code ...
Please do it after a settings.set, or else the value will not be saved. */ save():void{ return electron.ipcRenderer.sendSync("LIGHTCORD_SAVE_SETTINGS") }.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found