Skip to content

Instantly share code, notes, and snippets.

@bsredbeard
Created January 16, 2019 05:51
Show Gist options
  • Save bsredbeard/0bab118ee916935092198b0e5b8b502e to your computer and use it in GitHub Desktop.
Save bsredbeard/0bab118ee916935092198b0e5b8b502e to your computer and use it in GitHub Desktop.
function Verifier(name, calls){
if(!Array.isArray(calls)){
throw new Error(`Expected ${name} to have been called, but no calls were found.`);
}
var scopeCalls = calls;
this.withArguments = (...args) => {
scopeCalls = calls.filter((call) => {
return call.length === args.length &&
call.map((arg, idx) => arg === args[idx])
.reduce((x,y) => x && y, true);
});
if(scopeCalls.length < 1){
throw new Error(`Expected ${name} to have been called with [${args.join(', ')}], but it wasn't.`)
}
return this;
};
this.times = (numCalls) => {
if(numCalls !== scopeCalls.length) {
throw new Error(`Expected ${name} to have been called ${numCalls} times, but was called ${calls.length} times instead.`);
}
return this;
};
this.and = () => {
scopeCalls = calls;
return this;
}
Object.defineProperties(this, {
calls: {
get() { return scopeCalls.slice(); },
enumerable: true
},
length: {
get() { return scopeCalls.length; },
enumerable: true
}
});
}
function MockLocation(url){
const urlMatcher = /^([^:]+):\/+([^\/?#]+)(\/[^?#]*)?(\?[^#]+)?(#.*)?/;
const userHostPortMatcher = /^(?:([^@]+)@)?([^:]+):?(\d+)?$/;
const makeState = () => ({
href: undefined,
protocol: undefined,
host: undefined,
hostname: undefined,
port: undefined,
pathname: undefined,
search: undefined,
hash: undefined,
userpassword: undefined
});
var state = null;
const mocking = {};
function record() {
const args = Array.prototype.slice.apply(arguments, []);
const name = args.shift();
if(!mocking[name]){
mocking[name] = [];
}
mocking[name].push(args);
}
Object.defineProperties(this, {
href: {
get() {
record('get_href')
return state.href;
},
set(val) {
record('set_href', val);
const match = urlMatcher.exec(val);
if(match){
state = makeState();
state.href = val;
state.protocol = match[1];
state.pathname = match[3] || '/';
state.search = match[4] || '?';
state.hash = match[5] || '#';
const userHostPort = userHostPortMatcher.exec(match[2]);
if(userHostPort){
state.userpassword = userHostPort[1];
state.hostname = userHostPort[2];
state.port = userHostPort[3] || '80';
state.host = state.hostname + ':' + state.port;
} else {
state.host = match[2];
state.hostname = match[2]
}
} else {
throw new Error('Invalid url: ' + url);
}
}
},
protocol: {
get() {
record('get_protocol');
return state.protocol;
},
set() { throw new Error('location.protocol is a readonly property'); }
},
host: {
get() {
record('get_host')
return state.host;
},
set() { throw new Error('location.host is a readonly property'); }
},
hostname: {
get() {
record('get_hostname');
return state.hostname;
},
set() { throw new Error('location.host is a readonly property'); }
},
port: {
get() {
record('get_port');
return state.port;
},
set() { throw new Error('location.port is a readonly property'); }
},
pathname: {
get() {
record('get_pathname');
return state.pathname;
},
set() { throw new Error('location.pathname is a readonly property'); }
},
search: {
get() {
record('get_search');
return state.search;
},
set() { throw new Error('location.search is a readonly property'); }
},
hash: {
get() {
record('get_hash');
return state.hash;
},
set() { throw new Error('location.hash is a readonly property'); }
},
username: {
get() {
record('get_username');
var m = /[^:]+/.exec(state.userpassword||'');
return m ? m[0] : undefined;
},
set() { throw new Error('location.username is a readonly property'); }
},
password: {
get() {
record('get_password');
var m = /:(.+)/.exec(state.userpassword||'');
return m ? m[1] : undefined;
},
set() { throw new Error('location.password is a readonly property'); }
},
origin: {
get() {
record('get_origin');
return `${state.protocol}://${state.host}`;
},
set() { throw new Error('location.origin is a readonly property'); }
},
assign: {
value: (newUrl) => {
record('call_assign', newUrl);
this.href = newUrl;
},
writable: false
},
replace: {
value: (newUrl) => {
record('call_replace', newUrl);
this.href = newUrl;
},
writable: false
},
reload: {
value: (force) => {
record('call_reload', force);
},
writable: false
},
toString: {
value: () => {
record('call_toString')
return state.href;
},
writable: false
},
verifyCall: {
value: (name) => new Verifier(`call_${name}`, mocking[`call_${name}`]),
writable: false,
enumerable: true
},
verifyGet: {
value: (name) => new Verifier(`get_${name}`, mocking[`get_${name}`]),
writable: false,
enumerable: true
},
verifySet: {
value: (name) => new Verifier(`set_${name}`, mocking[`set_${name}`]),
writable: false,
enumerable: true
}
});
this.href = url;
}
function MockWindow(real, mockDefinition){
function getValue(key){
if(mockDefinition[key] && mockDefinition[key].get){
return mockDefinition[key].get();
} else {
return real[key];
}
}
function setValue(key, value){
if(mockDefinition[key] && mockDefinition[key].set){
mockDefinition[key].set(value);
} else {
real[key] = value;
}
}
Object.keys(real)
.concat(Object.keys(mockDefinition))
.filter((value, index, self) => self.indexOf(value) === index)
.forEach((key) => {
Object.defineProperty(this, key, {
enumerable: true,
get: function() { return getValue(key); },
set: function(value) { setValue(key, value); }
});
});
}
exports.MockLocation = MockLocation;
exports.MockWindow = MockWindow;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment