Files
meteor/packages/ecmascript/runtime-tests.js
Ben Newman ba0aa4d709 Avoid _.extend in IE8-sensitive ecmascript tests.
The underlying problem is that `es5-shim` polyfills `Array.prototype`
methods in IE8, but it can't actually make them non-enumerable, so
`_.extend` copies them, because it doesn't check `hasOwnProperty`.

This probably calls for a more general audit of for-in loops over objects
that could be Arrays, especially within underscore.
2015-07-24 16:54:16 -04:00

290 lines
6.5 KiB
JavaScript

Tinytest.add("ecmascript - runtime - template literals", (test) => {
function dump(pieces) {
var copy = {};
// Can't use _.extend({}, pieces) because es5-shim adds enumerable
// methods to Array.prototype, and _.extend has no own property check.
_.each(_.keys(pieces), key => copy[key] = pieces[key]);
return [copy, _.toArray(arguments).slice(1)];
};
const foo = 'B';
// uses `babelHelpers.taggedTemplateLiteralLoose`
test.equal(`\u0041${foo}C`, 'ABC');
test.equal(dump`\u0041${foo}C`,
[{0:'A', 1: 'C', raw: ['\\u0041', 'C']},
['B']]);
});
Tinytest.add("ecmascript - runtime - classes - basic", (test) => {
{
class Foo {
constructor(x) {
this.x = x;
}
}
test.throws(() => {
Foo(); // called without `new`
});
test.equal((new Foo(3)).x, 3);
}
{
class Bar {
constructor(x) {
this.x = x;
}
}
class Foo extends Bar {}
test.throws(() => {
Foo(); // called without `new`
});
test.equal((new Foo(3)).x, 3);
test.isTrue((new Foo(3)) instanceof Foo);
test.isTrue((new Foo(3)) instanceof Bar);
}
{
class Foo {
static staticMethod() {
return 'classy';
}
prototypeMethod() {
return 'prototypical';
}
}
test.equal(Foo.staticMethod(), 'classy');
test.equal((new Foo).prototypeMethod(), 'prototypical');
}
});
Tinytest.add("ecmascript - runtime - classes - use before declare", (test) => {
const x = function asdf() {};
if (typeof asdf === 'function') {
// We seem to be in IE 8, where function names leak into the enclosing
// scope, contrary to the spec. In this case, Babel does not (currently)
// throw an error if you use a class before you declare it. (Of course,
// any other browser can tell the developer they screwed up!)
test.expect_fail();
}
test.throws(() => {
new Foo(); // use before definition
class Foo {}
});
});
Tinytest.add("ecmascript - runtime - classes - inheritance", (test) => {
// uses `babelHelpers.inherits`
{
class Foo {
static static1() {
return 1;
}
}
Foo.static2 = function () {
return 2;
};
// static methods are inherited!
class Bar extends Foo {}
test.equal(Foo.static1(), 1);
test.equal(Foo.static2(), 2);
test.equal(Bar.static1(), 1);
test.equal(Bar.static2(), 2);
}
{
const buf = [];
class Foo {
constructor() {
buf.push('hi');
}
}
class Bar extends Foo {}
new Bar();
// derived class with no constructor gets a default constructor
// that calls the super constructor
test.equal(buf, ['hi']);
}
});
Tinytest.add("ecmascript - runtime - classes - computed props", (test) => {
{
const frob = "inc";
class Foo {
static [frob](n) { return n+1; }
}
test.equal(Foo.inc(3), 4);
}
});
if (Meteor.isServer) {
// getters and setters don't work in all clients, but they should work
// in classes on browsers that support them in the first place, and on
// the server. (Technically they just need a working
// Object.defineProperty, found in IE9+ and all modern environments.)
Tinytest.add("ecmascript - runtime - classes - getters/setters", (test) => {
// uses `babelHelpers.createClass`
class Foo {
get two() { return 1+1; }
static get three() { return 1+1+1; }
}
test.equal((new Foo).two, 2);
test.equal(Foo.three, 3);
});
}
Tinytest.add("ecmascript - runtime - block scope", (test) => {
{
const buf = [];
const thunks = [];
function print(x) {
buf.push(x);
};
function doLater(f) {
thunks.push(f);
};
for (let i = 0; i < 3; i++) {
print(i);
}
test.equal(buf, [0, 1, 2]);
buf.length = 0;
for (let i = 0; i < 3; i++) {
doLater(() => {
print(i);
});
}
_.each(thunks, f => f());
test.equal(buf, [0, 1, 2]);
}
});
Tinytest.add("ecmascript - runtime - classes - super", (test) => {
{
class Class1 {
foo() { return 123; }
static bar() { return 1; }
}
class Class2 extends Class1 {}
class Class3 extends Class2 {
foo() {
return super.foo() + Class3.bar();
}
}
test.equal((new Class3).foo(), 124);
}
{
class Foo {
constructor(value) { this.value = value; }
x() { return this.value; }
}
class Bar extends Foo {
constructor() { super(123); }
x() { return super.x(); }
}
test.equal((new Bar).x(), 123);
}
});
Tinytest.add("ecmascript - runtime - object rest/spread", (test) => {
const middle = {b:2, c:3};
// uses `babelHelpers._extends`
const full = {a:1, ...middle, d:4};
test.equal(full, {a:1, b:2, c:3, d:4});
});
Tinytest.add("ecmascript - runtime - spread args to new", (test) => {
const Foo = function (one, two, three) {
test.isTrue(this instanceof Foo);
test.equal(one, 1);
test.equal(two, 2);
test.equal(three, 3);
this.created = true;
};
const oneTwo = [1, 2];
// uses `babelHelpers.bind`
const foo = new Foo(...oneTwo, 3);
test.isTrue(foo.created);
});
Tinytest.add("ecmascript - runtime - destructuring", (test) => {
// uses `babelHelpers.objectWithoutProperties` and
// `babelHelpers.objectDestructuringEmpty`
const obj = {a:1, b:2};
const {a, ...rest} = obj;
test.equal(a, 1);
test.equal(rest, {b:2});
const {} = {};
test.throws(() => {
const {} = null;
}, /Cannot destructure undefined/);
});
Tinytest.addAsync("ecmascript - runtime - misc support", (test, done) => {
// Verify that the runtime was installed.
test.equal(typeof babelHelpers, "object");
class Base {
constructor(...args) {
this.sum = 0;
args.forEach(arg => this.sum += arg);
}
static inherited() {
return "inherited";
}
}
class Derived extends Base {
constructor() {
super(1, 2, 3);
}
}
// Check that static methods are inherited.
test.equal(Derived.inherited(), "inherited");
const d = new Derived();
test.equal(d.sum, 6);
const expectedError = new Error("expected");
Promise.resolve("working").then(result => {
test.equal(result, "working");
throw expectedError;
}).catch(error => {
test.equal(error, expectedError);
if (Meteor.isServer) {
const Fiber = Npm.require("fibers");
// Make sure the Promise polyfill runs callbacks in a Fiber.
test.instanceOf(Fiber.current, Fiber);
}
}).then(done, error => test.exception(error));
});