mithril-vndb/ospec/tests/test-ospec.js
Isiah Meadows d4551f49f5
Add streams to releases again, include minified bundle, drop in… (#2539)
* Minify stream, add stream stuff to releases again

* Kill off a lot of tech debt, drop internal utilities from npm

1. Kill `module/`, internalize `bundler/`, privatize `test-utils/`

We've been telling people to move elsewhere from these for a while, and
it's about time we just pull the plug here and finally remove them.

- We officially removed the bundler from the public API in v2.0, and
  that was the only one of these that was ever publicly documented.
  Usage should be low enough by now it shouldn't break anyone- I'm not
  seeing bundler bugs being reported anymore, either.
- The `module/` utility was so narrow and caveat-filled that I'm not
  sure anyone really used it (even us core Mithril devs never really
  used it), and we only had it documented in the repo folder it lived
  in. I think only one bug was ever filed, and it's because it somehow
  ended up completely non-functional without any of us realizing it.
- The test utilities were meant to be internal from day 1, but people
  started using it despite us core developers constantly telling people
  to look elsewhere and even the docs recommending specific alternatives
  without mention of our internal mocks. (Now if people would RTFM,
  that'd be nice...)

2. Add dedicated HTML test files to verify ospec and the promise
   polyfill, and ensure the promise tests are in pure ES5.

These are made specially for those and should be much easier to just run
now.

3. Fix the benchmark script to use the real DOM in browsers and to not
   require as many dependencies to create. Also, tweak them to be much
   more effective and precise on what's being tested.

Previously, it was rendering to the HTML file itself, while now it's
rendering to the `body`. This means in browsers, it's triggering layout
and everything, benchmarking how well Mithril optimizes for style and
layout recalcs, too. It also puts some pressure on the hyperscript
parser attribute application, so that can be noticed as well.

* Update dependencies
2019-10-03 04:58:14 -04:00

765 lines
18 KiB
JavaScript

"use strict"
// So it can load correctly in browsers using a global instance.
var o, callAsync
if (typeof require !== "undefined") {
/* eslint-disable global-require */
callAsync = require("../../test-utils/callAsync")
o = require("../ospec")
/* eslint-enable global-require */
} else {
callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
o = window.o
}
// this throws an async error that can't be caught in browsers
if (typeof process !== "undefined") {
o("incomplete assertion", function(done) {
var stackMatcher = /([\w\.\\\/\-]+):(\d+):/
// /!\ this test relies on the `new Error` expression being six lines
// above the `oo("test", function(){...})` call.
var matches = (new Error).stack.match(stackMatcher)
if (matches != null) {
var name = matches[1]
var num = Number(matches[2])
}
var oo = o.new()
oo("test", function() {
oo("incomplete")
})
oo.run(function(results) {
o(results.length).equals(1)
o(results[0].message).equals("Incomplete assertion in the test definition starting at...")
o(results[0].pass).equals(null)
var stack = o.cleanStackTrace(results[0].testError)
var matches2 = stack && stack.match(stackMatcher)
if (matches != null && matches2 != null) {
o(matches[1]).equals(name)
o(Number(matches2[2])).equals(num + 6)
}
done()
})
})
}
o("o.only", function(done) {
var oo = o.new()
oo.spec("won't run", function() {
oo("nope, skipped", function() {
o(true).equals(false)
})
})
oo.spec("ospec", function() {
oo("skipped as well", function() {
oo(true).equals(false)
})
oo.only(".only()", function() {
oo(2).equals(2)
}, true)
oo.only("another .only()", function(done) {
done("that fails")
}, true)
})
oo.run(function(results){
o(results.length).equals(2)
o(results[0].pass).equals(true)
o(results[1].pass).equals(false)
done()
})
})
// Predicate test passing on clone results
o.spec("reporting", function() {
var oo
o.beforeEach(function(){
oo = o.new()
oo.spec("clone", function() {
oo("fail", function() {
oo(true).equals(false)
})
oo("pass", function() {
oo(true).equals(true)
})
})
})
o("reports per instance", function(done, timeout) {
timeout(100) // Waiting on clone
oo.run(function(results) {
o(typeof results).equals("object")
o("length" in results).equals(true)
o(results.length).equals(2)("Two results")
o("error" in results[0] && "pass" in results[0]).equals(true)("error and pass keys present in failing result")
o("message" in results[0] && "context" in results[0]).equals(true)("message and context keys present in failing result")
o("message" in results[1] && "context" in results[1]).equals(true)("message and context keys present in passing result")
o(results[0].pass).equals(false)("Test meant to fail has failed")
o(results[1].pass).equals(true)("Test meant to pass has passed")
done()
})
})
o("o.report() returns the number of failures", function () {
var log = console.log, error = console.error
console.log = o.spy()
console.error = o.spy()
function makeError(msg) {try{throw msg ? new Error(msg) : new Error} catch(e){return e}}
try {
var errCount = o.report([{pass: true}, {pass: true}])
o(errCount).equals(0)
o(console.log.callCount).equals(1)
o(console.error.callCount).equals(0)
errCount = o.report([
{pass: false, error: makeError("hey"), message: "hey"}
])
o(errCount).equals(1)
o(console.log.callCount).equals(2)
o(console.error.callCount).equals(1)
errCount = o.report([
{pass: false, error: makeError("hey"), message: "hey"},
{pass: true},
{pass: false, error: makeError("ho"), message: "ho"}
])
o(errCount).equals(2)
o(console.log.callCount).equals(3)
o(console.error.callCount).equals(3)
} catch (e) {
o(1).equals(0)("Error while testing the reporter")
}
console.log = log
console.error = error
})
})
o.spec("ospec", function() {
o.spec("sync", function() {
var a = 0, b = 0, illegalAssertionThrows = false
var reservedTestNameTrows = false
o.before(function() {a = 1})
o.after(function() {a = 0})
o.beforeEach(function() {b = 1})
o.afterEach(function() {b = 0})
try {o("illegal assertion")} catch (e) {illegalAssertionThrows = true}
try {o("\x01reserved test name", function(){})} catch (e) {reservedTestNameTrows = true}
o("assertions", function() {
var nestedTestDeclarationThrows = false
try {o("illegal nested test", function(){})} catch (e) {nestedTestDeclarationThrows = true}
o(illegalAssertionThrows).equals(true)
o(nestedTestDeclarationThrows).equals(true)
o(reservedTestNameTrows).equals(true)
var spy = o.spy()
spy(a)
o(a).equals(b)
o(a).notEquals(2)
o({a: [1, 2], b: 3}).deepEquals({a: [1, 2], b: 3})
o([{a: 1, b: 2}, {c: 3}]).deepEquals([{a: 1, b: 2}, {c: 3}])
o(function(){throw new Error()}).throws(Error)
o(function(){"ayy".foo()}).throws(TypeError)
o(function(){Math.PI.toFixed(Math.pow(10,20))}).throws(RangeError)
o(function(){decodeURIComponent("%")}).throws(URIError)
o(function(){"ayy".foo()}).notThrows(SyntaxError)
o(function(){throw new Error("foo")}).throws("foo")
o(function(){throw new Error("foo")}).notThrows("bar")
var undef1 = {undef: void 0}
var undef2 = {UNDEF: void 0}
o(undef1).notDeepEquals(undef2)
o(undef1).notDeepEquals({})
o({}).notDeepEquals(undef1)
var sparse1 = [void 1, void 2, void 3]
delete sparse1[0]
var sparse2 = [void 1, void 2, void 3]
delete sparse2[1]
o(sparse1).notDeepEquals(sparse2)
var monkeypatch1 = [1, 2]
monkeypatch1.field = 3
var monkeypatch2 = [1, 2]
monkeypatch2.field = 4
o(monkeypatch1).notDeepEquals([1, 2])
o(monkeypatch1).notDeepEquals(monkeypatch2)
monkeypatch2.field = 3
o(monkeypatch1).deepEquals(monkeypatch2)
monkeypatch1.undef = undefined
monkeypatch2.UNDEF = undefined
o(monkeypatch1).notDeepEquals(monkeypatch2)
var values = ["a", "", 1, 0, true, false, null, undefined, Date(0), ["a"], [], function() {return arguments}.call(), new Uint8Array(), {a: 1}, {}]
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values.length; j++) {
if (i === j) o(values[i]).deepEquals(values[j])
else o(values[i]).notDeepEquals(values[j])
}
}
o(spy.callCount).equals(1)
o(spy.args.length).equals(1)
o(spy.args[0]).equals(1)
o(spy.calls.length).equals(1)
o(spy.calls[0]).deepEquals({this: undefined, args: [1]})
})
o("spy wrapping", function() {
var spy = o.spy(function view(vnode){
this.drawn = true
return {tag: "div", children: vnode.children}
})
var children = [""]
var state = {}
var output = spy.call(state, {children: children})
o(spy.length).equals(1)
o(spy.name).equals("view")
o(spy.callCount).equals(1)
o(spy.args.length).equals(1)
o(spy.args[0]).deepEquals({children: children})
o(spy.calls.length).equals(1)
o(spy.calls[0]).deepEquals({this: state, args: [{children: children}]})
o(state).deepEquals({drawn: true})
o(output).deepEquals({tag: "div", children: children})
})
})
o.spec("async callback", function() {
var a = 0, b = 0
o.after(function() {
o(a).equals(0)
o(b).equals(0)
})
o.spec("", function(){
o.before(function(done) {
callAsync(function() {
a = 1
done()
})
})
o.after(function(done) {
callAsync(function() {
a = 0
done()
})
})
o.beforeEach(function(done) {
o(b).equals(0)
callAsync(function() {
b = 1
done()
})
})
o.afterEach(function(done) {
callAsync(function() {
b = 0
done()
})
})
o("hooks work as intended the first time", function(done) {
callAsync(function() {
var spy = o.spy()
spy(a)
o(a).equals(1)
o(b).equals(1)
done()
})
})
o("hooks work as intended the second time", function(done) {
callAsync(function() {
var spy = o.spy()
spy(a)
o(a).equals(1)
o(b).equals(1)
done()
})
})
})
})
o.spec("throwing in test context is recorded as a failure", function() {
var oo
o.beforeEach(function(){oo = o.new()})
o.afterEach(function() {
oo.run(function(results) {
o(results.length).equals(1)
o(results[0].pass).equals(false)
})
})
o("sync test", function() {
oo("throw in sync test", function() {throw new Error})
})
o("async test", function() {
oo("throw in async test", function(done) {
throw new Error
done() // eslint-disable-line no-unreachable
})
})
})
o.spec("timeout", function () {
o("when using done()", function(done) {
var oo = o.new()
var err
// the success of this test is dependent on having the
// oo() call three linew below this one
try {throw new Error} catch(e) {err = e}
if (err.stack) {
var line = Number(err.stack.match(/:(\d+):/)[1])
oo("", function(oodone, timeout) {
// oodone() keep this line for now
timeout(1)
})
oo.run((function(results) {
o(results.length).equals(1)
o(results[0].pass).equals(false)
// todo test cleaned up results[0].error stack trace for the presence
// of the timeout stack entry
o(results[0].testError instanceof Error).equals(true)
o(o.cleanStackTrace(results[0].testError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
done()
}))
} else {
done()
}
})
o("when using a thenable", function(done) {
var oo = o.new()
var err
// the success of this test is dependent on having the
// oo() call three linew below this one
try {throw new Error} catch(e) {err = e}
if (err.stack) {
var line = Number(err.stack.match(/:(\d+):/)[1])
oo("", function() {
oo.timeout(1)
return {then: function(){}}
})
oo.run((function(results) {
o(results.length).equals(1)
o(results[0].pass).equals(false)
o(results[0].testError instanceof Error).equals(true)
o(o.cleanStackTrace(results[0].testError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
done()
}))
} else {
done()
}
})
})
o.spec("o.timeout", function() {
o("throws when called out of test definitions", function(done) {
var oo = o.new()
var count = 0
try { oo.timeout(1) } catch (e) { count++ }
oo.spec("a spec", function() {
try { oo.timeout(1) } catch (e) { count++ }
})
oo("", function() {
oo.timeout(30)
return {then: function(f) {setTimeout(f)}}
})
oo.run(function(){
o(count).equals(2)
done()
})
})
o("works", function(done) {
var oo = o.new()
var t = new Date
oo("", function() {
oo.timeout(10)
return {then: function() {}}
})
oo.run(function(){
o(new Date - t >= 10).equals(true)
o(200 > new Date - t).equals(true)
done()
})
})
})
o.spec("o.specTimeout", function() {
o("throws when called inside of test definitions", function(done) {
var err
var oo = o.new()
oo("", function() {
try { oo.specTimeout(5) } catch (e) {err = e}
return {then: function(f) {setTimeout(f)}}
})
oo.run(function(){
o(err instanceof Error).equals(true)
done()
})
})
o("works", function(done) {
var oo = o.new()
var t
oo.specTimeout(10)
oo.beforeEach(function () {
t = new Date
})
oo.afterEach(function () {
var diff = new Date - t
o(diff >= 10).equals(true)
o(diff < 200).equals(true)
})
oo("", function() {
oo(true).equals(true)
return {then: function() {}}
})
oo.run(function(results) {
o(results.length).equals(2)
o(results[0].pass).equals(true)
o(results[1].pass).equals(false)
done()
})
})
o("The parent and sibling suites are not affected by the specTimeout", function(done) {
var oo = o.new()
var t
oo.specTimeout(50)
oo.beforeEach(function () {
t = new Date
})
oo.afterEach(function () {
var diff = new Date - t
o(diff >= 50).equals(true)
o(diff < 80).equals(true)
})
oo.spec("nested 1", function () {
oo.specTimeout(80)
})
oo("", function() {
oo(true).equals(true)
return {then: function() {}}
})
oo.spec("nested 2", function () {
oo.specTimeout(80)
})
oo.spec("nested 3", function () {
oo("", function() {
oo(true).equals(true)
return {then: function() {}}
})
})
oo.run(function(results) {
o(results.length).equals(4)
o(results[0].pass).equals(true)
o(results[1].pass).equals(false)
o(results[2].pass).equals(true)
o(results[3].pass).equals(false)
done()
})
})
o("nested suites inherit the specTimeout", function(done) {
var oo = o.new()
oo.specTimeout(50)
oo.spec("nested", function () {
oo.spec("deeply", function() {
var t
oo.beforeEach(function () {
t = new Date
})
oo.afterEach(function () {
var diff = new Date - t
o(diff >= 50).equals(true)
o(diff < 80).equals(true)
})
oo("", function() {
oo(true).equals(true)
return {then: function() {}}
})
})
})
oo.run(function(results) {
o(results.length).equals(2)
o(results[0].pass).equals(true)
o(results[1].pass).equals(false)
done()
})
})
})
o.spec("calling done() twice throws", function () {
o("two successes", function(done) {
var oo = o.new()
var err = null
oo("foo", function(oodone) {
try {
oodone()
oodone()
} catch (e) {
err = e
}
o(err instanceof Error).equals(true)
o(err.message).equals("'oodone()' should only be called once.")
})
oo.run(function(results) {
o(results.length).equals(1)
o(results[0].pass).equals(true)
done()
})
})
o("a success followed by an error", function(done) {
var oo = o.new()
var err = null
oo("foo", function(oodone) {
try {
oodone()
oodone("error")
} catch (e) {
err = e
}
o(err instanceof Error).equals(true)
o(err.message).equals("'oodone()' should only be called once.")
})
oo.run(function(results) {
o(results.length).equals(1)
o(results[0].pass).equals(true)
done()
})
})
o("two errors", function(done) {
var oo = o.new()
var err = null
oo("foo", function(oodone) {
try {
oodone("bar")
oodone("baz")
} catch (e) {
err = e
}
o(err instanceof Error).equals(true)
o(err.message).equals("'oodone()' should only be called once.")
})
oo.run(function(results) {
o(results.length).equals(1)
o(results[0].pass).equals(false)
o(results[0].message).equals("bar")
done()
})
})
o("an error followed by a success", function(done) {
var oo = o.new()
var err = null
oo("foo", function(oodone) {
try {
oodone("bar")
oodone()
} catch (e) {
err = e
}
o(err instanceof Error).equals(true)
o(err.message).equals("'oodone()' should only be called once.")
})
oo.run(function(results) {
o(results.length).equals(1)
o(results[0].pass).equals(false)
o(results[0].message).equals("bar")
done()
})
})
})
o.spec("stack trace cleaner", function() {
o("handles line breaks", function() {
try {
throw new Error("line\nbreak")
} catch(error) {
var trace = o.cleanStackTrace(error)
o(trace).notEquals("break")
o(trace.indexOf("test-ospec.js") !== -1).equals(true)
}
})
})
o.spec("async promise", function() {
var a = 0, b = 0
function wrapPromise(fn) {
return new Promise(function (resolve, reject) {
callAsync(function () {
try {
fn()
resolve()
} catch(e) {
reject(e)
}
})
})
}
o.before(function() {
return wrapPromise(function () {
a = 1
})
})
o.after(function() {
return wrapPromise(function() {
a = 0
})
})
o.beforeEach(function() {
return wrapPromise(function() {
b = 1
})
})
o.afterEach(function() {
return wrapPromise(function() {
b = 0
})
})
o("promise functions", function() {
return wrapPromise(function() {
o(a).equals(b)
o(a).equals(1)("a and b should be initialized")
})
})
})
o.spec("descriptions", function() {
o("description returned on failure", function(done) {
var oo = o.new()
oo("no description", function() {
oo(1).equals(2)
})
oo("description", function() {
oo(1).equals(2)("howdy")
})
oo.run(function(results) {
o(results.length).equals(2)
o(results[1].message).equals(`howdy\n\n${results[0].message}`)
o(results[1].pass).equals(false)
done()
})
})
})
})
o.spec("the done parser", function() {
o("accepts non-English names", function() {
var oo = o.new()
var threw = false
oo("test", function(完了) {
oo(true).equals(true)
完了()
})
try {oo.run(function(){})} catch(e) {threw = true}
o(threw).equals(false)
})
o("tolerates comments", function() {
var oo = o.new()
var threw = false
oo("test", function(/*hey
*/ /**/ //ho
done /*hey
*/ /**/ //huuu
, timeout
) {
timeout(5)
oo(true).equals(true)
done()
})
try {oo.run(function(){})} catch(e) {threw = true}
o(threw).equals(false)
})
/*eslint-disable no-eval*/
try {eval("(()=>{})()"); o.spec("with ES6 arrow functions", function() {
function getCommentContent(f) {
f = f.toString()
return f.slice(f.indexOf("/*") + 2, f.lastIndexOf("*/"))
}
o("has no false positives 1", function(){
var oo = o.new()
var threw = false
eval(getCommentContent(function(){/*
oo(
'Async test parser mistakenly identified 1st token after a parens to be `done` reference',
done => {
oo(threw).equals(false)
done()
}
)
*/}))
try {oo.run(function(){})} catch(e) {threw = true}
o(threw).equals(false)
})
o("has no false negatives", function(){
var oo = o.new()
var threw = false
eval(getCommentContent(function(){/*
oo(
"Multiple references to the wrong thing doesn't fool the checker",
done => {
oo(threw).equals(false)
oo(threw).equals(false)
}
)
*/}))
try {oo.run(function(){})} catch(e) {threw = true}
o(threw).equals(true)
})
o("isn't fooled by comments", function(){
var oo = o.new()
var threw = false
oo(
"comments won't throw the parser off",
eval("done /*hey*/ /**/ => {oo(threw).equals(false);done()}")
)
try {oo.run(function(){})} catch(e) {threw = true}
o(threw).equals(false)
})
})} catch (e) {/*ES5 env, or no eval, ignore*/}
/*eslint-enable no-eval*/
})