Convert tests to Mocha/Chai/Sinon and lint them.
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...
This commit is contained in:
parent
8737c7e2c1
commit
12b8f044f1
39 changed files with 6210 additions and 10822 deletions
|
|
@ -3,4 +3,4 @@
|
|||
{"title": "Documentation", "url": "mithril.html"},
|
||||
{"title": "Mithril Blog", "url": "http://lhorie.github.io/mithril-blog/"},
|
||||
{"title": "Mailing List", "url": "https://groups.google.com/forum/#!forum/mithriljs"}
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
<p>If you already have your HTML written and want to convert it into a Mithril template, paste the HTML below and press the "Convert" button.</p>
|
||||
<!doctype html>
|
||||
<title>HTML to Mithril Template Converter</title>
|
||||
|
||||
<h1>HTML to Mithril Template Converter</h1>
|
||||
<p>
|
||||
If you have HTML you want to convert into a Mithril template, paste it below
|
||||
and press the "Convert" button. In case you're wondering, this itself is a
|
||||
Mithril app.
|
||||
</p>
|
||||
|
||||
<div id="converter"></div>
|
||||
|
||||
<script src="../mithril.min.js"></script>
|
||||
<script src="template-converter.js"></script>
|
||||
<script>
|
||||
m.mount(document.getElementById("converter"), templateConverter);
|
||||
</script>
|
||||
m.mount(document.getElementById("converter"), templateConverter)
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,90 +1,145 @@
|
|||
var templateConverter = {};
|
||||
/* global m: false */
|
||||
// TODO: ensure this targets the current API.
|
||||
window.templateConverter = (function () {
|
||||
"use strict"
|
||||
|
||||
templateConverter.DOMFragment = function(markup) {
|
||||
if (markup.indexOf("<!doctype") > -1) return [new DOMParser().parseFromString(markup, "text/html").childNodes[1]]
|
||||
var container = document.createElement("div");
|
||||
container.insertAdjacentHTML("beforeend", markup);
|
||||
return container.childNodes;
|
||||
}
|
||||
templateConverter.VirtualFragment = function recurse(domFragment) {
|
||||
var virtualFragment = [];
|
||||
for (var i = 0, el; el = domFragment[i]; i++) {
|
||||
if (el.nodeType == 3) {
|
||||
virtualFragment.push(el.nodeValue);
|
||||
}
|
||||
else if (el.nodeType == 1) {
|
||||
var attrs = {};
|
||||
for (var j = 0, attr; attr = el.attributes[j]; j++) {
|
||||
attrs[attr.name] = attr.value;
|
||||
}
|
||||
|
||||
virtualFragment.push({tag: el.nodeName.toLowerCase(), attrs: attrs, children: recurse(el.childNodes)});
|
||||
function each(list, f) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
f(list[i], i)
|
||||
}
|
||||
}
|
||||
return virtualFragment;
|
||||
}
|
||||
templateConverter.Template = function recurse() {
|
||||
if (Object.prototype.toString.call(arguments[0]) == "[object String]") {
|
||||
return new recurse(new templateConverter.VirtualFragment(new templateConverter.DOMFragment(arguments[0])));
|
||||
}
|
||||
|
||||
var virtualFragment = arguments[0], level = arguments[1]
|
||||
if (!level) level = 1;
|
||||
|
||||
var tab = "\n" + new Array(level + 1).join("\t");
|
||||
var virtuals = [];
|
||||
for (var i = 0, el; el = virtualFragment[i]; i++) {
|
||||
if (typeof el == "string") {
|
||||
if (el.match(/\t| {2,}/g) && el.trim().length == 0) virtuals.indented = true;
|
||||
else virtuals.push('"' + el.replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n") + '"');
|
||||
|
||||
function createFragment(markup) {
|
||||
if (markup.indexOf("<!doctype") >= 0) {
|
||||
return [
|
||||
new DOMParser()
|
||||
.parseFromString(markup, "text/html")
|
||||
.childNodes[1]
|
||||
]
|
||||
}
|
||||
else {
|
||||
var virtual = "";
|
||||
if (el.tag != "div") virtual += el.tag;
|
||||
if (el.attrs["class"]) {
|
||||
virtual += "." + el.attrs["class"].replace(/\t+/g, " ").split(" ").join(".");
|
||||
delete el.attrs["class"];
|
||||
|
||||
var container = document.createElement("div")
|
||||
container.insertAdjacentHTML("beforeend", markup)
|
||||
return container.childNodes
|
||||
}
|
||||
|
||||
function createVirtual(fragment) {
|
||||
var list = []
|
||||
|
||||
each(fragment, function (el) {
|
||||
if (el.nodeType === 3) {
|
||||
list.push(el.nodeValue)
|
||||
} else if (el.nodeType === 1) {
|
||||
var attrs = {}
|
||||
|
||||
each(el.attributes, function (attr) {
|
||||
attrs[attr.name] = attr.value
|
||||
})
|
||||
|
||||
list.push({
|
||||
tag: el.nodeName.toLowerCase(),
|
||||
attrs: attrs,
|
||||
children: createVirtual(el.childNodes)
|
||||
})
|
||||
}
|
||||
var attrNames = Object.keys(el.attrs).sort()
|
||||
for (var j = 0, attrName; attrName = attrNames[j]; j++) {
|
||||
if (attrName != "style") virtual += "[" + attrName + "='" + el.attrs[attrName].replace(/'/g, "\\'") + "']";
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
function TemplateBuilder(virtual, level) {
|
||||
this.virtual = virtual
|
||||
this.level = level
|
||||
this.virtuals = []
|
||||
this.indented = false
|
||||
}
|
||||
|
||||
TemplateBuilder.prototype = {
|
||||
addVirtualString: function (el) {
|
||||
if (/\t| {2,}/.test(el) && /^\s*/.test(el)) {
|
||||
this.indented = true
|
||||
} else {
|
||||
this.virtuals.push('"' + el.replace(/(["\r\n])/g, "\\$1") + '"')
|
||||
}
|
||||
if (virtual == "") virtual = "div"
|
||||
virtual = '"' + virtual + '"';
|
||||
|
||||
var style = ""
|
||||
},
|
||||
|
||||
addVirtualAttrs: function (el) {
|
||||
var virtual = el.tag === "div" ? "" : el.tag
|
||||
|
||||
if (el.attrs.class) {
|
||||
virtual += "." + el.attrs.class.replace(/\s+/g, ".")
|
||||
el.attrs.class = undefined
|
||||
}
|
||||
|
||||
each(Object.keys(el.attrs).sort(), function (attrName) {
|
||||
if (attrName === "style") return
|
||||
virtual += "[" + attrName + "='"
|
||||
virtual += el.attrs[attrName].replace(/'/g, "\\'") + "']"
|
||||
})
|
||||
|
||||
if (virtual === "") virtual = "div"
|
||||
virtual = '"' + virtual + '"'
|
||||
|
||||
if (el.attrs.style) {
|
||||
virtual += ", {style: " + ("{\"" + el.attrs.style.replace(/:/g, "\": \"").replace(/;/g, "\", \"") + "}").replace(/, "}|"}/, "}") + "}"
|
||||
var style = "{\"" + el.attrs.style
|
||||
.replace(/:/g, "\": \"")
|
||||
.replace(/;/g, "\", \"") + "}"
|
||||
virtual += ", {style: " + style.replace(/(, )"}/, "}") + "}"
|
||||
}
|
||||
|
||||
if (el.children.length > 0) {
|
||||
virtual += ", " + recurse(el.children, level + 1);
|
||||
|
||||
if (el.children.length !== 0) {
|
||||
var builder = new TemplateBuilder(el.children, this.level + 1)
|
||||
virtual += ", " + builder.complete()
|
||||
}
|
||||
|
||||
this.virtuals.push("m(" + virtual + ")")
|
||||
},
|
||||
|
||||
complete: function () {
|
||||
var tab = "\n"
|
||||
for (var i = 0; i <= this.level; i++) tab += "\t"
|
||||
|
||||
each(this.virtual, function (el) {
|
||||
if (typeof el === "string") {
|
||||
this.addVirtualString(el)
|
||||
} else {
|
||||
this.addVirtualAttrs(el)
|
||||
}
|
||||
}.bind(this))
|
||||
|
||||
if (!this.indented) tab = ""
|
||||
|
||||
if (this.virtuals.length === 1 && this.virtuals[0][0] === "\"") {
|
||||
return this.virtuals.join(", ")
|
||||
} else {
|
||||
var body = this.virtuals.join("," + tab)
|
||||
return "[" + tab + body + tab.slice(0, -1) + "]"
|
||||
}
|
||||
virtual = "m(" + virtual + ")";
|
||||
virtuals.push(virtual);
|
||||
}
|
||||
}
|
||||
if (!virtuals.indented) tab = "";
|
||||
|
||||
var isInline = virtuals.length == 1 && virtuals[0].charAt(0) == '"';
|
||||
var template = isInline ? virtuals.join(", ") : "[" + tab + virtuals.join("," + tab) + tab.slice(0, -1) + "]";
|
||||
return new String(template);
|
||||
}
|
||||
|
||||
templateConverter.controller = function() {
|
||||
this.source = m.prop("");
|
||||
this.output = m.prop("");
|
||||
|
||||
this.convert = function() {
|
||||
return this.output(new templateConverter.Template(this.source()));
|
||||
};
|
||||
|
||||
};
|
||||
return {
|
||||
controller: function () {
|
||||
this.source = m.prop("")
|
||||
this.output = m.prop("")
|
||||
|
||||
templateConverter.view = function(ctrl) {
|
||||
return m("div", [
|
||||
m("textarea", {autofocus: true, style: {width:"100%", height: "40%"}, onchange: m.withAttr("value", ctrl.source)}, ctrl.source()),
|
||||
m("button", {onclick: ctrl.convert.bind(ctrl)}, "Convert"),
|
||||
m("textarea", {style: {width:"100%", height: "40%"}}, ctrl.output())
|
||||
]);
|
||||
};
|
||||
this.convert = function () {
|
||||
var source = createVirtual(createFragment(this.source()))
|
||||
return this.output(new TemplateBuilder(source, 1).complete())
|
||||
}.bind(this)
|
||||
},
|
||||
|
||||
view: function (ctrl) {
|
||||
return m("div", [
|
||||
m("textarea", {
|
||||
autofocus: true,
|
||||
style: {width: "100%", height: "40%"},
|
||||
onchange: m.withAttr("value", ctrl.source)
|
||||
}, ctrl.source()),
|
||||
m("button", {onclick: ctrl.convert}, "Convert"),
|
||||
m("textarea", {style: {width: "100%", height: "40%"}},
|
||||
ctrl.output())
|
||||
])
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue