mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
test: new service worker features
This commit is contained in:
@@ -108,7 +108,7 @@ describe('contextBridge', () => {
|
||||
};
|
||||
|
||||
const callWithBindings = (fn: Function, worldId: number = 0) =>
|
||||
worldId === 0 ? w.webContents.executeJavaScript(`(${fn.toString()})(window)`) : w.webContents.executeJavaScriptInIsolatedWorld(worldId, [{ code: `(${fn.toString()})(window)` }]); ;
|
||||
worldId === 0 ? w.webContents.executeJavaScript(`(${fn.toString()})(window)`) : w.webContents.executeJavaScriptInIsolatedWorld(worldId, [{ code: `(${fn.toString()})(window)` }]);
|
||||
|
||||
const getGCInfo = async (): Promise<{
|
||||
trackedValues: number;
|
||||
@@ -1290,6 +1290,115 @@ describe('contextBridge', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeInMainWorld', () => {
|
||||
it('serializes function and proxies args', async () => {
|
||||
await makeBindingWindow(async () => {
|
||||
const values = [
|
||||
undefined,
|
||||
null,
|
||||
123,
|
||||
'string',
|
||||
true,
|
||||
[123, 'string', true, ['foo']],
|
||||
() => 'string',
|
||||
Symbol('foo')
|
||||
];
|
||||
function appendArg (arg: any) {
|
||||
// @ts-ignore
|
||||
globalThis.args = globalThis.args || [];
|
||||
// @ts-ignore
|
||||
globalThis.args.push(arg);
|
||||
}
|
||||
for (const value of values) {
|
||||
try {
|
||||
await contextBridge.executeInMainWorld({
|
||||
func: appendArg,
|
||||
args: [value]
|
||||
});
|
||||
} catch {
|
||||
contextBridge.executeInMainWorld({
|
||||
func: appendArg,
|
||||
args: ['FAIL']
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const result = await callWithBindings(() => {
|
||||
// @ts-ignore
|
||||
return globalThis.args.map(arg => {
|
||||
// Map unserializable IPC types to their type string
|
||||
if (['function', 'symbol'].includes(typeof arg)) {
|
||||
return typeof arg;
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
});
|
||||
});
|
||||
expect(result).to.deep.equal([
|
||||
undefined,
|
||||
null,
|
||||
123,
|
||||
'string',
|
||||
true,
|
||||
[123, 'string', true, ['foo']],
|
||||
'function',
|
||||
'symbol'
|
||||
]);
|
||||
});
|
||||
|
||||
it('allows function args to be invoked', async () => {
|
||||
const donePromise = once(ipcMain, 'done');
|
||||
makeBindingWindow(() => {
|
||||
const uuid = crypto.randomUUID();
|
||||
const done = (receivedUuid: string) => {
|
||||
if (receivedUuid === uuid) {
|
||||
require('electron').ipcRenderer.send('done');
|
||||
}
|
||||
};
|
||||
contextBridge.executeInMainWorld({
|
||||
func: (callback, innerUuid) => {
|
||||
callback(innerUuid);
|
||||
},
|
||||
args: [done, uuid]
|
||||
});
|
||||
});
|
||||
await donePromise;
|
||||
});
|
||||
|
||||
it('safely clones returned objects', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
const obj = contextBridge.executeInMainWorld({
|
||||
func: () => ({})
|
||||
});
|
||||
// @ts-ignore
|
||||
globalThis.safe = obj.constructor === Object;
|
||||
});
|
||||
const result = await callWithBindings(() => {
|
||||
// @ts-ignore
|
||||
return globalThis.safe;
|
||||
}, 999);
|
||||
expect(result).to.be.true();
|
||||
});
|
||||
|
||||
it('uses internal Function.prototype.toString', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
const funcHack = () => {
|
||||
// @ts-ignore
|
||||
globalThis.hacked = 'nope';
|
||||
};
|
||||
funcHack.toString = () => '() => { globalThis.hacked = \'gotem\'; }';
|
||||
contextBridge.executeInMainWorld({
|
||||
func: funcHack
|
||||
});
|
||||
});
|
||||
const result = await callWithBindings(() => {
|
||||
// @ts-ignore
|
||||
return globalThis.hacked;
|
||||
});
|
||||
expect(result).to.equal('nope');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
422
spec/api-service-worker-main-spec.ts
Normal file
422
spec/api-service-worker-main-spec.ts
Normal file
@@ -0,0 +1,422 @@
|
||||
import { ipcMain, session, webContents as webContentsModule, WebContents } from 'electron/main';
|
||||
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { once, on } from 'node:events';
|
||||
import * as fs from 'node:fs';
|
||||
import * as http from 'node:http';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import { listen, waitUntil } from './lib/spec-helpers';
|
||||
|
||||
// Toggle to add extra debug output
|
||||
const DEBUG = !process.env.CI;
|
||||
|
||||
describe('ServiceWorkerMain module', () => {
|
||||
const fixtures = path.resolve(__dirname, 'fixtures');
|
||||
const preloadRealmFixtures = path.resolve(fixtures, 'api/preload-realm');
|
||||
const webContentsInternal: typeof ElectronInternal.WebContents = webContentsModule as any;
|
||||
|
||||
let ses: Electron.Session;
|
||||
let serviceWorkers: Electron.ServiceWorkers;
|
||||
let server: http.Server;
|
||||
let baseUrl: string;
|
||||
let wc: WebContents;
|
||||
|
||||
beforeEach(async () => {
|
||||
ses = session.fromPartition(`service-worker-main-spec-${crypto.randomUUID()}`);
|
||||
serviceWorkers = ses.serviceWorkers;
|
||||
|
||||
if (DEBUG) {
|
||||
serviceWorkers.on('console-message', (_e, details) => {
|
||||
console.log(details.message);
|
||||
});
|
||||
}
|
||||
|
||||
const uuid = crypto.randomUUID();
|
||||
server = http.createServer((req, res) => {
|
||||
const url = new URL(req.url!, `http://${req.headers.host}`);
|
||||
// /{uuid}/{file}
|
||||
const file = url.pathname!.split('/')[2]!;
|
||||
|
||||
if (file.endsWith('.js')) {
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
}
|
||||
res.end(fs.readFileSync(path.resolve(fixtures, 'api', 'service-workers', file)));
|
||||
});
|
||||
const { port } = await listen(server);
|
||||
baseUrl = `http://localhost:${port}/${uuid}`;
|
||||
|
||||
wc = webContentsInternal.create({ session: ses });
|
||||
|
||||
if (DEBUG) {
|
||||
wc.on('console-message', (_e, _l, message) => {
|
||||
console.log(message);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (!wc.isDestroyed()) wc.destroy();
|
||||
server.close();
|
||||
ses.getPreloadScripts().map(({ id }) => ses.unregisterPreloadScript(id));
|
||||
});
|
||||
|
||||
function registerPreload (scriptName: string) {
|
||||
const id = ses.registerPreloadScript({
|
||||
type: 'service-worker',
|
||||
filePath: path.resolve(preloadRealmFixtures, scriptName)
|
||||
});
|
||||
expect(id).to.be.a('string');
|
||||
}
|
||||
|
||||
async function loadWorkerScript (scriptUrl?: string) {
|
||||
const scriptParams = scriptUrl ? `?scriptUrl=${scriptUrl}` : '';
|
||||
return wc.loadURL(`${baseUrl}/index.html${scriptParams}`);
|
||||
}
|
||||
|
||||
async function unregisterAllServiceWorkers () {
|
||||
await wc.executeJavaScript(`(${async function () {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (const registration of registrations) {
|
||||
registration.unregister();
|
||||
}
|
||||
}}())`);
|
||||
}
|
||||
|
||||
async function waitForServiceWorker (expectedRunningStatus: Electron.ServiceWorkersRunningStatusChangedEventParams['runningStatus'] = 'starting') {
|
||||
const serviceWorkerPromise = new Promise<Electron.ServiceWorkerMain>((resolve) => {
|
||||
function onRunningStatusChanged ({ versionId, runningStatus }: Electron.ServiceWorkersRunningStatusChangedEventParams) {
|
||||
if (runningStatus === expectedRunningStatus) {
|
||||
const serviceWorker = serviceWorkers.getWorkerFromVersionID(versionId)!;
|
||||
serviceWorkers.off('running-status-changed', onRunningStatusChanged);
|
||||
resolve(serviceWorker);
|
||||
}
|
||||
}
|
||||
serviceWorkers.on('running-status-changed', onRunningStatusChanged);
|
||||
});
|
||||
const serviceWorker = await serviceWorkerPromise;
|
||||
expect(serviceWorker).to.not.be.undefined();
|
||||
return serviceWorker!;
|
||||
}
|
||||
|
||||
/** Runs a test using the framework in preload-tests.js */
|
||||
const runTest = async (serviceWorker: Electron.ServiceWorkerMain, rpc: { name: string, args: any[] }) => {
|
||||
const uuid = crypto.randomUUID();
|
||||
serviceWorker.send('test', uuid, rpc.name, ...rpc.args);
|
||||
return new Promise((resolve, reject) => {
|
||||
serviceWorker.ipc.once(`test-result-${uuid}`, (_event, { error, result }) => {
|
||||
if (error) {
|
||||
reject(result);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
describe('serviceWorkers.getWorkerFromVersionID', () => {
|
||||
it('returns undefined for non-live service worker', () => {
|
||||
expect(serviceWorkers.getWorkerFromVersionID(-1)).to.be.undefined();
|
||||
expect(serviceWorkers._getWorkerFromVersionIDIfExists(-1)).to.be.undefined();
|
||||
});
|
||||
|
||||
it('returns instance for live service worker', async () => {
|
||||
const runningStatusChanged = once(serviceWorkers, 'running-status-changed');
|
||||
loadWorkerScript();
|
||||
const [{ versionId }] = await runningStatusChanged;
|
||||
const serviceWorker = serviceWorkers.getWorkerFromVersionID(versionId);
|
||||
expect(serviceWorker).to.not.be.undefined();
|
||||
const ifExistsServiceWorker = serviceWorkers._getWorkerFromVersionIDIfExists(versionId);
|
||||
expect(ifExistsServiceWorker).to.not.be.undefined();
|
||||
expect(serviceWorker).to.equal(ifExistsServiceWorker);
|
||||
});
|
||||
|
||||
it('does not crash on script error', async () => {
|
||||
wc.loadURL(`${baseUrl}/index.html?scriptUrl=sw-script-error.js`);
|
||||
let serviceWorker;
|
||||
const actualStatuses = [];
|
||||
for await (const [{ versionId, runningStatus }] of on(serviceWorkers, 'running-status-changed')) {
|
||||
if (!serviceWorker) {
|
||||
serviceWorker = serviceWorkers.getWorkerFromVersionID(versionId);
|
||||
}
|
||||
actualStatuses.push(runningStatus);
|
||||
if (runningStatus === 'stopping') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(actualStatuses).to.deep.equal(['starting', 'stopping']);
|
||||
expect(serviceWorker).to.not.be.undefined();
|
||||
});
|
||||
|
||||
it('does not find unregistered service worker', async () => {
|
||||
loadWorkerScript();
|
||||
const runningServiceWorker = await waitForServiceWorker('running');
|
||||
const { versionId } = runningServiceWorker;
|
||||
unregisterAllServiceWorkers();
|
||||
await waitUntil(() => runningServiceWorker.isDestroyed());
|
||||
const serviceWorker = serviceWorkers.getWorkerFromVersionID(versionId);
|
||||
expect(serviceWorker).to.be.undefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDestroyed()', () => {
|
||||
it('is not destroyed after being created', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker();
|
||||
expect(serviceWorker.isDestroyed()).to.be.false();
|
||||
});
|
||||
|
||||
it('is destroyed after being unregistered', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker();
|
||||
expect(serviceWorker.isDestroyed()).to.be.false();
|
||||
await unregisterAllServiceWorkers();
|
||||
await waitUntil(() => serviceWorker.isDestroyed());
|
||||
});
|
||||
});
|
||||
|
||||
describe('"running-status-changed" event', () => {
|
||||
it('handles when content::ServiceWorkerVersion has been destroyed', async () => {
|
||||
loadWorkerScript('sw-unregister-self.js');
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
await waitUntil(() => serviceWorker.isDestroyed());
|
||||
});
|
||||
});
|
||||
|
||||
describe('startWorkerForScope()', () => {
|
||||
it('resolves with running workers', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
const startWorkerPromise = serviceWorkers.startWorkerForScope(serviceWorker.scope);
|
||||
await expect(startWorkerPromise).to.eventually.be.fulfilled();
|
||||
const otherSW = await startWorkerPromise;
|
||||
expect(otherSW).to.equal(serviceWorker);
|
||||
});
|
||||
|
||||
it('rejects with starting workers', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('starting');
|
||||
const startWorkerPromise = serviceWorkers.startWorkerForScope(serviceWorker.scope);
|
||||
await expect(startWorkerPromise).to.eventually.be.rejected();
|
||||
});
|
||||
|
||||
it('starts previously stopped worker', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
const { scope } = serviceWorker;
|
||||
const stoppedPromise = waitForServiceWorker('stopped');
|
||||
await serviceWorkers._stopAllWorkers();
|
||||
await stoppedPromise;
|
||||
const startWorkerPromise = serviceWorkers.startWorkerForScope(scope);
|
||||
await expect(startWorkerPromise).to.eventually.be.fulfilled();
|
||||
});
|
||||
|
||||
it('resolves when called twice', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
const { scope } = serviceWorker;
|
||||
const [swA, swB] = await Promise.all([
|
||||
serviceWorkers.startWorkerForScope(scope),
|
||||
serviceWorkers.startWorkerForScope(scope)
|
||||
]);
|
||||
expect(swA).to.equal(swB);
|
||||
expect(swA).to.equal(serviceWorker);
|
||||
});
|
||||
});
|
||||
|
||||
describe('startTask()', () => {
|
||||
it('has no tasks in-flight initially', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker();
|
||||
expect(serviceWorker._countExternalRequests()).to.equal(0);
|
||||
});
|
||||
|
||||
it('can start and end a task', async () => {
|
||||
loadWorkerScript();
|
||||
|
||||
// Internally, ServiceWorkerVersion buckets tasks into requests made
|
||||
// during and after startup.
|
||||
// ServiceWorkerContext::CountExternalRequestsForTest only considers
|
||||
// requests made while SW is in running status so we need to wait for that
|
||||
// to read an accurate count.
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
|
||||
const task = serviceWorker.startTask();
|
||||
expect(task).to.be.an('object');
|
||||
expect(task).to.have.property('end').that.is.a('function');
|
||||
expect(serviceWorker._countExternalRequests()).to.equal(1);
|
||||
|
||||
task.end();
|
||||
|
||||
// Count will decrement after Promise.finally callback
|
||||
await new Promise<void>(queueMicrotask);
|
||||
expect(serviceWorker._countExternalRequests()).to.equal(0);
|
||||
});
|
||||
|
||||
it('can have more than one active task', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
|
||||
const taskA = serviceWorker.startTask();
|
||||
const taskB = serviceWorker.startTask();
|
||||
expect(serviceWorker._countExternalRequests()).to.equal(2);
|
||||
taskB.end();
|
||||
taskA.end();
|
||||
|
||||
// Count will decrement after Promise.finally callback
|
||||
await new Promise<void>(queueMicrotask);
|
||||
expect(serviceWorker._countExternalRequests()).to.equal(0);
|
||||
});
|
||||
|
||||
it('throws when starting task after destroyed', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker();
|
||||
await unregisterAllServiceWorkers();
|
||||
await waitUntil(() => serviceWorker.isDestroyed());
|
||||
expect(() => serviceWorker.startTask()).to.throw();
|
||||
});
|
||||
|
||||
it('throws when ending task after destroyed', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker();
|
||||
const task = serviceWorker.startTask();
|
||||
await unregisterAllServiceWorkers();
|
||||
await waitUntil(() => serviceWorker.isDestroyed());
|
||||
expect(() => task.end()).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("'versionId' property", () => {
|
||||
it('matches the expected value', async () => {
|
||||
const runningStatusChanged = once(serviceWorkers, 'running-status-changed');
|
||||
wc.loadURL(`${baseUrl}/index.html`);
|
||||
const [{ versionId }] = await runningStatusChanged;
|
||||
const serviceWorker = serviceWorkers.getWorkerFromVersionID(versionId);
|
||||
expect(serviceWorker).to.not.be.undefined();
|
||||
if (!serviceWorker) return;
|
||||
expect(serviceWorker).to.have.property('versionId').that.is.a('number');
|
||||
expect(serviceWorker.versionId).to.equal(versionId);
|
||||
});
|
||||
});
|
||||
|
||||
describe("'scope' property", () => {
|
||||
it('matches the expected value', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker();
|
||||
expect(serviceWorker).to.not.be.undefined();
|
||||
if (!serviceWorker) return;
|
||||
expect(serviceWorker).to.have.property('scope').that.is.a('string');
|
||||
expect(serviceWorker.scope).to.equal(`${baseUrl}/`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ipc', () => {
|
||||
beforeEach(() => {
|
||||
registerPreload('preload-tests.js');
|
||||
});
|
||||
|
||||
describe('on(channel)', () => {
|
||||
it('can receive a message during startup', async () => {
|
||||
registerPreload('preload-send-ping.js');
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker();
|
||||
const pingPromise = once(serviceWorker.ipc, 'ping');
|
||||
await pingPromise;
|
||||
});
|
||||
|
||||
it('receives a message', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
const pingPromise = once(serviceWorker.ipc, 'ping');
|
||||
runTest(serviceWorker, { name: 'testSend', args: ['ping'] });
|
||||
await pingPromise;
|
||||
});
|
||||
|
||||
it('does not receive message on ipcMain', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
const abortController = new AbortController();
|
||||
try {
|
||||
let pingReceived = false;
|
||||
once(ipcMain, 'ping', { signal: abortController.signal }).then(() => {
|
||||
pingReceived = true;
|
||||
});
|
||||
runTest(serviceWorker, { name: 'testSend', args: ['ping'] });
|
||||
await once(ses, '-ipc-message');
|
||||
await new Promise<void>(queueMicrotask);
|
||||
expect(pingReceived).to.be.false();
|
||||
} finally {
|
||||
abortController.abort();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('handle(channel)', () => {
|
||||
it('receives and responds to message', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
serviceWorker.ipc.handle('ping', () => 'pong');
|
||||
const result = await runTest(serviceWorker, { name: 'testInvoke', args: ['ping'] });
|
||||
expect(result).to.equal('pong');
|
||||
});
|
||||
|
||||
it('works after restarting worker', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
const { scope } = serviceWorker;
|
||||
serviceWorker.ipc.handle('ping', () => 'pong');
|
||||
await serviceWorkers._stopAllWorkers();
|
||||
await serviceWorkers.startWorkerForScope(scope);
|
||||
const result = await runTest(serviceWorker, { name: 'testInvoke', args: ['ping'] });
|
||||
expect(result).to.equal('pong');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('contextBridge', () => {
|
||||
beforeEach(() => {
|
||||
registerPreload('preload-tests.js');
|
||||
});
|
||||
|
||||
it('can evaluate func from preload realm', async () => {
|
||||
loadWorkerScript();
|
||||
const serviceWorker = await waitForServiceWorker('running');
|
||||
const result = await runTest(serviceWorker, { name: 'testEvaluate', args: ['evalConstructorName'] });
|
||||
expect(result).to.equal('ServiceWorkerGlobalScope');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extensions', () => {
|
||||
const extensionFixtures = path.join(fixtures, 'extensions');
|
||||
const testExtensionFixture = path.join(extensionFixtures, 'mv3-service-worker');
|
||||
|
||||
beforeEach(async () => {
|
||||
ses = session.fromPartition(`persist:${crypto.randomUUID()}-service-worker-main-spec`);
|
||||
serviceWorkers = ses.serviceWorkers;
|
||||
});
|
||||
|
||||
it('can observe extension service workers', async () => {
|
||||
const serviceWorkerPromise = waitForServiceWorker();
|
||||
const extension = await ses.loadExtension(testExtensionFixture);
|
||||
const serviceWorker = await serviceWorkerPromise;
|
||||
expect(serviceWorker.scope).to.equal(extension.url);
|
||||
});
|
||||
|
||||
it('has extension state available when preload runs', async () => {
|
||||
registerPreload('preload-send-extension.js');
|
||||
const serviceWorkerPromise = waitForServiceWorker();
|
||||
const extensionPromise = ses.loadExtension(testExtensionFixture);
|
||||
const serviceWorker = await serviceWorkerPromise;
|
||||
const result = await new Promise<any>((resolve) => {
|
||||
serviceWorker.ipc.handleOnce('preload-extension-result', (_event, result) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
const extension = await extensionPromise;
|
||||
expect(result).to.be.an('object');
|
||||
expect(result.id).to.equal(extension.id);
|
||||
expect(result.manifest).to.deep.equal(result.manifest);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -27,8 +27,9 @@ describe('session.serviceWorkers', () => {
|
||||
const uuid = v4();
|
||||
|
||||
server = http.createServer((req, res) => {
|
||||
const url = new URL(req.url!, `http://${req.headers.host}`);
|
||||
// /{uuid}/{file}
|
||||
const file = req.url!.split('/')[2]!;
|
||||
const file = url.pathname!.split('/')[2]!;
|
||||
|
||||
if (file.endsWith('.js')) {
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
@@ -76,7 +77,7 @@ describe('session.serviceWorkers', () => {
|
||||
describe('console-message event', () => {
|
||||
it('should correctly keep the source, message and level', async () => {
|
||||
const messages: Record<string, Electron.MessageDetails> = {};
|
||||
w.loadURL(`${baseUrl}/logs.html`);
|
||||
w.loadURL(`${baseUrl}/index.html?scriptUrl=sw-logs.js`);
|
||||
for await (const [, details] of on(ses.serviceWorkers, 'console-message')) {
|
||||
messages[details.message] = details;
|
||||
expect(details).to.have.property('source', 'console-api');
|
||||
|
||||
16
spec/fixtures/api/preload-realm/preload-send-extension.js
vendored
Normal file
16
spec/fixtures/api/preload-realm/preload-send-extension.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = contextBridge.executeInMainWorld({
|
||||
func: () => ({
|
||||
chromeType: typeof chrome,
|
||||
id: globalThis.chrome?.runtime.id,
|
||||
manifest: globalThis.chrome?.runtime.getManifest()
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
ipcRenderer.invoke('preload-extension-result', result);
|
||||
3
spec/fixtures/api/preload-realm/preload-send-ping.js
vendored
Normal file
3
spec/fixtures/api/preload-realm/preload-send-ping.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
ipcRenderer.send('ping');
|
||||
34
spec/fixtures/api/preload-realm/preload-tests.js
vendored
Normal file
34
spec/fixtures/api/preload-realm/preload-tests.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
const evalTests = {
|
||||
evalConstructorName: () => globalThis.constructor.name
|
||||
};
|
||||
|
||||
const tests = {
|
||||
testSend: (name, ...args) => {
|
||||
ipcRenderer.send(name, ...args);
|
||||
},
|
||||
testInvoke: async (name, ...args) => {
|
||||
const result = await ipcRenderer.invoke(name, ...args);
|
||||
return result;
|
||||
},
|
||||
testEvaluate: (testName, args) => {
|
||||
const func = evalTests[testName];
|
||||
const result = args
|
||||
? contextBridge.executeInMainWorld({ func, args })
|
||||
: contextBridge.executeInMainWorld({ func });
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
ipcRenderer.on('test', async (_event, uuid, name, ...args) => {
|
||||
console.debug(`running test ${name} for ${uuid}`);
|
||||
try {
|
||||
const result = await tests[name]?.(...args);
|
||||
console.debug(`responding test ${name} for ${uuid}`);
|
||||
ipcRenderer.send(`test-result-${uuid}`, { error: false, result });
|
||||
} catch (error) {
|
||||
console.debug(`erroring test ${name} for ${uuid}`);
|
||||
ipcRenderer.send(`test-result-${uuid}`, { error: true, result: error.message });
|
||||
}
|
||||
});
|
||||
3
spec/fixtures/api/service-workers/index.html
vendored
3
spec/fixtures/api/service-workers/index.html
vendored
@@ -2,7 +2,8 @@
|
||||
<html lang="en">
|
||||
<body>
|
||||
<script>
|
||||
navigator.serviceWorker.register('sw.js', {
|
||||
let scriptUrl = new URLSearchParams(location.search).get('scriptUrl') || 'sw.js';
|
||||
navigator.serviceWorker.register(scriptUrl, {
|
||||
scope: location.pathname.split('/').slice(0, 2).join('/') + '/'
|
||||
})
|
||||
</script>
|
||||
|
||||
10
spec/fixtures/api/service-workers/logs.html
vendored
10
spec/fixtures/api/service-workers/logs.html
vendored
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<script>
|
||||
navigator.serviceWorker.register('sw-logs.js', {
|
||||
scope: location.pathname.split('/').slice(0, 2).join('/') + '/'
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1
spec/fixtures/api/service-workers/sw-script-error.js
vendored
Normal file
1
spec/fixtures/api/service-workers/sw-script-error.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
throw new Error('service worker throwing on startup');
|
||||
3
spec/fixtures/api/service-workers/sw-unregister-self.js
vendored
Normal file
3
spec/fixtures/api/service-workers/sw-unregister-self.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
self.addEventListener('install', function () {
|
||||
registration.unregister();
|
||||
});
|
||||
Reference in New Issue
Block a user