food-runner v2.0.0
Food Runner
Tools for building web apps inside of browser extensions
Introduction
Food Runner provides solutions for a lot of thorny problems you might run into while developing web apps that are designed to work inside of browser extensions.
Food Runner offers the following features:
- Mocks for browser features, so you can run automated tests on your code in a Javascript server instead of a browser.
- An RPC-like interface to the browser's message-passing systems, so you can express inter-context communications more easily, like those between pages, or even other extensions.
- A context system that lets you emulate multiple pages and extensions at the same time while testing.
- Utilities for dealing with raw binary data easily and efficiently,
including:
- serialization and deserialization using BSON
- encoding and decoding using base 64
- High-level utilities for working with persistent browser storage.
- Tryout: a utility getting rapid visual feedback when developing UI views
Wait, what? What's a food runner?
In the restaurant industry, a food runner is the person who brings food from the kitchen to the table. They're a kind of communication channel between a restaurant's guests and its operators.
It seemed like a good metaphor for a piece of software whose main job is to facilitate communication between front-end and back-end pages in a browser extension.
Getting Started
Install the package using your favorite package manager using the package name:
food-runner
Creating a service
On your extension's background page:
import runner from 'food-runner';
export class TestService {
/**
* @param {string} name
* @returns {Promise.<string>}
*/
async greet(name) {
return 'hello, ' + name;
}
}
runner.server.serveInternal(new TestService());
Using a service
On your extension's application page:
import runner from 'food-runner';
/** @type {TestService} */
const service = await runner.client.get();
const greeting = await bg.greet('bob');
// greeting should be 'hello, bob'
Using a service from a different extension
Let's assume the server extension's id is server
,
and that extension is running the background page:
import runner from 'food-runner';
export class TestService {
/**
* @param {string} name
* @returns {Promise.<string>}
*/
async greet(name) {
return 'hello, ' + name;
}
}
runner.server.serveExternal(new TestService());
Let's assume the client extension's id is client
,
running an extension page:
import runner from 'food-runner';
/** @type {TestService} */
const service = await runner.client.get('server');
const greeting = await bg.greet(browser.runtime.id);
// greeting should be 'hello, client'
Running tests using services
Use the Food Runner globals
API to switch window
and browser
global mocks as necessary.
If you're using Jasmine to express tests, try something like this:
import runner from 'food-runner';
import * as globals from 'food-runner/testlib/globals.js';
describe("My Tests", () => {
it("test a service", async () => {
// create a context for the extension background page
const extId = 'ext';
const ext = globals.create(extId);
await ext.run('test/scripts/background.js');
// become the context for a browser tab holding an extension page
const tabId = 5;
globals.create(extId, tabId).replace();
// use the service on the background page
const bg = await runner.client.get();
await bg.doAThing();
});
});
More examples
For more examples, see the extensive test suite.
Limitations
Since Javascript has no good way to express asynchronous property getters and setters, only function calls are relayed between contexts. Properties on the server side will not be accessible to the client side.
Documentation
This is a very new project. Documentation practically non-existent at the moment.
The source code itself has many JSDoc comments, and the Getting Started section above has some simple examples. That's about it so far.
If/when this project gets more attention, the documention will be improved.
License
Copyright (C) 2022 Cuchaz Interactive, LLC and contributors (see AUTHORS.md)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.