Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428import { test, summary } from './helpers.js';
console.log('Class Tests\n');
class Person { constructor(name, age) { this.name = name; this.age = age; }}
let person = new Person('Alice', 30);test('constructor sets name', person.name, 'Alice');test('constructor sets age', person.age, 30);
class Calculator { constructor(value) { this.value = value; } add(n) { this.value = this.value + n; return this.value; } subtract(n) { this.value = this.value - n; return this.value; }}
let calc = new Calculator(10);test('method add', calc.add(5), 15);test('method subtract', calc.subtract(3), 12);test('prototype method not enumerable', Calculator.prototype.propertyIsEnumerable('add'), false);
class Point { setCoords(x, y) { this.x = x; this.y = y; } getDistance() { return this.x * this.x + this.y * this.y; }}
let p = new Point();p.setCoords(3, 4);test('no constructor class', p.getDistance(), 25);
class Counter { constructor(start) { this.count = start; } increment() { this.count = this.count + 1; return this.count; }}
let c1 = new Counter(0);let c2 = new Counter(10);test('instance isolation c1', c1.increment(), 1);test('instance isolation c2', c2.increment(), 11);
test('typeof instance', typeof person, 'object');test('instanceof Object', person instanceof Object, true);
class Rectangle { constructor(width, height) { this.width = width; this.height = height; } area() { return this.width * this.height; }}
let rect = new Rectangle(5, 10);test('method uses props', rect.area(), 50);rect.width = 8;test('modified prop', rect.area(), 80);
let p2 = new Person('Charlie', 35);delete p2.name;test('delete property', p2.name, undefined);test('other prop still exists', p2.age, 35);
class Chainable { constructor(value) { this.value = value; } add(n) { this.value = this.value + n; return this; } multiply(n) { this.value = this.value * n; return this; } get() { return this.value; }}
let chain = new Chainable(5);test('method chaining', chain.add(3).multiply(2).get(), 16);
class Feature { constructor(name) { this.name = name; this.enabled = false; } enable() { this.enabled = true; } disable() { this.enabled = false; }}
let feature = new Feature('DarkMode');test('initial bool prop', feature.enabled, false);feature.enable();test('toggle bool prop', feature.enabled, true);
class Animal { constructor(name) { this.name = name; } speak() { return this.name + ' makes a sound'; }}
class Dog extends Animal { speak() { return this.name + ' barks'; }}
let dog = new Dog('Rex');test('inheritance', dog.speak(), 'Rex barks');test('instanceof parent', dog instanceof Animal, true);test('instanceof child', dog instanceof Dog, true);
class Cat extends Animal { speak() { return super.speak() + ' - meow!'; }}
let cat = new Cat('Whiskers');test('super.method()', cat.speak(), 'Whiskers makes a sound - meow!');
class Bird extends Animal { speak() { let method = 'speak'; return super[method]() + ' - chirp!'; }}
let bird = new Bird('Tweety');test('super[method]()', bird.speak(), 'Tweety makes a sound - chirp!');
class SpreadBase { constructor(a, b) { this.sum = a + b; }}
class SpreadDerived extends SpreadBase { constructor() { super(...arguments); }}
let spreadDerived = new SpreadDerived(3, 4);test('super spread arguments', spreadDerived.sum, 7);
class Static { static value = 42; static method() { return 'static'; }}
test('static property', Static.value, 42);test('static method', Static.method(), 'static');test('static method not enumerable', Static.propertyIsEnumerable('method'), false);test('static field enumerable', Static.propertyIsEnumerable('value'), true);
class StrictStaticMethod { static method() { return this === undefined; }}
test('static class method implicit strict this', (0, StrictStaticMethod.method)(), true);
class StrictClassMethod { method() { return this === undefined; }}
const strictClassMethod = new StrictClassMethod().method;test('class method implicit strict this', strictClassMethod(), true);
let staticBlockStrict = false;try { Function('class C { static { with ({}) {} } }')();} catch (e) { staticBlockStrict = e instanceof SyntaxError;}test('class static block parses strict', staticBlockStrict, true);
class StaticNamedElements { static(value) { return value + 1; }}
let staticNamedElements = new StaticNamedElements();test('instance method named static', staticNamedElements.static(4), 5);
class StaticNamedField { static = 7;}
let staticNamedField = new StaticNamedField();test('instance field named static', staticNamedField.static, 7);
class BankAccount { #balance = 0;
constructor(initialBalance) { this.#balance = initialBalance; }
deposit(amount) { this.#balance = this.#balance + amount; return this.#balance; }
withdraw(amount) { this.#balance = this.#balance - amount; return this.#balance; }
getBalance() { return this.#balance; }}
let account = new BankAccount(100);test('private field initializer', account.getBalance(), 100);test('private field deposit', account.deposit(50), 150);test('private field withdraw', account.withdraw(30), 120);
class Vehicle { #speed = 0;
constructor(initialSpeed) { this.#speed = initialSpeed; }
getSpeed() { return this.#speed; }
accelerate(amount) { this.#speed = this.#speed + amount; }}
class Car extends Vehicle { #model;
constructor(model, speed) { super(speed); this.#model = model; }
getModel() { return this.#model; }}
let car = new Car('Tesla', 0);test('inherited private field', car.getSpeed(), 0);car.accelerate(50);test('inherited method with private field', car.getSpeed(), 50);test('own private field', car.getModel(), 'Tesla');
class Accumulator { count = 0;
increment() { this.count = this.count + 1; return this.count; }}
let accumulator = new Accumulator();test('field initializer', accumulator.count, 0);test('method with initialized field', accumulator.increment(), 1);
class Temperature { #celsius = 0;
constructor(celsius) { this.#celsius = celsius; }
get fahrenheit() { return (this.#celsius * 9) / 5 + 32; }
set fahrenheit(f) { this.#celsius = ((f - 32) * 5) / 9; }}
let temp = new Temperature(0);test('getter', temp.fahrenheit, 32);temp.fahrenheit = 212;test('setter', Math.round(temp.fahrenheit), 212);
function outerBinding(a, b) { return 'fn:' + a + b;}
const Boxed = class outerBinding { constructor(value) { this.value = value; } clone() { return new outerBinding(this.value); }};
test('class expression name does not leak', typeof outerBinding === 'function' && outerBinding !== Boxed, true);test('outer binding still callable', outerBinding(1, 2), 'fn:12');test('class expression inner name resolves', new Boxed(5).clone() instanceof Boxed, true);
let shadowed = 'untouched';const Shadow = class shadowed {};test('class expression name leaves let binding alone', shadowed, 'untouched');test('class expression still constructs', typeof Shadow, 'function');
function classCallError(call) { try { call(); return 'no error'; } catch (error) { return error.name + ': ' + error.message; }}
class ExplicitClassCall { constructor(value) { this.value = value; }}
test( 'explicit class constructor requires new', classCallError(() => ExplicitClassCall(1)), "TypeError: Class constructor cannot be invoked without 'new'");test('explicit class constructor still constructs', new ExplicitClassCall(2).value, 2);
class FieldClassCall { value = 3;}test( 'synthesized field constructor requires new', classCallError(() => FieldClassCall()), "TypeError: Class constructor cannot be invoked without 'new'");
class ArrowSuperBase { constructor(value) { this.value = value; this.seenNewTarget = new.target; }}
class ArrowSuperDerived extends ArrowSuperBase { constructor(value) { const callSuper = () => super(value); test('arrow super returns lexical this', callSuper(), this); }}
const arrowSuper = new ArrowSuperDerived(41);test('arrow super forwards arguments', arrowSuper.value, 41);test('arrow super preserves new.target', arrowSuper.seenNewTarget, ArrowSuperDerived);
class SpreadArrowSuperDerived extends ArrowSuperBase { constructor(...args) { (() => super(...args))(); }}
const spreadArrowSuper = new SpreadArrowSuperDerived(42);test('spread arrow super forwards arguments', spreadArrowSuper.value, 42);test( 'spread arrow super preserves new.target', spreadArrowSuper.seenNewTarget, SpreadArrowSuperDerived);
class NestedArrowSuperDerived extends ArrowSuperBase { constructor(value) { (() => () => super(value))()(); }}
const nestedArrowSuper = new NestedArrowSuperDerived(43);test('nested arrow super forwards arguments', nestedArrowSuper.value, 43);test( 'nested arrow super preserves new.target', nestedArrowSuper.seenNewTarget, NestedArrowSuperDerived);
summary();