Use pretender's passthrough for dynamically loaded scripts

This commit is contained in:
Robin Ward 2015-03-13 12:08:28 -04:00
parent f359cdeddc
commit 77f9d1e223
5 changed files with 131 additions and 28 deletions

View File

@ -3,8 +3,12 @@
import loadScript from 'discourse/lib/load-script';
export default function highlightSyntax($elem) {
const selector = Discourse.SiteSettings.autohighlight_all_code ? 'pre code' : 'pre code[class]';
const selector = Discourse.SiteSettings.autohighlight_all_code ? 'pre code' : 'pre code[class]',
path = Discourse.HighlightJSPath;
if (!path) { return; }
$(selector, $elem).each(function(i, e) {
loadScript(Discourse.HighlightJSPath).then(() => hljs.highlightBlock(e));
loadScript(path).then(() => hljs.highlightBlock(e));
});
}

View File

@ -49,9 +49,11 @@ Discourse.Ajax = Em.Mixin.create({
var performAjax = function(resolve, reject) {
args.headers = args.headers || {};
if (_trackView && (!args.type || args.type === "GET")) {
_trackView = false;
args.headers = { 'Discourse-Track-View': true };
args.headers['Discourse-Track-View'] = true;
}
args.success = function(xhr) {
@ -80,6 +82,10 @@ Discourse.Ajax = Em.Mixin.create({
if (!args.type) args.type = 'GET';
if (!args.dataType && args.type.toUpperCase() === 'GET') args.dataType = 'json';
if (args.dataType === "script") {
args.headers['Discourse-Script'] = true;
}
if (args.type === 'GET' && args.cache !== true) {
args.cache = false;
}

View File

@ -1,7 +1,7 @@
function parsePostData(query) {
var result = {};
const result = {};
query.split("&").forEach(function(part) {
var item = part.split("=");
const item = part.split("=");
result[item[0]] = decodeURIComponent(item[1]);
});
return result;
@ -25,15 +25,15 @@ const _widgets = [
];
export default function() {
var server = new Pretender(function() {
const server = new Pretender(function() {
// Load any fixtures automatically
var self = this;
const self = this;
Ember.keys(require._eak_seen).forEach(function(entry) {
if (/^fixtures/.test(entry)) {
var fixture = require(entry, null, null, true);
const fixture = require(entry, null, null, true);
if (fixture && fixture.default) {
var obj = fixture.default;
const obj = fixture.default;
Ember.keys(obj).forEach(function(url) {
self.get(url, function() {
return response(obj[url]);
@ -55,17 +55,8 @@ export default function() {
return response({});
});
this.get('/javascripts/jquery.magnific-popup-min.js', function() {
return response({});
});
this.get('/highlight.js', function() {
return response({});
});
this.post('/session', function(request) {
var data = parsePostData(request.requestBody);
const data = parsePostData(request.requestBody);
if (data.password === 'correct') {
return response({username: 'eviltrout'});
@ -130,10 +121,14 @@ export default function() {
};
server.unhandledRequest = function(verb, path) {
var error = 'Unhandled request in test environment: ' + path + ' (' + verb + ')';
const error = 'Unhandled request in test environment: ' + path + ' (' + verb + ')';
window.console.error(error);
throw error;
};
server.checkPassthrough = function(request) {
return request.requestHeaders['Discourse-Script'];
};
return server;
}

View File

@ -75,9 +75,6 @@ Discourse.injectTestHelpers();
Discourse.runInitializers();
Discourse.start();
Discourse.Route.mapRoutes();
Discourse.HighlightJSPath = "/highlight.js";
// messy but we need to pass tests
window.hljs = {highlightBlock: function(){}}
// disable logster error reporting
if (window.Logster) {

View File

@ -19,6 +19,7 @@ function Pretender(maps){
this.handlers = [];
this.handledRequests = [];
this.passthroughRequests = [];
this.unhandledRequests = [];
// reference the native XMLHttpRequest object so
@ -29,6 +30,9 @@ function Pretender(maps){
// the route map.
window.XMLHttpRequest = interceptor(this);
// "start" the server
this.running = true;
// trigger the route map DSL.
maps.call(this);
}
@ -41,10 +45,65 @@ function interceptor(pretender) {
// extend
var proto = new FakeXMLHttpRequest();
proto.send = function send(){
FakeXMLHttpRequest.prototype.send.apply(this, arguments);
pretender.handleRequest(this);
if (!pretender.running) {
throw new Error('You shut down a Pretender instance while there was a pending request. '+
'That request just tried to complete. Check to see if you accidentally shut down '+
'a pretender earlier than you intended to');
}
if (!pretender.checkPassthrough(this)) {
FakeXMLHttpRequest.prototype.send.apply(this, arguments);
pretender.handleRequest(this);
}
else {
var xhr = createPassthrough(this);
xhr.send.apply(xhr, arguments);
}
};
// passthrough handling
var evts = ['load', 'error', 'timeout', 'progress', 'abort', 'readystatechange'];
var lifecycleProps = ['readyState', 'responseText', 'responseXML', 'status', 'statusText'];
function createPassthrough(fakeXHR) {
var xhr = fakeXHR._passthroughRequest = new pretender._nativeXMLHttpRequest();
// listen to all events to update lifecycle properties
for (var i = 0; i < evts.length; i++) (function(evt) {
xhr['on' + evt] = function(e) {
// update lifecycle props on each event
for (var i = 0; i < lifecycleProps.length; i++) {
var prop = lifecycleProps[i];
if (xhr[prop]) {
fakeXHR[prop] = xhr[prop];
}
}
// fire fake events where applicable
fakeXHR.dispatchEvent(evt, e);
if (fakeXHR['on' + evt]) {
fakeXHR['on' + evt](e);
}
};
})(evts[i]);
xhr.open(fakeXHR.method, fakeXHR.url, fakeXHR.async, fakeXHR.username, fakeXHR.password);
xhr.timeout = fakeXHR.timeout;
xhr.withCredentials = fakeXHR.withCredentials;
return xhr;
}
proto._passthroughCheck = function(method, arguments) {
if (this._passthroughRequest) {
return this._passthroughRequest[method].apply(this._passthroughRequest, arguments);
}
return FakeXMLHttpRequest.prototype[method].apply(this, arguments);
}
proto.abort = function abort(){
return this._passthroughCheck('abort', arguments);
}
proto.getResponseHeader = function getResponseHeader(){
return this._passthroughCheck('getResponseHeader', arguments);
}
proto.getAllResponseHeaders = function getAllResponseHeaders(){
return this._passthroughCheck('getAllResponseHeaders', arguments);
}
FakeRequest.prototype = proto;
return FakeRequest;
}
@ -55,6 +114,22 @@ function verbify(verb){
};
}
function throwIfURLDetected(url){
var HTTP_REGEXP = /^https?/;
var message;
if(HTTP_REGEXP.test(url)) {
var parser = window.document.createElement('a');
parser.href = url;
message = "Pretender will not respond to requests for URLs. It is not possible to accurately simluate the browser's CSP. "+
"Remove the " + parser.protocol +"//"+ parser.hostname +" from " + url + " and try again";
throw new Error(message)
}
}
var PASSTHROUGH = {};
Pretender.prototype = {
get: verbify('GET'),
post: verbify('POST'),
@ -69,9 +144,29 @@ Pretender.prototype = {
var registry = this.registry[verb];
registry.add([{path: path, handler: handler}]);
},
passthrough: PASSTHROUGH,
checkPassthrough: function(request) {
var verb = request.method.toUpperCase();
var path = request.url;
throwIfURLDetected(path);
verb = verb.toUpperCase();
var recognized = this.registry[verb].recognize(path);
var match = recognized && recognized[0];
if (match && match.handler == PASSTHROUGH) {
this.passthroughRequests.push(request);
this.passthroughRequest(verb, path, request);
return true;
}
return false;
},
handleRequest: function handleRequest(request){
var verb = request.method.toUpperCase();
var path = request.url;
var handler = this._handlerFor(verb, path, request);
if (handler) {
@ -81,9 +176,10 @@ Pretender.prototype = {
try {
var statusHeadersAndBody = handler.handler(request),
status = statusHeadersAndBody[0],
headers = statusHeadersAndBody[1],
headers = this.prepareHeaders(statusHeadersAndBody[1]),
body = this.prepareBody(statusHeadersAndBody[2]);
request.respond(status, headers, body);
this.handledRequest(verb, path, request);
} catch (error) {
this.erroredRequest(verb, path, request, error);
@ -93,8 +189,10 @@ Pretender.prototype = {
this.unhandledRequest(verb, path, request);
}
},
prepareBody: function(body){ return body; },
handledRequest: function(verb, path, request){/* no-op */},
prepareBody: function(body) { return body; },
prepareHeaders: function(headers) { return headers; },
handledRequest: function(verb, path, request) { /* no-op */},
passthroughRequest: function(verb, path, request) { /* no-op */},
unhandledRequest: function(verb, path, request) {
throw new Error("Pretender intercepted "+verb+" "+path+" but no handler was defined for this type of request");
},
@ -116,6 +214,9 @@ Pretender.prototype = {
},
shutdown: function shutdown(){
window.XMLHttpRequest = this._nativeXMLHttpRequest;
// "stop" the server
this.running = false;
}
};