Details: 1. All tests now live in `test`. All test dependencies that aren't from npm live in `test-deps`. 2. The QUnit tests are gone, as well as their dependencies. Half of them duplicated existing tests, and some of them depended on the real DOM to properly test. 3. All tests are now using Mocha to run the tests, Chai for assertions, and Sinon and Sinon Chai for testing some callbacks. 4. Tests are run through mocha-phantomjs. If you want to run just the tests, run `grunt mocha_phantomjs` or fire up a server in the root and open `http://localhost:<port>/test/index.html`, e.g. `python3 -m http.server`. 5. The linter I chose is ESLint. It is relatively easy to configure, but with a lot of flexibility. The rules I chose mostly were in tune to the style the project was already using. I'm not including a style guide in this commit, but one will likely come. You can check out the `.eslintrc` in the root and in `test/` for the two configs. The `.eslintignore` includes a TODO for `mithril.js` itself targeted at me, in the root. Other info: - As a drive-by fix, I fixed line endings on a few of the files. - I also took care of a few other files and linted them as I went: - `Gruntfile.js` - `test/input-cursor.html` (was in `tests/`) - `test/svg.html` (was in `tests/`) - `docs/layout/tools/template-converter.html` - `docs/layout/tools/template-converter.js` I didn't test the template converter after linting it, because it needs further scrutiny to ensure it works with the latest version of Mithril. I know the API has changed a little, which is why I want to be sure. - I simplified the `.travis.yml` file because none of the tests are run directly through Node anymore. They are always run in a browser of some kind. Hopefully, this turned out all right...
258 lines
6.2 KiB
JavaScript
258 lines
6.2 KiB
JavaScript
;(function () { // eslint-disable-line no-extra-semi
|
|
"use strict"
|
|
|
|
/* eslint-disable no-extend-native */
|
|
Array.prototype.forEach = Array.prototype.forEach || function (callback) {
|
|
for (var i = 0; i < this.length; i++) {
|
|
callback(this[i], i, this)
|
|
}
|
|
}
|
|
|
|
Array.prototype.indexOf = Array.prototype.indexOf || function (item) {
|
|
for (var i = 0; i < this.length; i++) {
|
|
if (this[i] === item) return i
|
|
}
|
|
return -1
|
|
}
|
|
|
|
Array.prototype.map = Array.prototype.map || function (callback) {
|
|
var results = []
|
|
this.forEach(function (value, i, array) {
|
|
results.push(callback(value, i, array))
|
|
})
|
|
return results
|
|
}
|
|
|
|
Array.prototype.filter = Array.prototype.filter || function (callback) {
|
|
var results = []
|
|
this.forEach(function (value, i, array) {
|
|
if (callback(value, i, array)) results.push(value)
|
|
})
|
|
return results
|
|
}
|
|
|
|
Object.keys = Object.keys || function (obj) {
|
|
var keys = []
|
|
for (var i in obj) if ({}.hasOwnProperty.call(obj, i)) {
|
|
keys.push(i)
|
|
}
|
|
return keys
|
|
}
|
|
/* eslint-enable no-extend-native */
|
|
})()
|
|
|
|
window.mock = (function () {
|
|
"use strict"
|
|
|
|
var window = {}
|
|
var document = window.document = {
|
|
// FIXME: add document.createRange().createContextualFragment()
|
|
|
|
childNodes: [],
|
|
|
|
createElement: function (tag) {
|
|
return {
|
|
style: {},
|
|
childNodes: [],
|
|
nodeType: 1,
|
|
nodeName: tag.toUpperCase(),
|
|
appendChild: document.appendChild,
|
|
removeChild: document.removeChild,
|
|
replaceChild: document.replaceChild,
|
|
insertBefore: function (node, reference) {
|
|
node.parentNode = this
|
|
var referenceIndex = this.childNodes.indexOf(reference)
|
|
|
|
var index = this.childNodes.indexOf(node)
|
|
if (index > -1) this.childNodes.splice(index, 1)
|
|
|
|
if (referenceIndex < 0) this.childNodes.push(node)
|
|
else this.childNodes.splice(referenceIndex, 0, node)
|
|
},
|
|
|
|
insertAdjacentHTML: function (position, html) {
|
|
// TODO: accept markup
|
|
if (position === "beforebegin") {
|
|
this.parentNode.insertBefore(
|
|
document.createTextNode(html),
|
|
this)
|
|
} else if (position === "beforeend") {
|
|
this.appendChild(document.createTextNode(html))
|
|
}
|
|
},
|
|
|
|
setAttribute: function (name, value) {
|
|
this[name] = value.toString()
|
|
},
|
|
|
|
setAttributeNS: function (namespace, name, value) {
|
|
this.namespaceURI = namespace
|
|
this[name] = value.toString()
|
|
},
|
|
|
|
getAttribute: function (name) {
|
|
return this[name]
|
|
},
|
|
|
|
addEventListener: function () {},
|
|
removeEventListener: function () {}
|
|
}
|
|
},
|
|
|
|
createElementNS: function (namespace, tag) {
|
|
var element = document.createElement(tag)
|
|
element.namespaceURI = namespace
|
|
return element
|
|
},
|
|
|
|
createTextNode: function (text) {
|
|
return {nodeValue: text.toString()}
|
|
},
|
|
|
|
replaceChild: function (newChild, oldChild) {
|
|
var index = this.childNodes.indexOf(oldChild)
|
|
if (index > -1) this.childNodes.splice(index, 1, newChild)
|
|
else this.childNodes.push(newChild)
|
|
newChild.parentNode = this
|
|
oldChild.parentNode = null
|
|
},
|
|
|
|
appendChild: function (child) {
|
|
var index = this.childNodes.indexOf(child)
|
|
if (index > -1) this.childNodes.splice(index, 1)
|
|
this.childNodes.push(child)
|
|
child.parentNode = this
|
|
},
|
|
|
|
removeChild: function (child) {
|
|
var index = this.childNodes.indexOf(child)
|
|
this.childNodes.splice(index, 1)
|
|
child.parentNode = null
|
|
},
|
|
|
|
// getElementsByTagName is only used by JSONP tests, it's not required
|
|
// by Mithril
|
|
getElementsByTagName: function (name) {
|
|
name = name.toLowerCase()
|
|
var out = []
|
|
|
|
function traverse(node){
|
|
if (node.childNodes && node.childNodes.length > 0) {
|
|
node.childNodes.forEach(function (curr) {
|
|
if (curr.nodeName.toLowerCase() === name) {
|
|
out.push(curr)
|
|
}
|
|
traverse(curr)
|
|
})
|
|
}
|
|
}
|
|
|
|
traverse(document)
|
|
return out
|
|
}
|
|
}
|
|
|
|
document.documentElement = document.createElement("html")
|
|
|
|
window.scrollTo = function () {}
|
|
|
|
;(function (window) {
|
|
// This is an actual conforming implementation of the
|
|
// requestAnimationFrame spec, with the nonstandard extension of
|
|
// rAF.$resolve for running the callbacks. It works in Node and the
|
|
// browser.
|
|
// https://html.spec.whatwg.org/multipage/#animation-frames
|
|
//
|
|
// Adding and removing callbacks run in constant time. Please don't
|
|
// modify this unless it actually has an edge case bug. It will break
|
|
// other tests
|
|
var callbacks = []
|
|
var id = 0
|
|
var indices = {}
|
|
|
|
function requestAnimationFrame(callback) {
|
|
id++
|
|
indices[id] = callbacks.length
|
|
callbacks.push({
|
|
callback: callback,
|
|
id: id
|
|
})
|
|
return id
|
|
}
|
|
|
|
window.cancelAnimationFrame = function (id) {
|
|
var index = indices[id]
|
|
if (index !== 0) {
|
|
indices[id] = 0
|
|
callbacks[index] = undefined
|
|
}
|
|
}
|
|
|
|
var nanotime = typeof process === "object" ? function () {
|
|
var time = process.hrtime() // eslint-disable-line no-undef
|
|
return time[0] * 1e9 + time[1]
|
|
} : typeof performance === "object" ? function () {
|
|
return performance.now()
|
|
} : function () {
|
|
// PhantomJS 1 doesn't have the Performance API implemented.
|
|
return +new Date()
|
|
}
|
|
|
|
requestAnimationFrame.$resolve = function () {
|
|
var list = callbacks
|
|
callbacks = []
|
|
indices = {}
|
|
|
|
for (var i = 0; i < list.length; i++) {
|
|
var data = list[i]
|
|
if (data !== undefined) {
|
|
data.callback.call(data.id, nanotime())
|
|
}
|
|
}
|
|
}
|
|
|
|
window.requestAnimationFrame = requestAnimationFrame
|
|
})(window)
|
|
|
|
window.XMLHttpRequest = (function () {
|
|
function Request() {
|
|
this.$headers = {}
|
|
|
|
this.setRequestHeader = function (key, value) {
|
|
this.$headers[key] = value
|
|
}
|
|
|
|
this.open = function (method, url) {
|
|
this.method = method
|
|
this.url = url
|
|
}
|
|
|
|
this.send = function () {
|
|
this.responseText = JSON.stringify(this)
|
|
this.readyState = 4
|
|
this.status = 200
|
|
Request.$instances.push(this)
|
|
}
|
|
}
|
|
|
|
Request.$instances = []
|
|
return Request
|
|
})()
|
|
|
|
var location = window.location = {search: "", pathname: "", hash: ""}
|
|
|
|
window.history = {
|
|
$$length: 0,
|
|
|
|
pushState: function (data, title, url) {
|
|
window.history.$$length++
|
|
location.pathname = location.search = location.hash = url
|
|
},
|
|
|
|
replaceState: function (data, title, url) {
|
|
location.pathname = location.search = location.hash = url
|
|
}
|
|
}
|
|
|
|
return window
|
|
})()
|