From 6a2a296ee05312d56de3ae47f5dfb6e04f877692 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Tue, 3 Feb 2015 16:15:41 +0000 Subject: [PATCH] feat(snippet): Add black/white lists - fixes #373 --- README.md | 2 +- lib/default-config.js | 7 +- lib/public/init.js | 37 +++++++- lib/server/index.js | 3 +- lib/snippet.js | 3 +- package.json | 4 +- test/specs/e2e/e2e.options.snippet.js | 74 +++++++++++++++- test/specs/e2e/proxy/e2e.proxy.snippet.js | 103 ++++++++++++++++++++++ 8 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 test/specs/e2e/proxy/e2e.proxy.snippet.js diff --git a/README.md b/README.md index 44e664578..68c7a3a85 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# BrowserSync [![Build status](https://ci.appveyor.com/api/projects/status/r5vung2ipn9uj4sy?svg=true)](https://ci.appveyor.com/project/shakyShane/browser-sync) [![Build Status](http://img.shields.io/travis/shakyShane/browser-sync/master.svg?style=flat)](https://travis-ci.org/shakyShane/browser-sync) [![Coverage Status](https://img.shields.io/coveralls/shakyShane/browser-sync.svg?style=flat)](https://coveralls.io/r/shakyShane/browser-sync?branch=master) [![NPM version](https://img.shields.io/npm/v/browser-sync.svg?style=flat)](https://www.npmjs.com/package/browser-sync) +# BrowserSync [![Build status](https://ci.appveyor.com/api/projects/status/r5vung2ipn9uj4sy?svg=true)](https://ci.appveyor.com/project/shakyShane/browser-sync) [![Build Status](https://travis-ci.org/BrowserSync/browser-sync.svg?branch=master)](https://travis-ci.org/BrowserSync/browser-sync) [![Coverage Status](https://img.shields.io/coveralls/shakyShane/browser-sync.svg?style=flat)](https://coveralls.io/r/shakyShane/browser-sync?branch=master) [![NPM version](https://img.shields.io/npm/v/browser-sync.svg?style=flat)](https://www.npmjs.com/package/browser-sync) > Keep multiple browsers & devices in sync when building websites. diff --git a/lib/default-config.js b/lib/default-config.js index 3613e697a..5a726bf99 100644 --- a/lib/default-config.js +++ b/lib/default-config.js @@ -140,13 +140,16 @@ module.exports = { * You can also provide patterns for certain urls * that should be ignored from the snippet injection. * @property snippetOptions - * @since 1.7.0 - * @param {String|Array} [ignorePaths=undefined] + * @since 2.0.0 + * @param {Array} [blacklis] + * @param {Array} [whitelist] * @param {RegExp} [rule.match=/<body[^>]*>/i] * @param {Function} [rule.fn=Function] * @type Object */ snippetOptions: { + whitelist: [], + blacklist: [], rule: { match: /]*>/i, fn: function (snippet, match) { diff --git a/lib/public/init.js b/lib/public/init.js index 279a90711..0affa0bf2 100644 --- a/lib/public/init.js +++ b/lib/public/init.js @@ -1,8 +1,9 @@ "use strict"; var Immutable = require("immutable"); -var merge = require("../cli/cli-options").merge; -var tfunk = require("eazy-logger").compile; +var _ = require("lodash"); +var merge = require("../cli/cli-options").merge; +var tfunk = require("eazy-logger").compile; /** * @param {BrowserSync} browserSync @@ -41,8 +42,40 @@ module.exports = function (browserSync, name, pjson) { item.set("startPath", path); } } + fixSnippetOptions(item); }); return browserSync.init(config, args.cb); }; }; + +/** + * Back-compat options for snippetOptions.ignorePaths + */ +function fixSnippetOptions (item) { + + var ignorePaths = item.getIn(["snippetOptions", "ignorePaths"]); + var includePaths = item.getIn(["snippetOptions", "whitelist"]); + + if (ignorePaths) { + if (_.isString(ignorePaths)) { + ignorePaths = [ignorePaths]; + } + ignorePaths = ignorePaths.map(ensureSlash); + item.setIn(["snippetOptions", "blacklist"], Immutable.List(ignorePaths)); + } + if (includePaths) { + includePaths = includePaths.map(ensureSlash); + item.setIn(["snippetOptions", "whitelist"], Immutable.List(includePaths)); + } +} + +/** + * Enforce paths to begin with a forward slash + */ +function ensureSlash (item) { + if (item[0] !== "/") { + return "/" + item; + } + return item; +} diff --git a/lib/server/index.js b/lib/server/index.js index 3ceb00133..daf29ec7a 100644 --- a/lib/server/index.js +++ b/lib/server/index.js @@ -171,7 +171,8 @@ module.exports.createProxy = function (options, scripts, bs) { var snippetOptions = options.get("snippetOptions").toJS(); var foxyServer = foxy(options.getIn(["proxy", "target"]), { rules: snippetUtils.getRegex(options.get("snippet"), options.get("snippetOptions")), - ignorePaths: snippetOptions.ignorePaths, + whitelist: snippetOptions.whitelist, + blacklist: snippetOptions.blacklist, middleware: mw, errHandler: function (err) { bs.logger.debug("{red:[proxy error]} %s", err.message); diff --git a/lib/snippet.js b/lib/snippet.js index 0cf70c8da..85aa8ec67 100644 --- a/lib/snippet.js +++ b/lib/snippet.js @@ -54,7 +54,8 @@ var utils = { getSnippetMiddleware: function (snippet, options) { return lrSnippet({ rules: [utils.getRegex(snippet, options)], - ignorePaths: options.get("ignorePaths") + blacklist: options.get("blacklist").toJS(), + whitelist: options.get("whitelist").toJS() }); }, /** diff --git a/package.json b/package.json index 5f4856960..91ebebfc4 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "easy-extender": "^2.2.0", "eazy-logger": "^2.1.1", "emitter-steward": "^0.0.1", - "foxy": "^8.1.1", + "foxy": "^9.0.0", "glob-watcher": "^0.0.7", "immutable": "^3.4.1", "localtunnel": "^1.3.0", @@ -50,7 +50,7 @@ "meow": "^3.0.0", "opn": "^1.0.0", "portscanner": "^1.0.0", - "resp-modifier": "^1.0.2", + "resp-modifier": "^2.0.1", "serve-index": "^1.6.1", "serve-static": "^1.4.2", "socket.io": "^1.3.2", diff --git a/test/specs/e2e/e2e.options.snippet.js b/test/specs/e2e/e2e.options.snippet.js index ba0f2d019..78d6fb7d1 100644 --- a/test/specs/e2e/e2e.options.snippet.js +++ b/test/specs/e2e/e2e.options.snippet.js @@ -12,12 +12,13 @@ describe("E2E snippet ignore paths test (1)", function () { before(function (done) { browserSync.reset(); var config = { - server: { + server: { baseDir: "test/fixtures" }, - open: false, + open: false, + logLevel: "silent", snippetOptions: { - ignorePaths: "iframe.html" + ignorePaths: "iframe.html" //back-compat } }; instance = browserSync(config, done).instance; @@ -38,6 +39,73 @@ describe("E2E snippet ignore paths test (1)", function () { }); }); }); +describe("E2E snippet blacklist paths test (1)", function () { + + var instance; + + before(function (done) { + browserSync.reset(); + var config = { + server: { + baseDir: "test/fixtures" + }, + open: false, + logLevel: "silent", + snippetOptions: { + blacklist: ["/iframe.html"] // correct syntax + } + }; + instance = browserSync(config, done).instance; + }); + + after(function () { + instance.cleanup(); + }); + + it("does not inject the snippet when excluded path hit", function (done) { + request(instance.server) + .get("/iframe.html") + .set("accept", "text/html") + .expect(200) + .end(function (err, res) { + assert.notInclude(res.text, instance.options.get("snippet")); + done(); + }); + }); +}); +describe("E2E snippet blacklist paths test (1)", function () { + + var instance; + + before(function (done) { + browserSync.reset(); + var config = { + server: { + baseDir: "test/fixtures" + }, + open: false, + logLevel: "silent", + snippetOptions: { + whitelist: ["/iframe.html"] // correct syntax + } + }; + instance = browserSync(config, done).instance; + }); + + after(function () { + instance.cleanup(); + }); + + it("Always injects snippet when path matches in whitelist", function (done) { + request(instance.server) + .get("/iframe.html") + .expect(200) + .end(function (err, res) { + assert.include(res.text, instance.options.get("snippet")); + done(); + }); + }); +}); describe("E2E snippet custom regex", function () { var instance; diff --git a/test/specs/e2e/proxy/e2e.proxy.snippet.js b/test/specs/e2e/proxy/e2e.proxy.snippet.js new file mode 100644 index 000000000..4780719b1 --- /dev/null +++ b/test/specs/e2e/proxy/e2e.proxy.snippet.js @@ -0,0 +1,103 @@ +"use strict"; + +var browserSync = require("../../../../index"); + +var connect = require("connect"); +var serveStatic = require("serve-static"); +var request = require("supertest"); +var assert = require("chai").assert; + +describe("E2E proxy test with snippet options: Whitelist", function () { + + var instance, server, options; + + before(function (done) { + + browserSync.reset(); + + var app = connect(); + app.use(serveStatic("./test/fixtures")); + server = app.listen(); + var proxytarget = "http://localhost:" + server.address().port; + + var config = { + proxy: proxytarget, + logLevel: "silent", + open: false, + snippetOptions: { + whitelist: ["/index-large.html"] + } + }; + + instance = browserSync.init([], config, function (err, bs) { + options = bs.options; + done(); + }).instance; + }); + + after(function () { + instance.cleanup(); + server.close(); + }); + + it("can init proxy & serve a page with whitelist option (injects snippet even without html headers)", function (done) { + + assert.isDefined(instance.server); + + request(instance.server) + .get("/index-large.html") + .expect(200) + .end(function (err, res) { + assert.include(res.text, options.get("snippet")); + done(); + }); + }); +}); + +describe("E2E proxy test with snippet options: blacklist", function () { + + var instance, server, options; + + before(function (done) { + + browserSync.reset(); + + var app = connect(); + app.use(serveStatic("./test/fixtures")); + server = app.listen(); + var proxytarget = "http://localhost:" + server.address().port; + + var config = { + proxy: proxytarget, + logLevel: "silent", + open: false, + snippetOptions: { + blacklist: ["/index-large.html"] + } + }; + + instance = browserSync.init([], config, function (err, bs) { + options = bs.options; + done(); + }).instance; + }); + + after(function () { + instance.cleanup(); + server.close(); + }); + + it("can init proxy & serve a page with whitelist option", function (done) { + + assert.isDefined(instance.server); + + request(instance.server) + .get("/index-large.html") + .set("accept", "text/html") + .expect(200) + .end(function (err, res) { + assert.notInclude(res.text, options.get("snippet")); + done(); + }); + }); +});