890a557eed
* chore: bump node in DEPS to v20.10.0 * chore: update feat_initialize_asar_support.patch no code changes; patch just needed an update due to nearby upstream changes Xref: https://github.com/nodejs/node/pull/49986 * chore: update pass_all_globals_through_require.patch no manual changes; patch applied with fuzz Xref: https://github.com/nodejs/node/pull/49657 * chore: update refactor_allow_embedder_overriding_of_internal_fs_calls Xref: https://github.com/nodejs/node/pull/49912 no code changes; patch just needed an update due to nearby upstream changes * chore: update chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch Xref: https://github.com/nodejs/node/pull/49986 minor manual changes needed to sync with upstream change * update fix_expose_the_built-in_electron_module_via_the_esm_loader.patch Xref: https://github.com/nodejs/node/pull/50096 Xref: https://github.com/nodejs/node/pull/50314 in lib/internal/modules/esm/load.js, update the code that checks for `format === 'electron'`. I'd like 👀 on this Xref: https://github.com/nodejs/node/pull/49657 add braces in lib/internal/modules/esm/translators.js to sync with upstream * fix: lazyload fs in esm loaders to apply asar patches * https://github.com/nodejs/node/pull/50127 * https://github.com/nodejs/node/pull/50096 * esm: jsdoc for modules code https://github.com/nodejs/node/pull/49523 * test: set test-cli-node-options as flaky https://github.com/nodejs/node/pull/50296 * deps: update c-ares to 1.20.1 https://github.com/nodejs/node/pull/50082 * esm: bypass CommonJS loader under --default-type=module https://github.com/nodejs/node/pull/49986 * deps: update uvwasi to 0.0.19 https://github.com/nodejs/node/pull/49908 * lib,test: do not hardcode Buffer.kMaxLength https://github.com/nodejs/node/pull/49876 * crypto: account for disabled SharedArrayBuffer https://github.com/nodejs/node/pull/50034 * test: fix edge snapshot stack traces https://github.com/nodejs/node/pull/49659 * src: generate snapshot with --predictable https://github.com/nodejs/node/pull/48749 * chore: fixup patch indices * fs: throw errors from sync branches instead of separate implementations https://github.com/nodejs/node/pull/49913 * crypto: ensure valid point on elliptic curve in SubtleCrypto.importKey https://github.com/nodejs/node/pull/50234 * esm: detect ESM syntax in ambiguous JavaScrip https://github.com/nodejs/node/pull/50096 * fixup! test: fix edge snapshot stack traces * esm: unflag extensionless ES module JavaScript and Wasm in module scope https://github.com/nodejs/node/pull/49974 * [tagged-ptr] Arrowify objects https://chromium-review.googlesource.com/c/v8/v8/+/4705331 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr <charles@charleskerr.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
209 lines
7.3 KiB
TypeScript
209 lines
7.3 KiB
TypeScript
import { expect } from 'chai';
|
|
import * as cp from 'node:child_process';
|
|
import { BrowserWindow } from 'electron';
|
|
import * as fs from 'fs-extra';
|
|
import * as os from 'node:os';
|
|
import * as path from 'node:path';
|
|
import { pathToFileURL } from 'node:url';
|
|
|
|
const runFixture = async (appPath: string, args: string[] = []) => {
|
|
const result = cp.spawn(process.execPath, [appPath, ...args], {
|
|
stdio: 'pipe'
|
|
});
|
|
|
|
const stdout: Buffer[] = [];
|
|
const stderr: Buffer[] = [];
|
|
result.stdout.on('data', (chunk) => stdout.push(chunk));
|
|
result.stderr.on('data', (chunk) => stderr.push(chunk));
|
|
|
|
const [code, signal] = await new Promise<[number | null, NodeJS.Signals | null]>((resolve) => {
|
|
result.on('close', (code, signal) => {
|
|
resolve([code, signal]);
|
|
});
|
|
});
|
|
|
|
return {
|
|
code,
|
|
signal,
|
|
stdout: Buffer.concat(stdout).toString().trim(),
|
|
stderr: Buffer.concat(stderr).toString().trim()
|
|
};
|
|
};
|
|
|
|
const fixturePath = path.resolve(__dirname, 'fixtures', 'esm');
|
|
|
|
describe('esm', () => {
|
|
describe('main process', () => {
|
|
it('should load an esm entrypoint', async () => {
|
|
const result = await runFixture(path.resolve(fixturePath, 'entrypoint.mjs'));
|
|
expect(result.code).to.equal(0);
|
|
expect(result.stdout).to.equal('ESM Launch, ready: false');
|
|
});
|
|
|
|
it('should load an esm entrypoint based on type=module', async () => {
|
|
const result = await runFixture(path.resolve(fixturePath, 'package'));
|
|
expect(result.code).to.equal(0);
|
|
expect(result.stdout).to.equal('ESM Package Launch, ready: false');
|
|
});
|
|
|
|
it('should wait for a top-level await before declaring the app ready', async () => {
|
|
const result = await runFixture(path.resolve(fixturePath, 'top-level-await.mjs'));
|
|
expect(result.code).to.equal(0);
|
|
expect(result.stdout).to.equal('Top level await, ready: false');
|
|
});
|
|
|
|
it('should allow usage of pre-app-ready apis in top-level await', async () => {
|
|
const result = await runFixture(path.resolve(fixturePath, 'pre-app-ready-apis.mjs'));
|
|
expect(result.code).to.equal(0);
|
|
});
|
|
|
|
it('should allow use of dynamic import', async () => {
|
|
const result = await runFixture(path.resolve(fixturePath, 'dynamic.mjs'));
|
|
expect(result.code).to.equal(0);
|
|
expect(result.stdout).to.equal('Exit with app, ready: false');
|
|
});
|
|
});
|
|
|
|
describe('renderer process', () => {
|
|
let w: BrowserWindow | null = null;
|
|
const tempDirs: string[] = [];
|
|
|
|
afterEach(async () => {
|
|
if (w) w.close();
|
|
w = null;
|
|
while (tempDirs.length) {
|
|
await fs.remove(tempDirs.pop()!);
|
|
}
|
|
});
|
|
|
|
async function loadWindowWithPreload (preload: string, webPreferences: Electron.WebPreferences) {
|
|
const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'e-spec-preload-'));
|
|
tempDirs.push(tmpDir);
|
|
const preloadPath = path.resolve(tmpDir, 'preload.mjs');
|
|
await fs.writeFile(preloadPath, preload);
|
|
|
|
w = new BrowserWindow({
|
|
show: false,
|
|
webPreferences: {
|
|
...webPreferences,
|
|
preload: preloadPath
|
|
}
|
|
});
|
|
|
|
let error: Error | null = null;
|
|
w.webContents.on('preload-error', (_, __, err) => {
|
|
error = err;
|
|
});
|
|
|
|
await w.loadFile(path.resolve(fixturePath, 'empty.html'));
|
|
|
|
return [w.webContents, error] as [Electron.WebContents, Error | null];
|
|
}
|
|
|
|
describe('nodeIntegration', () => {
|
|
it('should support an esm entrypoint', async () => {
|
|
const [webContents] = await loadWindowWithPreload('import { resolve } from "path"; window.resolvePath = resolve;', {
|
|
nodeIntegration: true,
|
|
sandbox: false,
|
|
contextIsolation: false
|
|
});
|
|
|
|
const exposedType = await webContents.executeJavaScript('typeof window.resolvePath');
|
|
expect(exposedType).to.equal('function');
|
|
});
|
|
|
|
it('should delay load until the ESM import chain is complete', async () => {
|
|
const [webContents] = await loadWindowWithPreload(`import { resolve } from "path";
|
|
await new Promise(r => setTimeout(r, 500));
|
|
window.resolvePath = resolve;`, {
|
|
nodeIntegration: true,
|
|
sandbox: false,
|
|
contextIsolation: false
|
|
});
|
|
|
|
const exposedType = await webContents.executeJavaScript('typeof window.resolvePath');
|
|
expect(exposedType).to.equal('function');
|
|
});
|
|
|
|
it('should support a top-level await fetch blocking the page load', async () => {
|
|
const [webContents] = await loadWindowWithPreload(`
|
|
const r = await fetch("package/package.json");
|
|
window.packageJson = await r.json();`, {
|
|
nodeIntegration: true,
|
|
sandbox: false,
|
|
contextIsolation: false
|
|
});
|
|
|
|
const packageJson = await webContents.executeJavaScript('window.packageJson');
|
|
expect(packageJson).to.deep.equal(require('./fixtures/esm/package/package.json'));
|
|
});
|
|
|
|
const hostsUrl = pathToFileURL(process.platform === 'win32' ? 'C:\\Windows\\System32\\drivers\\etc\\hosts' : '/etc/hosts');
|
|
|
|
describe('without context isolation', () => {
|
|
it('should use blinks dynamic loader in the main world', async () => {
|
|
const [webContents] = await loadWindowWithPreload('', {
|
|
nodeIntegration: true,
|
|
sandbox: false,
|
|
contextIsolation: false
|
|
});
|
|
|
|
let error: Error | null = null;
|
|
try {
|
|
await webContents.executeJavaScript(`import(${JSON.stringify(hostsUrl)})`);
|
|
} catch (err) {
|
|
error = err as Error;
|
|
}
|
|
|
|
expect(error).to.not.equal(null);
|
|
// This is a blink specific error message
|
|
expect(error?.message).to.include('Failed to fetch dynamically imported module');
|
|
});
|
|
});
|
|
|
|
describe('with context isolation', () => {
|
|
let badFilePath = '';
|
|
|
|
beforeEach(async () => {
|
|
badFilePath = path.resolve(path.resolve(os.tmpdir(), 'bad-file.badjs'));
|
|
await fs.promises.writeFile(badFilePath, 'const foo = "bar";');
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await fs.promises.unlink(badFilePath);
|
|
});
|
|
|
|
it('should use nodes esm dynamic loader in the isolated context', async () => {
|
|
const [, preloadError] = await loadWindowWithPreload(`await import(${JSON.stringify((pathToFileURL(badFilePath)))})`, {
|
|
nodeIntegration: true,
|
|
sandbox: false,
|
|
contextIsolation: true
|
|
});
|
|
|
|
expect(preloadError).to.not.equal(null);
|
|
// This is a node.js specific error message
|
|
expect(preloadError!.toString()).to.include('Unknown file extension');
|
|
});
|
|
|
|
it('should use blinks dynamic loader in the main world', async () => {
|
|
const [webContents] = await loadWindowWithPreload('', {
|
|
nodeIntegration: true,
|
|
sandbox: false,
|
|
contextIsolation: true
|
|
});
|
|
|
|
let error: Error | null = null;
|
|
try {
|
|
await webContents.executeJavaScript(`import(${JSON.stringify(hostsUrl)})`);
|
|
} catch (err) {
|
|
error = err as Error;
|
|
}
|
|
|
|
expect(error).to.not.equal(null);
|
|
// This is a blink specific error message
|
|
expect(error?.message).to.include('Failed to fetch dynamically imported module');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|