Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG][Electron] filechooser event not emitted when dialog.show(Open|Save)Dialog is called #8278

Open
OiNutter opened this issue Aug 18, 2021 · 15 comments

Comments

@OiNutter
Copy link

Duplicate of #5013. Full details and repro steps available there.

Opening new issue as requested by maintainers on original issue.

This is definitely a blocker for our integration tests as we need to test open and save functionality in our app and we're not able to select the files in the dialogs.

@tgdcat
Copy link

tgdcat commented Nov 19, 2021

I'm also having trouble with showOpenDialogSync and showSaveDialogSync because the test stopped.
Is there any workaround?

@wsw0108
Copy link

wsw0108 commented Jan 10, 2022

I have create a test repo to trying to mock electron dialog like spectron-fake-dialog does, but failed.

Does not work, but returned mocked is true.
https://github.com/wsw0108/electron-playwright-test/blob/master/test/test.js#L74-L81

This console.log does not write to file.
https://github.com/wsw0108/electron-playwright-test/blob/master/test/preload.js#L37

Does anyone may have time to help to find out why?

@Daveiano
Copy link

I am also having this issue and have been looking for a solution.

@wsw0108 I am quite new to JS testing, but I will have a look into your code if I get some time. Please report back!

@wsw0108
Copy link

wsw0108 commented Jan 11, 2022

@Daveiano I have found the fail cause, it's the async thing of electron ipc. The mock works actually.

@wsw0108
Copy link

wsw0108 commented Jan 11, 2022

Based on spectron-fake-dialog, I wrote a little library to fake electron dialog for playwright/electron,
https://github.com/wsw0108/playwright-fake-dialog

@MikeJerred
Copy link

Based on spectron-fake-dialog, I wrote a little library to fake electron dialog for playwright/electron, https://github.com/wsw0108/playwright-fake-dialog

Awesome! Can actually simplify this and just do:

const electronApp = await playwright._electron.launch({ args: ['./main.js']});

// ...

// in test:
electronApp.evaluate(
  async({ dialog }, filePaths) => {
    dialog.showOpenDialog = () => Promise.resolve({ canceled: false, filePaths });
  },
  ['file.txt']
);

@wsw0108
Copy link

wsw0108 commented Jan 11, 2022

@MikeJerred Elegant solution.

Can I use your idea to update my repo?

@MikeJerred
Copy link

@MikeJerred Elegant solution.

Can I use your idea to update my repo?

Yep of course

@Daveiano
Copy link

@wsw0108 I am getting this error when using playwright-fake-dialog

page.evaluate: ReferenceError: require is not defined
    at eval (eval at evaluate (:3:2389), <anonymous>:2:31)
    at t.default.evaluate (<anonymous>:3:2412)
    at t.default.<anonymous> (<anonymous>:1:44)

    at ipcRendererSendSyncAsync (/var/www/weather-data-center-electron-forge/node_modules/playwright-fake-dialog/index.js:12:15)
    at mock (/var/www/weather-data-center-electron-forge/node_modules/playwright-fake-dialog/index.js:22:10)
    at /var/www/weather-data-center-electron-forge/src/main/renderer-empty.test.ts:93:13
    at fulfilled (/var/www/weather-data-center-electron-forge/src/main/renderer-empty.test.ts:5:58)

Setup is the following:

import { _electron as electron } from "playwright-core";
const { launch, mock } = require('playwright-fake-dialog')

...

beforeAll(async () => {
  // Launch Electron app.
  electronApp = await launch(electron, {
    args: [
      '.'
    ],
    env: {
      ...process.env
    },
  });

  // Get the first window that the app opens, wait if necessary.
  page = await electronApp.firstWindow();

  // Wait for frame actually loaded.
  await page.waitForSelector('main');

  // Direct Electron console to Node terminal.
  page.on('console', console.log);
});

...

it('should import new data', async () => {
  await mock(page, [
    {
      method: 'showOpenDialog',
      value: {
        filePaths: [`${__dirname.replace('src/main', '')}tests/data/upload-data-start-16-08-21-30-09-21-1196-records.csv`]
      }
    }
  ]);

  await page.click('button#import');
});

Any help and suggestions would be very appreciated!

@wsw0108
Copy link

wsw0108 commented Jan 12, 2022

https://github.com/wsw0108/electron-playwright-test/blob/e3c6ff53d9d71360270ddd37305b188b8273d2c5/app/main/index.js#L43

using 0.1.0 needs above lines in your electron main.js.

If you use html input, just use filechooser from playwright.

@Daveiano
Copy link

Ah, got you, thank you!

I got it working now with the snippet from @MikeJerred, many thanks for that!

wsw0108 added a commit to wsw0108/playwright-fake-dialog that referenced this issue Jan 12, 2022
@FomerMay
Copy link

FomerMay commented Mar 22, 2022

H, i need some help to use the playwright-fake-dialog, do i need any code from "test/preload.js"?(https://github.com/wsw0108/electron-playwright-test/blob/master/test/preload.js).

This is my code

await mock(electronApp, [
   {
     method: 'showOpenDialog',
     value: {
       filePaths: [join(__dirname, 'minimal.pdf')]
     }
   }
 ])
 await newPage.click('[e2e-id="action-bar-create"]');

And i get this ERROR in our Log-File:
"fs.api.selectFilesWithOpenDialog (id 46189c0c) - failed due to error: n.a.dialog.showOpenDialog is not a function"

Our Application uses Electron 9, could that be a problem?

@dquak
Copy link

dquak commented Jan 30, 2024

Based on spectron-fake-dialog, I wrote a little library to fake electron dialog for playwright/electron, https://github.com/wsw0108/playwright-fake-dialog

Awesome! Can actually simplify this and just do:

const electronApp = await playwright._electron.launch({ args: ['./main.js']});

// ...

// in test:
electronApp.evaluate(
  async({ dialog }, filePaths) => {
    dialog.showOpenDialog = () => Promise.resolve({ canceled: false, filePaths });
  },
  ['file.txt']
);

best method, you can also control dialogs like so:

const dialogControlHandler = electronApp.evaluateHandler(() => {
  let resolve;
  const promise = new Promise(res => { resolve = res; });
  
  return {promise, resolve};
});


const dialogOptions = await electronApp.evaluate(
 async({ dialog },{ dialogControlHandler }) => {
   return new Promise(res => {
     dialog.showMessageBox = (options) => {
       res(options);
       return dialogControlHandler.promise;
     };
   });
  },
 { dialogControlHandler }
);

//Now you have the dialog options avaliable at `dialogOptions` (eg: dialogOptions.title)

//Choose how to resolve the dialog:
dialogControlHandler.evaluate( ({ resolve }) => resolve({response: 1, checkboxchecked: false}) );

@mxschmitt
Copy link
Member

Investigation:

  • Upstream Electron method ShowOpenDialog calls immediately OS APIs
  • When dealing with normal file pickers, upstream its a noop when intercepted like here in this cp
  • Probably a new event should be introduced so Electron can wait for it and return the value once emitted. Since Electron API would be different. event -> path[] all the time. It's never a file content etc.
  • Similar / different request is about setInputFiles should set path property. There we should extend 'DOM.setFileInputFiles' so it sets the path. [Feature] Set File.path property when uploading files with Electron #10527

dcalhoun added a commit to Automattic/studio that referenced this issue May 17, 2024
Playwright lacks support for interacting with native dialogs, so we mock
the dialog module to simulate the user clicking the "Delete site"
confirmation button with "Delete site files from my computer" checked.

See: microsoft/playwright#2143
See: microsoft/playwright#8278 (comment)
dcalhoun added a commit to Automattic/studio that referenced this issue May 29, 2024
Playwright lacks support for interacting with native dialogs, so we mock
the dialog module to simulate the user clicking the "Delete site"
confirmation button with "Delete site files from my computer" checked.

See: microsoft/playwright#2143
See: microsoft/playwright#8278 (comment)
wojtekn pushed a commit to Automattic/studio that referenced this issue May 30, 2024
* test: Delete site E2E case supports native dialogs

Playwright lacks support for interacting with native dialogs, so we mock
the dialog module to simulate the user clicking the "Delete site"
confirmation button with "Delete site files from my computer" checked.

See: microsoft/playwright#2143
See: microsoft/playwright#8278 (comment)

* test: Update Onboarding continue button label text

* refactor: Fix typo

* test: Improve stability of onboarding interactions

Combine `toBeVisible`'s interval with the ability to assert the precence
of one of multiple elements to ensure the UI is ready before
conditionally proceeding with the onboarding steps. This reduces the
likelihood that the UI is not ready when the test interactions begin and
also allows the Sites tests to succeed regardless of whether sites
already exists when the tests begin to run.
@sdet-g33
Copy link

Hello everyone, do we have any updates on this ^^^?

I am new to Playwright and I need help with a file upload in my Electron app. My app doesn't have an input element with type="file", so I have to use a FileChooser instead.

Here's the scenario: there's a "Browse Files" button in the app. When I click it, a file manager dialog opens. I then select a file and click the "Upload" button in the file manager dialog to upload the file.

In my script, it successfully clicks the "Browse Files" button, and the file manager dialog opens. However, it doesn't select a file or upload it, and the script times out with an error.

Error: page.waitForEvent: Target page, context or browser has been closed
=========================== logs ===========================
waiting for event "filechooser"
====================================

Versions:
"@playwright/test": "^1.45.3",
"electron-playwright-helpers": "^1.7.1",
"electron": "29.1.0",

Here is my test script:

const browseFilesBtn = await page.locator('button[aria-label="browse files"]')

    try {
      const fileChooserPromise = page.waitForEvent('filechooser');
      await page.click(browseFilesBtn);
      const fileChooser = await fileChooserPromise;
      await fileChooser.setFiles(filepath);
  } catch (e) {
      console.log(e.message);
  }
});

I have tried this one as well:

const [fileChooser] = await Promise.all([
  page.waitForEvent('filechooser'),
  page.click('button[aria-label="browse files"]'),
]);


console.log("File chooser event triggered.");
await fileChooser.setFiles(filePath);
console.log("File selected.");

Thank you all in advance!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants