Last active
January 20, 2017 03:52
-
-
Save vjau/9a8db88d5f1f82d4f9c02e82b29b466f to your computer and use it in GitHub Desktop.
nigthwatch command to fake Date in the browser. Put this in a commands folder and set the folder name in nightwatch.json "custom_commands_path" : "./commands",
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
"we go on the page": function(browser){ | |
browser | |
.url("http://localhost:3000") | |
.waitForElementVisible("#stuff", 10000) | |
.assert.title("MyPage") | |
}, | |
"we fake the Date": function(browser){ | |
browser | |
.fakeDate(new Date(2001, 1, 1).getTime()) //DONE ! | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//contains lolex 1.5.1 build | |
exports.command = function(milliseconds, callback){ | |
"use strict"; | |
this.execute(function(milliseconds) { | |
(function (f) { | |
if (typeof exports === "object" && typeof module !== "undefined") { | |
module.exports = f() | |
} else if (typeof define === "function" && define.amd) { | |
define([], f) | |
} else { | |
var g; | |
if (typeof window !== "undefined") { | |
g = window | |
} else if (typeof global !== "undefined") { | |
g = global | |
} else if (typeof self !== "undefined") { | |
g = self | |
} else { | |
g = this | |
} | |
g.lolex = f() | |
} | |
})(function () { | |
var define, module, exports; | |
return (function e(t, n, r) { | |
function s(o, u) { | |
if (!n[o]) { | |
if (!t[o]) { | |
var a = typeof require == "function" && require; | |
if (!u && a)return a(o, !0); | |
if (i)return i(o, !0); | |
var f = new Error("Cannot find module '" + o + "'"); | |
throw f.code = "MODULE_NOT_FOUND", f | |
} | |
var l = n[o] = {exports: {}}; | |
t[o][0].call(l.exports, function (e) { | |
var n = t[o][1][e]; | |
return s(n ? n : e) | |
}, l, l.exports, e, t, n, r) | |
} | |
return n[o].exports | |
} | |
var i = typeof require == "function" && require; | |
for (var o = 0; o < r.length; o++)s(r[o]); | |
return s | |
})({ | |
1: [function (require, module, exports) { | |
(function (global) { | |
/*global global, window*/ | |
/** | |
* @author Christian Johansen ([email protected]) and contributors | |
* @license BSD | |
* | |
* Copyright (c) 2010-2014 Christian Johansen | |
*/ | |
(function (global) { | |
"use strict"; | |
// Make properties writable in IE, as per | |
// http://www.adequatelygood.com/Replacing-setTimeout-Globally.html | |
global.setTimeout = global.setTimeout; | |
global.clearTimeout = global.clearTimeout; | |
global.setInterval = global.setInterval; | |
global.clearInterval = global.clearInterval; | |
global.Date = global.Date; | |
// setImmediate is not a standard function | |
// avoid adding the prop to the window object if not present | |
if (global.setImmediate !== undefined) { | |
global.setImmediate = global.setImmediate; | |
global.clearImmediate = global.clearImmediate; | |
} | |
// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref() | |
// browsers, a number. | |
// see https://github.com/cjohansen/Sinon.JS/pull/436 | |
var NOOP = function () { | |
return undefined; | |
}; | |
var timeoutResult = setTimeout(NOOP, 0); | |
var addTimerReturnsObject = typeof timeoutResult === "object"; | |
var hrtimePresent = (global.process && typeof global.process.hrtime === "function"); | |
clearTimeout(timeoutResult); | |
var NativeDate = Date; | |
var uniqueTimerId = 1; | |
/** | |
* Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into | |
* number of milliseconds. This is used to support human-readable strings passed | |
* to clock.tick() | |
*/ | |
function parseTime(str) { | |
if (!str) { | |
return 0; | |
} | |
var strings = str.split(":"); | |
var l = strings.length, i = l; | |
var ms = 0, parsed; | |
if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { | |
throw new Error("tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits"); | |
} | |
while (i--) { | |
parsed = parseInt(strings[i], 10); | |
if (parsed >= 60) { | |
throw new Error("Invalid time " + str); | |
} | |
ms += parsed * Math.pow(60, (l - i - 1)); | |
} | |
return ms * 1000; | |
} | |
/** | |
* Floor function that also works for negative numbers | |
*/ | |
function fixedFloor(n) { | |
return (n >= 0 ? Math.floor(n) : Math.ceil(n)); | |
} | |
/** | |
* % operator that also works for negative numbers | |
*/ | |
function fixedModulo(n, m) { | |
return ((n % m) + m) % m; | |
} | |
/** | |
* Used to grok the `now` parameter to createClock. | |
*/ | |
function getEpoch(epoch) { | |
if (!epoch) { | |
return 0; | |
} | |
if (typeof epoch.getTime === "function") { | |
return epoch.getTime(); | |
} | |
if (typeof epoch === "number") { | |
return epoch; | |
} | |
throw new TypeError("now should be milliseconds since UNIX epoch"); | |
} | |
function inRange(from, to, timer) { | |
return timer && timer.callAt >= from && timer.callAt <= to; | |
} | |
function mirrorDateProperties(target, source) { | |
var prop; | |
for (prop in source) { | |
if (source.hasOwnProperty(prop)) { | |
target[prop] = source[prop]; | |
} | |
} | |
// set special now implementation | |
if (source.now) { | |
target.now = function now() { | |
return target.clock.now; | |
}; | |
} else { | |
delete target.now; | |
} | |
// set special toSource implementation | |
if (source.toSource) { | |
target.toSource = function toSource() { | |
return source.toSource(); | |
}; | |
} else { | |
delete target.toSource; | |
} | |
// set special toString implementation | |
target.toString = function toString() { | |
return source.toString(); | |
}; | |
target.prototype = source.prototype; | |
target.parse = source.parse; | |
target.UTC = source.UTC; | |
target.prototype.toUTCString = source.prototype.toUTCString; | |
return target; | |
} | |
function createDate() { | |
function ClockDate(year, month, date, hour, minute, second, ms) { | |
// Defensive and verbose to avoid potential harm in passing | |
// explicit undefined when user does not pass argument | |
switch (arguments.length) { | |
case 0: | |
return new NativeDate(ClockDate.clock.now); | |
case 1: | |
return new NativeDate(year); | |
case 2: | |
return new NativeDate(year, month); | |
case 3: | |
return new NativeDate(year, month, date); | |
case 4: | |
return new NativeDate(year, month, date, hour); | |
case 5: | |
return new NativeDate(year, month, date, hour, minute); | |
case 6: | |
return new NativeDate(year, month, date, hour, minute, second); | |
default: | |
return new NativeDate(year, month, date, hour, minute, second, ms); | |
} | |
} | |
return mirrorDateProperties(ClockDate, NativeDate); | |
} | |
function addTimer(clock, timer) { | |
if (timer.func === undefined) { | |
throw new Error("Callback must be provided to timer calls"); | |
} | |
if (!clock.timers) { | |
clock.timers = {}; | |
} | |
timer.id = uniqueTimerId++; | |
timer.createdAt = clock.now; | |
timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0)); | |
clock.timers[timer.id] = timer; | |
if (addTimerReturnsObject) { | |
return { | |
id: timer.id, | |
ref: NOOP, | |
unref: NOOP | |
}; | |
} | |
return timer.id; | |
} | |
/* eslint consistent-return: "off" */ | |
function compareTimers(a, b) { | |
// Sort first by absolute timing | |
if (a.callAt < b.callAt) { | |
return -1; | |
} | |
if (a.callAt > b.callAt) { | |
return 1; | |
} | |
// Sort next by immediate, immediate timers take precedence | |
if (a.immediate && !b.immediate) { | |
return -1; | |
} | |
if (!a.immediate && b.immediate) { | |
return 1; | |
} | |
// Sort next by creation time, earlier-created timers take precedence | |
if (a.createdAt < b.createdAt) { | |
return -1; | |
} | |
if (a.createdAt > b.createdAt) { | |
return 1; | |
} | |
// Sort next by id, lower-id timers take precedence | |
if (a.id < b.id) { | |
return -1; | |
} | |
if (a.id > b.id) { | |
return 1; | |
} | |
// As timer ids are unique, no fallback `0` is necessary | |
} | |
function firstTimerInRange(clock, from, to) { | |
var timers = clock.timers, | |
timer = null, | |
id, | |
isInRange; | |
for (id in timers) { | |
if (timers.hasOwnProperty(id)) { | |
isInRange = inRange(from, to, timers[id]); | |
if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) { | |
timer = timers[id]; | |
} | |
} | |
} | |
return timer; | |
} | |
function firstTimer(clock) { | |
var timers = clock.timers, | |
timer = null, | |
id; | |
for (id in timers) { | |
if (timers.hasOwnProperty(id)) { | |
if (!timer || compareTimers(timer, timers[id]) === 1) { | |
timer = timers[id]; | |
} | |
} | |
} | |
return timer; | |
} | |
function lastTimer(clock) { | |
var timers = clock.timers, | |
timer = null, | |
id; | |
for (id in timers) { | |
if (timers.hasOwnProperty(id)) { | |
if (!timer || compareTimers(timer, timers[id]) === -1) { | |
timer = timers[id]; | |
} | |
} | |
} | |
return timer; | |
} | |
function callTimer(clock, timer) { | |
var exception; | |
if (typeof timer.interval === "number") { | |
clock.timers[timer.id].callAt += timer.interval; | |
} else { | |
delete clock.timers[timer.id]; | |
} | |
try { | |
if (typeof timer.func === "function") { | |
timer.func.apply(null, timer.args); | |
} else { | |
/* eslint no-eval: "off" */ | |
eval(timer.func); | |
} | |
} catch (e) { | |
exception = e; | |
} | |
if (!clock.timers[timer.id]) { | |
if (exception) { | |
throw exception; | |
} | |
return; | |
} | |
if (exception) { | |
throw exception; | |
} | |
} | |
function timerType(timer) { | |
if (timer.immediate) { | |
return "Immediate"; | |
} | |
if (timer.interval !== undefined) { | |
return "Interval"; | |
} | |
return "Timeout"; | |
} | |
function clearTimer(clock, timerId, ttype) { | |
if (!timerId) { | |
// null appears to be allowed in most browsers, and appears to be | |
// relied upon by some libraries, like Bootstrap carousel | |
return; | |
} | |
if (!clock.timers) { | |
clock.timers = []; | |
} | |
// in Node, timerId is an object with .ref()/.unref(), and | |
// its .id field is the actual timer id. | |
if (typeof timerId === "object") { | |
timerId = timerId.id; | |
} | |
if (clock.timers.hasOwnProperty(timerId)) { | |
// check that the ID matches a timer of the correct type | |
var timer = clock.timers[timerId]; | |
if (timerType(timer) === ttype) { | |
delete clock.timers[timerId]; | |
} else { | |
throw new Error("Cannot clear timer: timer created with set" + timerType(timer) | |
+ "() but cleared with clear" + ttype + "()"); | |
} | |
} | |
} | |
function uninstall(clock, target) { | |
var method, | |
i, | |
l; | |
var installedHrTime = "_hrtime"; | |
for (i = 0, l = clock.methods.length; i < l; i++) { | |
method = clock.methods[i]; | |
if (method === "hrtime" && target.process) { | |
target.process.hrtime = clock[installedHrTime]; | |
} else { | |
if (target[method] && target[method].hadOwnProperty) { | |
target[method] = clock["_" + method]; | |
} else { | |
try { | |
delete target[method]; | |
} catch (ignore) { /* eslint empty-block: "off" */ | |
} | |
} | |
} | |
} | |
// Prevent multiple executions which will completely remove these props | |
clock.methods = []; | |
} | |
function hijackMethod(target, method, clock) { | |
var prop; | |
clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method); | |
clock["_" + method] = target[method]; | |
if (method === "Date") { | |
var date = mirrorDateProperties(clock[method], target[method]); | |
target[method] = date; | |
} else { | |
target[method] = function () { | |
return clock[method].apply(clock, arguments); | |
}; | |
for (prop in clock[method]) { | |
if (clock[method].hasOwnProperty(prop)) { | |
target[method][prop] = clock[method][prop]; | |
} | |
} | |
} | |
target[method].clock = clock; | |
} | |
var timers = { | |
setTimeout: setTimeout, | |
clearTimeout: clearTimeout, | |
setImmediate: global.setImmediate, | |
clearImmediate: global.clearImmediate, | |
setInterval: setInterval, | |
clearInterval: clearInterval, | |
Date: Date | |
}; | |
if (hrtimePresent) { | |
timers.hrtime = global.process.hrtime; | |
} | |
var keys = Object.keys || function (obj) { | |
var ks = [], | |
key; | |
for (key in obj) { | |
if (obj.hasOwnProperty(key)) { | |
ks.push(key); | |
} | |
} | |
return ks; | |
}; | |
exports.timers = timers; | |
function createClock(now, loopLimit) { | |
loopLimit = loopLimit || 1000; | |
var clock = { | |
now: getEpoch(now), | |
hrNow: 0, | |
timeouts: {}, | |
Date: createDate(), | |
loopLimit: loopLimit | |
}; | |
clock.Date.clock = clock; | |
clock.setTimeout = function setTimeout(func, timeout) { | |
return addTimer(clock, { | |
func: func, | |
args: Array.prototype.slice.call(arguments, 2), | |
delay: timeout | |
}); | |
}; | |
clock.clearTimeout = function clearTimeout(timerId) { | |
return clearTimer(clock, timerId, "Timeout"); | |
}; | |
clock.setInterval = function setInterval(func, timeout) { | |
return addTimer(clock, { | |
func: func, | |
args: Array.prototype.slice.call(arguments, 2), | |
delay: timeout, | |
interval: timeout | |
}); | |
}; | |
clock.clearInterval = function clearInterval(timerId) { | |
return clearTimer(clock, timerId, "Interval"); | |
}; | |
clock.setImmediate = function setImmediate(func) { | |
return addTimer(clock, { | |
func: func, | |
args: Array.prototype.slice.call(arguments, 1), | |
immediate: true | |
}); | |
}; | |
clock.clearImmediate = function clearImmediate(timerId) { | |
return clearTimer(clock, timerId, "Immediate"); | |
}; | |
clock.tick = function tick(ms) { | |
ms = typeof ms === "number" ? ms : parseTime(ms); | |
var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now; | |
var timer = firstTimerInRange(clock, tickFrom, tickTo); | |
var oldNow; | |
clock.duringTick = true; | |
function updateHrTime(newNow) { | |
clock.hrNow += (newNow - clock.now); | |
} | |
var firstException; | |
while (timer && tickFrom <= tickTo) { | |
if (clock.timers[timer.id]) { | |
updateHrTime(timer.callAt); | |
tickFrom = timer.callAt; | |
clock.now = timer.callAt; | |
try { | |
oldNow = clock.now; | |
callTimer(clock, timer); | |
// compensate for any setSystemTime() call during timer callback | |
if (oldNow !== clock.now) { | |
tickFrom += clock.now - oldNow; | |
tickTo += clock.now - oldNow; | |
previous += clock.now - oldNow; | |
} | |
} catch (e) { | |
firstException = firstException || e; | |
} | |
} | |
timer = firstTimerInRange(clock, previous, tickTo); | |
previous = tickFrom; | |
} | |
clock.duringTick = false; | |
updateHrTime(tickTo); | |
clock.now = tickTo; | |
if (firstException) { | |
throw firstException; | |
} | |
return clock.now; | |
}; | |
clock.next = function next() { | |
var timer = firstTimer(clock); | |
if (!timer) { | |
return clock.now; | |
} | |
clock.duringTick = true; | |
try { | |
clock.now = timer.callAt; | |
callTimer(clock, timer); | |
return clock.now; | |
} finally { | |
clock.duringTick = false; | |
} | |
}; | |
clock.runAll = function runAll() { | |
var numTimers, i; | |
for (i = 0; i < clock.loopLimit; i++) { | |
if (!clock.timers) { | |
return clock.now; | |
} | |
numTimers = Object.keys(clock.timers).length; | |
if (numTimers === 0) { | |
return clock.now; | |
} | |
clock.next(); | |
} | |
throw new Error("Aborting after running " + clock.loopLimit + "timers, assuming an infinite loop!"); | |
}; | |
clock.runToLast = function runToLast() { | |
var timer = lastTimer(clock); | |
if (!timer) { | |
return clock.now; | |
} | |
return clock.tick(timer.callAt); | |
}; | |
clock.reset = function reset() { | |
clock.timers = {}; | |
}; | |
clock.setSystemTime = function setSystemTime(now) { | |
// determine time difference | |
var newNow = getEpoch(now); | |
var difference = newNow - clock.now; | |
var id, timer; | |
// update 'system clock' | |
clock.now = newNow; | |
// update timers and intervals to keep them stable | |
for (id in clock.timers) { | |
if (clock.timers.hasOwnProperty(id)) { | |
timer = clock.timers[id]; | |
timer.createdAt += difference; | |
timer.callAt += difference; | |
} | |
} | |
}; | |
if (hrtimePresent) { | |
clock.hrtime = function (prev) { | |
if (Array.isArray(prev)) { | |
var oldSecs = (prev[0] + prev[1] / 1e9); | |
var newSecs = (clock.hrNow / 1000); | |
var difference = (newSecs - oldSecs); | |
var secs = fixedFloor(difference); | |
var nanosecs = fixedModulo(difference * 1e9, 1e9); | |
return [ | |
secs, | |
nanosecs | |
]; | |
} | |
return [ | |
fixedFloor(clock.hrNow / 1000), | |
fixedModulo(clock.hrNow * 1e6, 1e9) | |
]; | |
}; | |
} | |
return clock; | |
} | |
exports.createClock = createClock; | |
exports.install = function install(target, now, toFake, loopLimit) { | |
var i, | |
l; | |
if (typeof target === "number") { | |
toFake = now; | |
now = target; | |
target = null; | |
} | |
if (!target) { | |
target = global; | |
} | |
var clock = createClock(now, loopLimit); | |
clock.uninstall = function () { | |
uninstall(clock, target); | |
}; | |
clock.methods = toFake || []; | |
if (clock.methods.length === 0) { | |
clock.methods = keys(timers); | |
} | |
for (i = 0, l = clock.methods.length; i < l; i++) { | |
if (clock.methods[i] === "hrtime") { | |
if (target.process && typeof target.process.hrtime === "function") { | |
hijackMethod(target.process, clock.methods[i], clock); | |
} | |
} else { | |
hijackMethod(target, clock.methods[i], clock); | |
} | |
} | |
return clock; | |
}; | |
}(global || this)); | |
}).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
}, {}] | |
}, {}, [1])(1) | |
}); | |
console.log("lolex installed"); | |
var clock = lolex.install(milliseconds, ["Date"]); | |
}, [milliseconds], callback); | |
return this; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Copyright (c) 2010-2014, Christian Johansen, [email protected]. All rights reserved. | |
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | |
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | |
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. | |
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment