I have an Electron app that first runs its backend and awaits its connection before opening the first window like:
/**
* Connects to the Flask app, then creates the window
*/
let flaskProc = null;
const connectToFlask = () => {
//...
//run the venv and start the script
flaskProc = require("child_process").spawn(command, args);
flaskProc.stdout.on('data', function (data) {
const output = data.toString('utf8');
console.log("FLASK RUNNING! data:", output);
/* Wait until Flask server is running to create the window */
if(output.includes("Serving Flask")){
testFlaskConnection().then((value) => {
if(value===true){
//emitting an event for Playwright tests
createWindow();
}
})
.catch(err => console.error(err));
}
});
//...
}
const testFlaskConnection = () => {
if (flaskProc) {
console.log("Attempting connection to Flask...");
return fetch(`${SERVER_URL}/`,{ method: "GET" })
.then((response) =>{
//...
return true;
})
//...
}
}
app.whenReady().then(() => {
connectToFlask();
});
and when the window is closed, the backend shuts down like:
const shutdownFlaskConnection = () => {
if (flaskProc) {
return fetch(`${SERVER_URL}/shutdown`, { method:"POST" })
.then((response) => {
/* If the response doesn't come back (error 404), Flask has probably
already shut down before it could be sent */
if (!response.ok && response.status!=404) {
console.error("Flask shutdown request failed:", response.status);
flaskProc.kill('SIGINT'); //kill Flask jic
flaskProc=null;
return;
}
/* Otherwise, Flask was shutdown */
console.log("Flask shutdown request successful.");
flaskProc=null;
})
.catch((error) => {
console.error("Error with shutdown request:", error);
flaskProc.kill('SIGINT');
flaskProc = null;
});
}
}
//...
let isAppQuitting=false; //flag for on close event
let win=null;
const createWindow = () => {
//...
win.on('close', (evt) => {
if(!isAppQuitting){
evt.preventDefault(); //pause shutdown to run one last request
console.log("Attempting graceful exit of Flask...");
shutdownFlaskConnection().then(() => {
//close again, properly this time
isAppQuitting=true; //reset flag to skip if block
win.close(); //run the event handler again
}).catch((err) => {
console.error(err);
isAppQuitting=true; //reset flag to skip if block
win.close(); //run the event handler again
});
}
});
}
/**
* When all the windows close, quit the app
*/
app.on('window-all-closed', () => {
if (process.platform !== 'darwin'){
app.quit();
}
});
/**
* When Electron is closed through the terminal, also kill Flask
*/
app.on('quit', () => {
if (flaskProc) {
console.log("Quitting: Sending SIGINT to Flask process...");
flaskProc.kill('SIGINT');
}
});
The latter part, I suspect, is the reason this error is happening.
The app works. The problem is that I want to add some tests with Playwright and this seems to be causing problems.
Just opening and closing the app works. Both of the tests below work:
const { test, _electron: electron } = require('@playwright/test')
test('launch app', async () => {
//NOTE: The launch path is relative to the folder the script is running from
const electronApp = await electron.launch({ args: ['apps/frontend/src/electron/main.js'] })
// close app
await electronApp.close()
});
const { test, _electron: electron } = require('@playwright/test')
let electronApp = null;
test.beforeAll(async () => {
//NOTE: The launch path is relative to the folder the script is running from
electronApp = await electron.launch({ args: ['apps/frontend/src/electron/main.js'] });
});
test.afterAll(async () => {
// close app
await electronApp.close();
})
test('launch app', async () => {
console.log("filler");
});
But the moment I try to interact with the app, the test will run until it’s timed out, even after all actions have been completed.
test('launch app', async () => {
// Get the first window that the app opens, wait if necessary.
const window = await electronApp.firstWindow();
// Print the title
console.log(await window.title());
// close app
await electronApp.close();
});
Opens the app, prints the title, and then closes the app, as it should, but then the test just keeps running. It then times out, fails, and I have to stop the test runner manually.
The error message I get looks like:
Running 1 test using 1 worker
✘ 1 appslibse2e_testsmain.spec.js:15:1 › launch app (30.0s)
Index Page
1) appslibse2e_testsmain.spec.js:15:1 › launch app ───────────────────────────────────────
Test timeout of 30000ms exceeded.
1 failed
appslibse2e_testsmain.spec.js:15:1 › launch app ────────────────────────────────────────
Terminate batch job (Y/N)? y
I know that this problem is due to the Flask app because if I replace my main.js with the one from the Electron Quick Start Guide, it runs perfectly. The following message is what that prints to the terminal:
Running 1 test using 1 worker
✓ 1 appslibse2e_testsmain.spec.js:15:1 › launch app (242ms)
Index Page
1 passed (1.6s)
I have tried emitting a message for Playwright to pick up or a console.log() message, but neither work. It’s difficult searching for answers since this is such a specific problem.
Any help would be appreciated. Thank you for your time.