This library contains a browser VM that is intented for debugging, designing, and editing web applications. It comes with loads of features that enables you perform certain tasks against your application that you couldn't otherwise do in a normal web browser.
How does this work?
- Render HTML & CSS against any browser
- Edit the source file of any DOM object
- Defer application execution to workers
- Register your own custom protocols
- DOM objects are diffed & patched
- Register custom file editors
- Custom dependency graph strategy
Render HTML & CSS against any browser
This is a planned feature for this library. Though, it's setup to allow native rendering for any engine you want. Here's an example:
// doesn't exist yet;;;document.body.appendChildbrowser.renderer.element; // return WebRTC preview of what's being rendered in Chrome;document.body.appendChildbrowser.renderer.element; // IE WebRTC preview
The renderer receives just the HTML & CSS of the application, and provides visual information about each visible element on the page including bounding rectangles (
getBoundingClientRect()), and computed styles
window.getComputedStyle(element). DOM events (clicks, inputs, user events) are also passed to & from the renderer.
Diffing & Patching DOM objects
The synthetic browser comes with diffing & patching capabilities (similar to how ReactJS works) that only updates HTML & CSS that have changed. HTML & CSS patching works with any URI resource loaded in the synthetic browser, and is triggered whenever the target application reloads (which occurs when a file resource changes).
There are a few motivations behind the diffing & patching functionality:
- It ensures that DOM objects (HTML elements and CSS rules) are kept as the user is editing their application. This also means that users can make visual adjustments to HTML & CSS and see immediate results while the synthetic browser is applying their edits to source code in the background.
- Patches can be sent over the network while diffing & application execution occurs in another process.
Here's a basic example:
const browser = kernel ;// show the app previewdocumentbody;// open the URI to previewawait browser;// flip the body upside downconst body = browserdocumentbody;// apply immediately so that the user sees thisbodystyletransform = "rotate(180)";// dispatch a source file edit.const edit = body;edit;// dispatch source file updateawait PrivateBusProvider;// after some time the synthetic browser will reload with the new changes;
Edit source file of any DOM object
The mutations API is used to diff & patch DOM objects whenever an application reloads. The APIs can also be used to apply edits to DOM object source files.
;;;// messaging channel for the application;// use just a basic DOM renderer for the synthetic browser. Can be// something such as SafariDOMRenderer, IEDOMRenderer in the future.;// display the preview of the web applicationdocument.body.appendChildrenderer.element;;// load google into the synthetic browser. Note that for this to work you'll either// need to run this example in an Electron app, or register a custom protocol handler that proxies all HTTP requests through a backend.await browser.open"";;browser.document.querySelectorAll"div".forEach;// dispatch a file edit request that will modify the source code// of each mutated DOM element above.await bus.dispatchnew ApplyFileEditRequestmutations;// synthetic browser SHOULD be reloaded at this point, and the changes// displayed to the user
You'll notice above that file mutations can be applied to any resource that's currently loaded in the synthetic browser, including files from a remote HTTP server.
File mutations don't actually mutate the original source file. Instead, they're applied to an intermediate caching layer which you can access like so:
;;await fileCache.save"cache://my/fake/file.txt", ;;;console.logcontent; // hello world
Note that the cache layer will load files from their original source if they do not exist in memory. The caching layer will also watch source files for any changes (either via file watchers, long polling, or short polling), and keep itself up to date.
Using our previous synthetic browser, we can access the cached file where mutations are applied to. Here's an example assuming that the edits are being to "http://google.com/index.html":
// ... PREVIOUS CODE// dispatch a file edit request that will modify the source code// of each mutated DOM element above.await bus.dispatchnew ApplyFileEditRequestmutations;;;await = await tmpFile.read;console.logcontent; // <html>... <div style="color:red;">...</div></html>
If you have multiple instances of a synthetic browser that are all loading different variations of a web application and use the same kernel object, then each one of them will automatically reload whenever a shared cache resource changes. For example:
;;await browser1.open"";await browser2.open"";
;;styleSheetClone.cssText = ""; // remove ALL styles;// diff against clone & capture all REMOVE mutationsedit.fromDiffstyleSheet;// dispatch file edits that will be applied to say...bus.dispatchnew ApplyFileEditRequestedit.mutations;
browser2 instances will receive the above file change assuming that they both share the
main.css style sheet (they likely would).
Defer application execution to worker
You can connect synthetic browsers together to offload some of the work to other processes. This was primarily created for Tandem to ensure that the UI doesn't lock-up whenever a user visually edits their web application (edits are defered to a separate NodeJS process).
[ EXAMPLE ]
It's also possible to defer web application execution to the cloud through AWS Lamba or some other service. The benefit around this would be to offload all of the heavy work to a remote process, allowing you to run many variations of your application. This is great especially for visual Q/A testing.
Register your own custom protocol
This library currently supports
http(s):// protocols out of the box. If you need to add your own, you can easily do that:
The above example demonstrates a custom
cache:// protocol that can be used in a synthetic browser session.
Register custom file editors
Visual Q/A Testing
This library is great for visual Q/A testing since you can run many variations of your application under the same process with different pages, states, and rendering engines. Here's a basic example:
- TODO - docs on defering application execution
Custom dependency graph strategy
Still a work in progress
Tandem will come with (core currently supports it) custom dependency graph strategies for Webpack, Rollup, Browserify, and other bundling systems. For example:
Here's what the
index.js file might look like:
;;;const HelloComponent extends React.Componentrenderreturn <h1>Hello this.props.message </h1>;}}ReactDOM;
With the given
webpack strategy, the synthetic browser will use your
webpack.config.js to map & transpile your application dependencies. After that, the synthetic browser will run your application like it would in a normal browser. Any change to a mapped dependency will automatically trigger an application reload.
Contains all dependencies. Used for dependency injection.
Kernel, renderer?: ISyntheticRenderer)SyntheticBrowser(kernel:
Creates a new synthetic browser that executes web application code in the current process.