"use strict";
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
{
console.log("=== Date Tests ===");
const bigDay = new Date(2019, 6, 19);
let v_date = bigDay.toLocaleDateString();
assert(v_date === "7/19/2019" || v_date === "19/7/2019" || v_date === "Fri Jul 19 2019", "bigDay should be July 19, 2019");
assert(bigDay.getTime() < Date.now(), "bigDay should be in the past");
}
{
console.log("=== Class Tests ===");
class MyClass {
constructor() {
this.constructorRan = true;
}
myField = "foo";
myMethod() {
return "instance method result";
}
static myStaticField = "bar";
static myStaticMethod() {
return "static method result";
}
static {
this.staticBlockRan = true;
}
#myPrivateField = "private bar";
getPrivateField() {
return this.#myPrivateField;
}
#myPrivateMethod() {
return "private method result";
}
callPrivateMethod() {
return this.#myPrivateMethod();
}
}
const myInstance = new MyClass();
assert(myInstance.myField === "foo", "Instance field should be accessible");
assert(myInstance.constructorRan === true, "Constructor should have run");
assert(myInstance.myMethod() === "instance method result", "Instance method should return correct value");
assert(MyClass.myStaticField === "bar", "Static field should be accessible");
assert(MyClass.myStaticMethod() === "static method result", "Static method should return correct value");
assert(MyClass.staticBlockRan === true, "Static block should have run");
assert(myInstance.getPrivateField() === "private bar", "Private field should be accessible via public method");
assert(myInstance.callPrivateMethod() === "private method result", "Private method should be accessible via public method");
}
{
console.log("=== Transpiled Class Tests ===");
function MyClass() {
this.myField = "foo";
this.constructorRan = true;
}
MyClass.myStaticField = "bar";
MyClass.myStaticMethod = function () {
return "static method result";
};
MyClass.prototype.myMethod = function () {
return "instance method result";
};
(function () {
MyClass.staticBlockRan = true;
})();
const myInstance = new MyClass();
assert(myInstance.myField === "foo", "Transpiled: Instance field should be accessible");
assert(myInstance.constructorRan === true, "Transpiled: Constructor should have run");
assert(myInstance.myMethod() === "instance method result", "Transpiled: Instance method should return correct value");
assert(MyClass.myStaticField === "bar", "Transpiled: Static field should be accessible");
assert(MyClass.myStaticMethod() === "static method result", "Transpiled: Static method should return correct value");
assert(MyClass.staticBlockRan === true, "Transpiled: Static block should have run");
}
{
console.log("=== TDZ Tests ===");
try {
let tmp =new MyClass(); assert(false, "TDZ test failed: No error thrown");
} catch (e) {
assert(e instanceof ReferenceError, "Expected a ReferenceError");
}
class MyClass {}
}
{
console.log("=== Testing class constructor call without new... ===");
class MyClass {
myField = "foo";
myMethod() {
this.myField = "bar";
}
}
const myInstance = new MyClass();
assert(myInstance.myField === "foo", "Instance field should be accessible");
myInstance.myMethod();
assert(myInstance.myField === "bar", "Instance method should modify field");
try {
let tmp = MyClass();
assert(false, "FAIL: Class constructor called without new should throw");
} catch (e) {
assert(e instanceof TypeError, "Expected a TypeError");
}
}
{
console.log("=== Testing class expression name scope... ===");
const MyClass = class MyClassLongerName {
};
try {
let tmp = new MyClassLongerName(); } catch (e) {
assert(e instanceof ReferenceError, "Expected a ReferenceError");
let tmp = new MyClass(); assert(tmp instanceof MyClass, "Instance should be of type MyClass");
}
}
{
console.log("=== Testing class constructor with rest parameters... ===");
class Color {
constructor(...values) {
this.values = values;
}
}
const red = new Color(255, 0, 0);
assert(Array.isArray(red.values), "red.values should be an array");
const anotherRed = new Color(255, 0, 0);
assert(red !== anotherRed, "Different instances should not be strictly equal");
assert(red != anotherRed, "Different instances should not be loosely equal");
}
{
console.log("=== Testing class constructor explicit return of object... ===");
class MyClass {
constructor() {
this.myField = "foo";
return {}; }
}
assert(new MyClass().myField === undefined, "Constructor returning a different object should override 'this'");
}
{
console.log("=== Testing class method access to instance fields... ===");
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
getRed() {
return this.values[0];
}
}
const red = new Color(255, 0, 0);
assert(red.getRed() === 255, "getRed should return the red component");
}
{
console.log("=== Testing method reference equality... ===");
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
this.getRed = function () {
return this.values[0];
};
}
getGreen() {
return this.values[1];
}
}
assert(typeof new Color(255, 0, 0).getRed === "function", "getRed should be a function");
assert(new Color().getRed !== new Color().getRed, "Instance method references should not be equal");
assert(new Color().getGreen === new Color().getGreen, "Prototype method references should be equal");
class A {
method() {}
}
const a = new A();
const m1 = a.method;
const m2 = a.method;
assert(m1 === m2, "Prototype method references should be equal");
function foo() {}
const f1 = foo;
const f2 = foo;
assert(f1 === f2, "Function references should be equal");
}
{
console.log("=== Testing class method modifying instance fields... ===");
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
getRed() {
return this.values[0];
}
setRed(value) {
this.values[0] = value;
}
}
const red = new Color(255, 0, 0);
red.setRed(0);
assert(red.getRed() === 0, "setRed should update the red component");
const red2 = new Color(255, 0, 0);
red2.values[0] = 0;
assert(red2.values[0] === 0, "Directly modifying values should reflect in getRed");
}
{
console.log("=== Testing class private fields... ===");
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
getRed() {
return this.#values[0];
}
setRed(value) {
if (value < 0 || value > 255) {
throw new RangeError("Invalid R value");
}
this.#values[0] = value;
}
redDifference(anotherColor) {
if (!(#values in anotherColor)) {
throw new TypeError("Color instance expected");
}
return this.#values[0] - anotherColor.#values[0];
}
}
const red = new Color(255, 0, 0);
assert(red.getRed() === 255, "getRed should return the red component");
try {
let script = `
class Test {
#values;
}
console.log(red.#values);
`;
eval(script);
assert(false, "Accessing private field should throw");
} catch (e) {
assert(e instanceof SyntaxError, "Expected a SyntaxError");
}
red.setRed(0);
assert(red.getRed() === 0, "setRed should update the red component");
try {
red.setRed(300);
assert(false, "Setting invalid red value should throw");
} catch (e) {
assert(e instanceof RangeError, "Expected a RangeError");
}
const red2 = new Color(255, 0, 0);
const crimson = new Color(220, 20, 60);
assert(red2.redDifference(crimson) === 35, "redDifference should compute the difference in red components");
}
{
console.log("=== Testing bad class private field usages... ===");
try {
eval("class BadIdeas { #firstName; #firstName; }");
} catch (e) {
assert(e instanceof SyntaxError, "Expected a SyntaxError");
}
try {
let scritpt = `
class BadIdeas {
#lastName;
constructor() {
delete this.#lastName;
}
}`;
eval(scritpt);
} catch (e) {
assert(e instanceof SyntaxError, "Expected a SyntaxError");
}
}
{
console.log("=== Testing getters and setters... ===");
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
get red() {
return this.values[0];
}
set red(value) {
this.values[0] = value;
}
}
const my_color = new Color(255, 0, 0);
assert(my_color.red === 255, "Initial red value should be 255");
my_color.red = 0;
assert(my_color.red === 0, "Red value should be updated to 0");
}
{
console.log("=== Testing read-only getter... ===");
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
get red() {
return this.values[0];
}
}
const red = new Color(255, 0, 0);
try {
red.red = 0; assert(false, "Setting read-only property 'red' should have thrown an error");
} catch (e) {
assert(e instanceof TypeError, "Caught exception should be a TypeError");
}
assert(red.red === 255, "Red value should remain 255");
}
{
console.log("=== Testing write-only setter... ===");
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
set red(value) {
this.values[0] = value;
}
}
const red = new Color(255, 0, 0);
red.red = 100;
assert(red.red === undefined, "Getting write-only property 'red' should return undefined");
}
{
console.log("=== Testing class field initializers... ===");
class MyClass {
luckyNumber = Math.random();
}
assert(new MyClass().luckyNumber < 1, "luckyNumber should be less than 1");
}
{
console.log("=== Testing class field initializers in constructor... ===");
class MyClass {
constructor() {
this.luckyNumber = Math.random();
}
}
assert(new MyClass().luckyNumber < 1, "luckyNumber should be less than 1");
}
{
console.log("=== Testing static methods... ===");
class Color {
static isValid(r, g, b) {
return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255;
}
static {
Color.myStaticProperty = "foo";
}
}
assert(Color.myStaticProperty === "foo", "Static property should be 'foo'");
assert(Color.isValid(255, 0, 0) === true, "Color (255, 0, 0) should be valid");
assert(Color.isValid(-1, 0, 0) === false, "Color (-1, 0, 0) should be invalid");
assert(Color.isValid(1000, 0, 0) === false, "Color (1000, 0, 0) should be invalid");
assert(new Color(0, 0, 0).isValid === undefined, "Instance should not have isValid method");
}
{
console.log("=== Testing subclassing and private fields... ===");
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
get red() {
return this.values[0];
}
set red(value) {
this.values[0] = value;
}
}
class ColorWithAlpha extends Color {
#alpha;
constructor(r, g, b, a) {
super(r, g, b);
this.#alpha = a;
}
get alpha() {
return this.#alpha;
}
set alpha(value) {
if (value < 0 || value > 1) {
throw new RangeError("Alpha value must be between 0 and 1");
}
this.#alpha = value;
}
}
const color = new ColorWithAlpha(255, 0, 0, 0.5);
assert(color.red === 255, "Red value should be 255");
assert(color.alpha === 0.5, "Alpha value should be 0.5");
assert(color.toString() === "[object Object]", "toString should return '[object Object]'");
}
{
class HTMLElement {
constructor() {
this.onclick = null;
this.textContent = "";
}
}
class Counter extends HTMLElement {
#xValue = 0;
constructor() {
super();
this.onclick = this.#clicked.bind(this);
}
get #x() {
return this.#xValue;
}
set #x(value) {
this.#xValue = value;
window.requestAnimationFrame(this.#render.bind(this));
}
#clicked() {
this.#x++;
}
#render() {
this.textContent = this.#x.toString();
}
connectedCallback() {
this.#render();
}
}
{
console.log("=== Testing Counter private getter/setter and event binding ===");
if (typeof globalThis.window === 'undefined') {
globalThis.window = globalThis;
}
if (typeof window.requestAnimationFrame !== 'function') {
window.requestAnimationFrame = function (cb) { cb(); };
}
const counter = new Counter();
counter.connectedCallback();
assert(counter.textContent === '0', "Counter initial render should be '0'");
assert(typeof counter.onclick === 'function', 'onclick should be a function');
counter.onclick();
assert(counter.textContent === '1', "Counter should increment to '1' after one click");
counter.onclick();
counter.onclick();
assert(counter.textContent === '3', "Counter should be '3' after three clicks");
}
}
{
console.log("=== Testing superclass method calls with super... ===");
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
get red() {
return this.#values[0];
}
set red(value) {
this.#values[0] = value;
}
toString() {
return this.#values.join(", ");
}
}
assert(new Color(255, 0, 0).toString() === "255, 0, 0", "toString should return '255, 0, 0'");
class ColorWithAlpha extends Color {
#alpha;
constructor(r, g, b, a) {
super(r, g, b);
this.#alpha = a;
}
get alpha() {
return this.#alpha;
}
set alpha(value) {
if (value < 0 || value > 1) {
throw new RangeError("Alpha value must be between 0 and 1");
}
this.#alpha = value;
}
toString() {
return `${super.toString()}, ${this.#alpha}`;
}
log() {
throw new SyntaxError("Cannot access superclass private field '#values'");
}
}
assert(new ColorWithAlpha(255, 0, 0, 0.5).toString() === "255, 0, 0, 0.5", "toString should return '255, 0, 0, 0.5'");
let tmp = new ColorWithAlpha(255, 0, 0, 0.5);
try {
tmp.log(); } catch (e) {
assert(e instanceof SyntaxError, "Expected a SyntaxError");
}
const color = new ColorWithAlpha(255, 0, 0, 0.5);
assert(color instanceof Color, "color should be instance of Color");
assert(color instanceof ColorWithAlpha, "color should be instance of ColorWithAlpha");
}
{
console.log("=== Testing super call in arrow function within constructor... ===");
var count_12_3_5_1 = 0;
class A_12_3_5_1 {
constructor() {
count_12_3_5_1++;
}
}
class B_12_3_5_1 extends A_12_3_5_1 {
constructor() {
super();
this.af = _ => super();
}
}
var b_12_3_5_1 = new B_12_3_5_1();
try {
b_12_3_5_1.af();
assert(false, "FAIL: super call in arrow function should throw");
} catch (e) {
assert(e instanceof ReferenceError, "Expected a ReferenceError");
}
assert(count_12_3_5_1 === 2, "The value of `count_12_3_5_1` is `2`, because S7 of `SuperCall : super Arguments` will call the super constructor.");
}
{
console.log("=== Testing super call in immediately invoked arrow function within constructor... ===");
var count_14_2_16 = 0;
class A_14_2_16 {
constructor() {
count_14_2_16++;
}
}
class B_14_2_16 extends A_14_2_16 {
constructor() {
(_ => super())();
}
}
var bar_14_2_16 = new B_14_2_16();
assert(count_14_2_16 === 1, "The value of `count_14_2_16` is `1`, because S7 of `SuperCall : super Arguments` will call the super constructor.");
}
{
console.log("=== Testing super call in immediately invoked arrow function within constructor (strict mode)... ===");
var count_side = 0;
class A_side { constructor() { count_side++; } }
class B_side extends A_side { constructor() { (_ => super())(); } }
new B_side();
assert(count_side === 1, "The value of `count_side` is `1`, because S7 of `SuperCall : super Arguments` will call the super constructor.");
}
{
class Foo_14_5_14 extends null {}
assert(Object.getPrototypeOf(Foo_14_5_14.prototype) === null, "The prototype of Foo_14_5_14.prototype should be null");
assert(Object.getPrototypeOf(Foo_14_5_14.prototype.constructor) === Function.prototype, "The prototype of Foo_14_5_14.prototype.constructor should be Function.prototype");
assert(Foo_14_5_14 === Foo_14_5_14.prototype.constructor, "Foo_14_5_14 should be equal to Foo_14_5_14.prototype.constructor");
}