├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── CONTRIBUTING.md ├── History.md ├── README.md ├── bin └── bin.js ├── browser └── reporter.js ├── example ├── error.js └── simple.js ├── index.js ├── lib └── launch.js ├── package-lock.json ├── package.json ├── static └── index.html ├── tea.yaml └── test ├── close.js ├── electron.js ├── empty.js ├── error.js ├── input.js ├── spawn.js ├── static.js └── stream.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node: ['14', '16', '18'] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: ${{ matrix.node }} 16 | - run: npm ci 17 | - run: sudo apt-get install xvfb 18 | - run: xvfb-run --auto-servernum npm test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | node_modules 3 | static/reporter.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | browser/ 2 | test/ 3 | .* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | - 12 5 | - 14 6 | addons: 7 | apt: 8 | packages: 9 | - xvfb 10 | install: 11 | - export DISPLAY=':99.0' 12 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 13 | - npm install 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## `browser-run` is an **OPEN Open Source Projects** 2 | ----------------------------------------- 3 | 4 | ## What? 5 | 6 | Individuals making significant and valuable contributions are given commit-access to a project to contribute as they see fit. A project is more like an open wiki than a standard guarded open source project. 7 | 8 | ## Rules 9 | 10 | There are a few basic ground-rules for contributors: 11 | 12 | 1. **No `--force` pushes** or modifying the Git history in any way. 13 | 1. **Non-master branches** ought to be used for ongoing work. 14 | 1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors. 15 | 1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor. 16 | 1. Contributors should attempt to adhere to the prevailing code-style. 17 | 18 | ## Releases 19 | 20 | Declaring formal releases remains the prerogative of the project maintainer(s). 21 | 22 | ## Changes to this arrangement 23 | 24 | This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. 25 | 26 | ----------------------------------------- 27 | 28 | Taken with much kudos from https://github.com/Level/community/blob/master/CONTRIBUTING.md#level-projects-are-open-open-source-projects 29 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.4.0 / 2014-08-12 3 | ================== 4 | 5 | * update browserify for security reasons 6 | * add history 7 | 8 | 0.3.0 / 2014-07-13 9 | ================== 10 | 11 | * attempt fixing unicode problems 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # browser-run 2 | 3 | The easiest way of running code in a browser environment. 4 | 5 | Bundles `electronjs` by default! 6 | 7 | [![CI](https://github.com/juliangruber/browser-run/actions/workflows/ci.yml/badge.svg)](https://github.com/juliangruber/browser-run/actions/workflows/ci.yml) 8 | [![downloads](https://img.shields.io/npm/dm/browser-run.svg)](https://www.npmjs.org/package/browser-run) 9 | 10 | ## Usage 11 | 12 | ```bash 13 | $ echo "console.log('Hey from ' + location); window.close()" | browser-run 14 | Hey from http://localhost:53227/ 15 | $ 16 | ``` 17 | 18 | Or use `browser-run` programmatically: 19 | 20 | ```js 21 | var run = require('browser-run'); 22 | 23 | var browser = run(); 24 | browser.pipe(process.stdout); 25 | browser.end('console.log(location); window.close()'); 26 | ``` 27 | 28 | ## Example with browserify 29 | 30 | ```bash 31 | $ browserify main.js | browser-run 32 | ``` 33 | 34 | or 35 | 36 | ```js 37 | var browserify = require('browserify'); 38 | var browser = require('browser-run'); 39 | 40 | browserify('main.js').bundle().pipe(browser()).pipe(process.stdout); 41 | ``` 42 | 43 | ## CLI 44 | 45 | ```bash 46 | $ browser-run --help 47 | Run JavaScript in a browser. 48 | Write code to stdin and receive console output on stdout. 49 | Usage: browser-run [OPTIONS] 50 | 51 | Options: 52 | --version Show version number [boolean] 53 | -b, --browser Browser to use. Always available: electron. Available if 54 | installed: chrome, firefox, ie, safari [default: "electron"] 55 | --sandbox Enable electron sandbox [boolean] [default: true] 56 | --basedir Set this if you need to require node modules in node mode 57 | -h, --help Print help [boolean] 58 | -p, --port Starts listening on that port and waits for you to open a 59 | browser 60 | -s, --static Serve static assets from this directory 61 | -m, --mock Path to code to handle requests for mocking a dynamic back-end 62 | -i, --input Input type. Defaults to 'javascript', can be set to 'html'. 63 | -n, --node Enable nodejs apis in electron 64 | ``` 65 | 66 | ## Custom html file 67 | 68 | By using `--input html` or `{ input: 'html' }` you can provide a custom html file for browser-run to use. Keep in mind though that it always needs to have `` above other script tags so browser-run is able to properly forward your `console.log`s etc to the terminal. 69 | 70 | ## Dynamic back-end mock 71 | 72 | By using `--mock mock.js` or `{ mock: 'mock.js'}` you can provide a custom server-side implementation and handle all requests that are sent to paths beginning with `/mock` 73 | 74 | mock.js needs to export a function that accepts `req` and `res` arguments for handling requests. 75 | 76 | Example: 77 | 78 | ```js 79 | module.exports = function(req,res){ 80 | if (req.url === '/mock/echo') { 81 | req.pipe(res) 82 | } 83 | } 84 | ``` 85 | 86 | ## API 87 | 88 | ### run([opts]) 89 | 90 | Returns a duplex stream and starts a webserver. 91 | 92 | `opts` can be: 93 | 94 | * `port`: If speficied, no browser will be started, so you can point one yourself to `http://localhost/` 95 | * `browser`: Browser to use. Defaults to `electron`. Available if installed: 96 | * `chrome` 97 | * `firefox` 98 | * `ie` 99 | * `safari` 100 | * `static`: Serve static files from this directory 101 | * `mock`: Path to code to handle requests for mocking a dynamic back-end 102 | * `input`: Input type. Defaults to `javascript`, can be set to `html`. 103 | * `node`: Enable nodejs integration in electron 104 | * `sandbox`: Enable electron sandbox. Default: `true`. 105 | * `basedir`: Set this if you need to require node modules in `node` mode 106 | 107 | If only an empty string is written to it, an error will be thrown as there is nothing to execute. 108 | 109 | If you call `window.close()` inside the script, the browser will exit. 110 | 111 | ### run#stop() 112 | 113 | Stop the underlying webserver. 114 | 115 | ## Headless testing 116 | 117 | In environments without a screen, you can use `Xvfb` to simulate one. 118 | 119 | ### GitHub Actions 120 | 121 | This is a full example to run `npm test`. Refer to the last 2 lines in the YAML config: 122 | 123 | ```yml 124 | on: 125 | - pull_request 126 | - push 127 | 128 | jobs: 129 | test: 130 | runs-on: ubuntu-latest 131 | steps: 132 | - uses: actions/checkout@v4 133 | - run: npm install 134 | - run: xvfb-run npm test 135 | timeout-minutes: 5 # If the tests fails, the browser will hang open indefinitely 136 | ``` 137 | 138 | ### Travis 139 | 140 | Add this to your travis.yml: 141 | 142 | ```yml 143 | addons: 144 | apt: 145 | packages: 146 | - xvfb 147 | install: 148 | - export DISPLAY=':99.0' 149 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 150 | - npm install 151 | ``` 152 | 153 | [Full example](https://github.com/rhysd/Shiba/blob/055a11a0a2b4f727577fe61371a88d8db9277de5/.travis.yml). 154 | 155 | ### Any gnu/linux box 156 | 157 | ```bash 158 | $ sudo apt-get install xvfb # or equivalent 159 | $ export DISPLAY=':99.0' 160 | $ Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 161 | $ browser-run ... 162 | ``` 163 | 164 | ### Docker 165 | 166 | There is also an example [Docker image](https://hub.docker.com/r/kipparker/docker-tape-run). [Source](https://github.com/fraserxu/docker-tape-run) 167 | 168 | ## Installation 169 | 170 | With [npm](http://npmjs.org) do 171 | 172 | ```bash 173 | $ npm install browser-run # for library 174 | $ npm install -g browser-run # for cli 175 | ``` 176 | 177 | ## Sponsors 178 | 179 | This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)! 180 | 181 | Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)! 182 | 183 | ## License 184 | 185 | (MIT) 186 | -------------------------------------------------------------------------------- /bin/bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var run = require('..'); 4 | var yargs = require('yargs/yargs') 5 | 6 | var argv = yargs(process.argv.slice(2)) 7 | .usage( 8 | 'Run JavaScript in a browser.\n' + 9 | 'Write code to stdin and receive console output on stdout.\n' + 10 | 'Usage: $0 [OPTIONS]' 11 | ) 12 | 13 | .describe('browser', 'Browser to use. ' 14 | + 'Always available: electron. ' 15 | + 'Available if installed: ' 16 | + 'chrome, firefox, ie, safari') 17 | .alias('browser', 'b') 18 | .default('browser', 'electron') 19 | 20 | .describe('port', 'Starts listening on that port and waits for you to open a browser') 21 | .alias('p', 'port') 22 | 23 | .describe('static', 'Serve static assets from this directory') 24 | .alias('s', 'static') 25 | 26 | .describe('mock', 'Path to code to handle requests for mocking a dynamic back-end') 27 | .alias('m', 'mock') 28 | 29 | .describe('input', 'Input type. Defaults to \'javascript\', can be set to \'html\'.') 30 | .alias('i', 'input') 31 | 32 | .describe('node', 'Enable nodejs apis in electron') 33 | .alias('n', 'node') 34 | 35 | .describe('sandbox', 'Enable electron sandbox') 36 | .boolean('sandbox') 37 | .default('sandbox', true) 38 | 39 | .describe('basedir', 'Set this if you need to require node modules in node mode') 40 | 41 | .help('h') 42 | .describe('help', 'Print help') 43 | .alias('h', 'help') 44 | 45 | .argv; 46 | 47 | argv.nodeIntegration = argv['node-integration'] 48 | 49 | process.stdin 50 | .pipe(run(argv)) 51 | .pipe(process.stdout); 52 | -------------------------------------------------------------------------------- /browser/reporter.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | require('source-map-support').install(); 3 | 4 | if (!/Electron/.test(window.navigator.userAgent)) { 5 | var close = window.close; 6 | window.close = function () { 7 | setTimeout(function () { 8 | close.call(window); 9 | }, 1000); 10 | }; 11 | 12 | window.onerror = function (msg, file, line, column, err) { 13 | if (err && msg.indexOf(err.stack) > -1) { 14 | err.stack = err.stack + '\n at ' + file + ':' + line + ':' + column 15 | } 16 | console.error(err && err.stack 17 | || err 18 | || back); 19 | }; 20 | 21 | var xws = require('xhr-write-stream')('/xws'); 22 | var ws = require('utf8-stream')(); 23 | ws.pipe(xws); 24 | 25 | var console = window.console || {}; 26 | var methods = ['log', 'error', 'warn', 'dir', 'debug', 'info', 'trace']; 27 | for (var i = 0; i < methods.length; i++) (function (method) { 28 | var old = console[method]; 29 | console[method] = function (msg) { 30 | ws.write(Array.prototype.slice.call(arguments, 0).join(' ') + '\n'); 31 | if (old) old.apply(console, arguments); 32 | if (msg instanceof Error && typeof JSON != 'undefined') { 33 | ws.write(JSON.stringify(msg) + '\n'); 34 | } 35 | } 36 | })(methods[i]); 37 | } 38 | 39 | })(); 40 | -------------------------------------------------------------------------------- /example/error.js: -------------------------------------------------------------------------------- 1 | function foo(){ throw new Error('bar') } 2 | console.log('about to call foo'); 3 | foo(); 4 | -------------------------------------------------------------------------------- /example/simple.js: -------------------------------------------------------------------------------- 1 | console.log(location); 2 | window.close(); 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var path = require('path'); 3 | var spawn = require('child_process').spawn; 4 | var through = require('through'); 5 | var duplex = require('duplexer'); 6 | var fs = require('fs'); 7 | var xws = require('xhr-write-stream')(); 8 | var enstore = require('enstore'); 9 | var launch = require('./lib/launch'); 10 | var ecstatic = require('ecstatic'); 11 | var injectScript = require('html-inject-script'); 12 | var destroyable = require('server-destroy'); 13 | var extend = require('xtend') 14 | 15 | try { 16 | fs.statSync(__dirname + '/static/reporter.js') 17 | } catch (_) { 18 | console.error('Reporter script missing. Run `npm run build` first.') 19 | } 20 | 21 | module.exports = function (opts) { 22 | if (!opts) opts = {}; 23 | if ('number' == typeof opts) opts = { port: opts }; 24 | if (!opts.browser) opts.browser = 'electron'; 25 | if (!opts.input) opts.input = 'javascript'; 26 | return runner(opts); 27 | }; 28 | 29 | function runner (opts) { 30 | var empty = true; 31 | var input = through(function (chunk) { 32 | if (empty && chunk.toString().trim() != '') empty = false; 33 | this.queue(chunk); 34 | }, function () { 35 | if (empty) dpl.emit('error', new Error('javascript required')); 36 | this.queue(null); 37 | }); 38 | var bundle = enstore(); 39 | input.pipe(bundle.createWriteStream()); 40 | var output = through(); 41 | var dpl = duplex(input, output); 42 | 43 | var mockHandler = opts.mock && require(path.resolve('./', opts.mock)) 44 | 45 | var server = http.createServer(function (req, res) { 46 | if (opts.input === 'javascript') { 47 | if (/^\/bundle\.js/.test(req.url)) { 48 | res.setHeader('content-type', 'application/javascript'); 49 | res.setHeader('cache-control', 'no-cache'); 50 | bundle.createReadStream().pipe(res); 51 | return; 52 | } 53 | 54 | if (req.url == '/') { 55 | fs.createReadStream(__dirname + '/static/index.html').pipe(res); 56 | return; 57 | } 58 | } else if (opts.input === 'html') { 59 | if (req.url == '/') { 60 | bundle.createReadStream().pipe(injectScript(['/reporter.js'])).pipe(res); 61 | return; 62 | } 63 | } 64 | 65 | if (req.url == '/xws') { 66 | req.pipe(xws(function (stream) { 67 | stream.pipe(output); 68 | })); 69 | return req.on('end', res.end.bind(res)); 70 | } 71 | if (req.url == '/reporter.js') { 72 | res.setHeader('content-type', 'application/javascript'); 73 | fs.createReadStream(__dirname + '/static/reporter.js').pipe(res); 74 | return; 75 | } 76 | if (opts.static) { 77 | ecstatic({ root: opts.static })(req, res); 78 | return; 79 | } 80 | if (mockHandler && '/mock' === req.url.substr(0,5)){ 81 | return mockHandler(req, res); 82 | } 83 | 84 | res.end('not supported'); 85 | }); 86 | destroyable(server); 87 | 88 | var browser; 89 | 90 | if (opts.port) { 91 | server.listen(opts.port); 92 | } else { 93 | server.listen(function () { 94 | var address = server.address(); 95 | if (!address) return; // already closed 96 | var port = address.port; 97 | 98 | launch(extend(opts, { 99 | loc: 'http://localhost:' + port, 100 | name: opts.browser, 101 | bundle: bundle 102 | }), function(err, _browser){ 103 | if (err) return dpl.emit('error', err); 104 | browser = _browser; 105 | 106 | // electron 107 | if (browser.pipe) { 108 | browser.setEncoding('utf8'); 109 | browser.pipe(output); 110 | } 111 | 112 | browser.on('exit', function (code, signal) { 113 | try { server.destroy() } catch (e) {} 114 | dpl.emit('exit', code, signal); 115 | }); 116 | }); 117 | }); 118 | } 119 | 120 | dpl.stop = function () { 121 | try { server.destroy(); } catch (e) {} 122 | if (browser) browser.kill(); 123 | }; 124 | 125 | return dpl; 126 | } 127 | -------------------------------------------------------------------------------- /lib/launch.js: -------------------------------------------------------------------------------- 1 | var launcher = require('browser-launcher'); 2 | var Electron = require('electron-stream'); 3 | 4 | module.exports = function(opts, cb){ 5 | if (/^electron/.test(opts.name)) { 6 | var browser = Electron(opts); 7 | if (opts.input === 'javascript') { 8 | opts.bundle.createReadStream().pipe(browser); 9 | } else { 10 | browser.end('location = ' + JSON.stringify(opts.loc)); 11 | } 12 | process.nextTick(cb.bind(null, null, browser)); 13 | } else { 14 | launcher(function(err, launch){ 15 | if (err) return cb(err); 16 | launch(opts.loc, { 17 | headless: false, 18 | browser: opts.name 19 | }, cb); 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browser-run", 3 | "description": "Transform stream that executes JavaScript it receives in a real browser and outputs console output", 4 | "version": "12.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/juliangruber/browser-run.git" 8 | }, 9 | "homepage": "https://github.com/juliangruber/browser-run", 10 | "main": "index.js", 11 | "bin": { 12 | "browser-run": "./bin/bin.js" 13 | }, 14 | "scripts": { 15 | "build": "browserify browser/reporter.js -o static/reporter.js", 16 | "prepublish": "npm run build", 17 | "release": "np", 18 | "test": "npm run build && node--test" 19 | }, 20 | "dependencies": { 21 | "browser-launcher": "^3.0.1", 22 | "duplexer": "^0.1.1", 23 | "ecstatic": "^4.1.2", 24 | "electron-stream": "^11.0.0", 25 | "enstore": "^1.0.1", 26 | "html-inject-script": "^2.0.0", 27 | "server-destroy": "^1.0.1", 28 | "source-map-support": "^0.4.0", 29 | "through": "^2.3.8", 30 | "xhr-write-stream": "^0.1.2", 31 | "xtend": "^4.0.1", 32 | "yargs": "^16.2.0" 33 | }, 34 | "devDependencies": { 35 | "browserify": "^14.1.0", 36 | "concat-stream": "^1.5.1", 37 | "np": "^8.0.4", 38 | "test": "^3.0.0", 39 | "tree-kill": "^1.0.0", 40 | "utf8-stream": "^0.0.0" 41 | }, 42 | "keywords": [ 43 | "browser", 44 | "stream", 45 | "test", 46 | "headless", 47 | "duplex" 48 | ], 49 | "author": { 50 | "name": "Julian Gruber", 51 | "email": "mail@juliangruber.com", 52 | "url": "http://juliangruber.com" 53 | }, 54 | "license": "MIT", 55 | "engines": { 56 | "node": ">=14" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0xE7DEE1B8Bb97C3065850Cf582D6DED57C6009587' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /test/close.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var run = require('..'); 4 | 5 | test('window.close()', function (t, done) { 6 | var browser = run(); 7 | var eventCount = 0; 8 | 9 | browser.on('data', function (data) { 10 | assert.strictEqual(data, 'foo\n', 'data'); 11 | if (++eventCount === 2) done(); 12 | }); 13 | 14 | browser.on('exit', function (code) { 15 | assert.strictEqual(code, 0, 'exit'); 16 | if (++eventCount === 2) done(); 17 | }); 18 | 19 | browser.write('console.log("foo");'); 20 | browser.write('window.close();'); 21 | browser.end(); 22 | }); 23 | -------------------------------------------------------------------------------- /test/electron.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var run = require('..'); 4 | var concat = require('concat-stream'); 5 | 6 | test('electron', async function (t) { 7 | await t.test('nodeIntegration off by default', function (t, done) { 8 | var browser = run({ 9 | browser: 'electron' 10 | }); 11 | let eventCount = 0; 12 | 13 | browser.pipe(concat(function(data){ 14 | assert(!/electron\.asar/.test(data.toString())); 15 | if (++eventCount === 2) done(); 16 | })); 17 | 18 | browser.on('exit', function (code) { 19 | assert.strictEqual(code, 0, 'exit'); 20 | if (++eventCount === 2) done(); 21 | }); 22 | 23 | browser.write('console.log(__dirname);'); 24 | browser.write('window.close();'); 25 | browser.end(); 26 | }); 27 | await t.test('nodeIntegration on', function (t) { 28 | var browser = run({ 29 | browser: 'electron', 30 | nodeIntegration: true 31 | }); 32 | let eventCount = 0; 33 | 34 | browser.pipe(concat(function(data){ 35 | assert(/browser-run/.test(data.toString())); 36 | if (++eventCount === 2) done(); 37 | })); 38 | 39 | browser.on('exit', function (code) { 40 | assert.strictEqual(code, 0, 'exit'); 41 | if (++eventCount === 2) done(); 42 | }); 43 | 44 | browser.write('console.log(__dirname);'); 45 | browser.write('window.close();'); 46 | browser.end(); 47 | }); 48 | await t.test('basedir option', function (t) { 49 | var browser = run({ 50 | browser: 'electron', 51 | node: true, 52 | basedir: __dirname + '/../' 53 | }); 54 | let eventCount = 0; 55 | 56 | browser.pipe(concat(function(data){ 57 | assert(/^true\n$/.test(data.toString())); 58 | if (++eventCount === 2) done(); 59 | })); 60 | 61 | browser.on('exit', function (code) { 62 | assert.strictEqual(code, 0, 'exit'); 63 | if (++eventCount === 2) done(); 64 | }); 65 | 66 | browser.write('console.log(!!require.resolve("test"));'); 67 | browser.write('window.close();'); 68 | browser.end(); 69 | }); 70 | await t.test('supports async functions', function (t) { 71 | var browser = run({ 72 | browser: 'electron' 73 | }); 74 | let eventCount = 0; 75 | 76 | browser.pipe(concat(function(data){ 77 | assert.strictEqual(data.toString(), 'ok\n'); 78 | if (++eventCount === 2) done(); 79 | })); 80 | 81 | browser.on('exit', function (code) { 82 | assert.strictEqual(code, 0, 'exit'); 83 | if (++eventCount === 2) done(); 84 | }); 85 | 86 | browser.write('const fn = async () => \'ok\';'); 87 | browser.write('fn().then(text => { console.log(text); window.close(); })'); 88 | browser.end(); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/empty.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var run = require('..'); 4 | 5 | test('empty', function (t, done) { 6 | var browser = run(); 7 | 8 | browser.on('data', function (data) { 9 | throw new Error(data.toString()); 10 | }); 11 | 12 | browser.on('error', function (err) { 13 | browser.stop(); 14 | assert(err); 15 | done(); 16 | }); 17 | 18 | browser.write(' '); 19 | browser.end(); 20 | }); 21 | -------------------------------------------------------------------------------- /test/error.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var run = require('..'); 4 | 5 | test('error', function (t, done) { 6 | var browser = run({ browser: 'foobar' }); 7 | 8 | browser.on('error', function (err) { 9 | browser.stop(); 10 | assert(err); 11 | done(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/input.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var run = require('..'); 4 | var concat = require('concat-stream'); 5 | 6 | test('input=javascript (default)', function (t, done) { 7 | var browser = run(); 8 | let eventCount = 0; 9 | 10 | browser.pipe(concat(function(data){ 11 | assert.strictEqual('\n', data.toString()); 12 | if (++eventCount === 2) done(); 13 | })); 14 | 15 | browser.on('exit', function (code) { 16 | assert.strictEqual(code, 0, 'exit'); 17 | if (++eventCount === 2) done(); 18 | }); 19 | 20 | browser.write('console.log(document.title);'); 21 | browser.write('window.close();'); 22 | browser.end(); 23 | }); 24 | 25 | test('input=html', function (t, done) { 26 | var browser = run({ 27 | input: 'html' 28 | }); 29 | let eventCount = 0; 30 | 31 | browser.pipe(concat(function(data){ 32 | assert.strictEqual('FOO\n', data.toString()); 33 | if (++eventCount === 2) done(); 34 | })); 35 | 36 | browser.on('exit', function (code) { 37 | assert.strictEqual(code, 0, 'exit'); 38 | if (++eventCount === 2) done(); 39 | }); 40 | 41 | browser.write('FOO'); 42 | browser.write(''); 43 | browser.end(); 44 | }); 45 | -------------------------------------------------------------------------------- /test/spawn.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var spawn = require('child_process').spawn; 4 | var kill = require('tree-kill'); 5 | 6 | test('spawn', function (t, done) { 7 | var ps = spawn(__dirname + '/../bin/bin.js'); 8 | 9 | ps.stdout.on('data', function (data) { 10 | assert.strictEqual(data.toString(), 'foo\n'); 11 | kill(ps.pid); 12 | done(); 13 | }); 14 | ps.stderr.on('data', d => { throw new Error(d.toString()) }); 15 | 16 | ps.stdin.write('console.log("foo");'); 17 | ps.stdin.end(); 18 | }); 19 | -------------------------------------------------------------------------------- /test/static.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var run = require('..'); 4 | var fs = require('fs'); 5 | 6 | test('static', function (t, done) { 7 | var browser = run({ static: __dirname }); 8 | 9 | browser.on('data', function (data) { 10 | assert.strictEqual(data.trim(), fs.readFileSync(`${__dirname}/static.js`).toString().trim()); 11 | done(); 12 | }); 13 | 14 | browser.write('fetch("/static.js").then(res => res.text()).then(body => {console.log(body); window.close()})'); 15 | browser.end(); 16 | }); 17 | -------------------------------------------------------------------------------- /test/stream.js: -------------------------------------------------------------------------------- 1 | var test = require('test'); 2 | var assert = require('assert'); 3 | var run = require('..'); 4 | 5 | test('stream', function (t, done) { 6 | var browser = run(); 7 | 8 | browser.on('data', function (data) { 9 | browser.stop(); 10 | assert.strictEqual(data, 'foo\n', 'correct stdout'); 11 | done(); 12 | }); 13 | 14 | browser.end('console.log("foo")'); 15 | }); 16 | --------------------------------------------------------------------------------