mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-23 01:23:19 +00:00

This contains prettier formatting fixes for using-declaration.js. The file isn't fully formatted at this state. There is a minor scoping code change that must happen in the next commit to be able to remove this file from .prettierignore, but I wanted to separate the code change from the formatting change to improve the review process. This contains formatting and changing single quotes to double quotes.
447 lines
13 KiB
JavaScript
447 lines
13 KiB
JavaScript
describe("basic usage", () => {
|
|
test.xfail("disposes after block exit", () => {
|
|
let disposed = false;
|
|
let inBlock = false;
|
|
{
|
|
expect(disposed).toBeFalse();
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
disposed = true;
|
|
},
|
|
};
|
|
inBlock = true;
|
|
expect(disposed).toBeFalse();
|
|
}
|
|
|
|
expect(inBlock).toBeTrue();
|
|
expect(disposed).toBeTrue();
|
|
});
|
|
|
|
test.xfail("disposes in reverse order after block exit", () => {
|
|
const disposed = [];
|
|
{
|
|
expect(disposed).toHaveLength(0);
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
disposed.push("a");
|
|
},
|
|
};
|
|
using b = {
|
|
[Symbol.dispose]() {
|
|
disposed.push("b");
|
|
},
|
|
};
|
|
expect(disposed).toHaveLength(0);
|
|
}
|
|
|
|
expect(disposed).toEqual(["b", "a"]);
|
|
});
|
|
|
|
test.xfail("disposes in reverse order after block exit even in same declaration", () => {
|
|
const disposed = [];
|
|
{
|
|
expect(disposed).toHaveLength(0);
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
disposed.push("a");
|
|
},
|
|
},
|
|
b = {
|
|
[Symbol.dispose]() {
|
|
disposed.push("b");
|
|
},
|
|
};
|
|
expect(disposed).toHaveLength(0);
|
|
}
|
|
|
|
expect(disposed).toEqual(["b", "a"]);
|
|
});
|
|
});
|
|
|
|
describe("behavior with exceptions", () => {
|
|
function ExpectedError(name) {
|
|
this.name = name;
|
|
}
|
|
|
|
test.xfail("is run even after throw", () => {
|
|
let disposed = false;
|
|
let inBlock = false;
|
|
let inCatch = false;
|
|
try {
|
|
expect(disposed).toBeFalse();
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
disposed = true;
|
|
},
|
|
};
|
|
inBlock = true;
|
|
expect(disposed).toBeFalse();
|
|
throw new ExpectedError();
|
|
expect().fail();
|
|
} catch (e) {
|
|
expect(disposed).toBeTrue();
|
|
expect(e).toBeInstanceOf(ExpectedError);
|
|
inCatch = true;
|
|
}
|
|
expect(disposed).toBeTrue();
|
|
expect(inBlock).toBeTrue();
|
|
expect(inCatch).toBeTrue();
|
|
});
|
|
|
|
test.xfail("throws error if dispose method does", () => {
|
|
let disposed = false;
|
|
let endOfTry = false;
|
|
let inCatch = false;
|
|
try {
|
|
expect(disposed).toBeFalse();
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
disposed = true;
|
|
throw new ExpectedError();
|
|
},
|
|
};
|
|
expect(disposed).toBeFalse();
|
|
endOfTry = true;
|
|
} catch (e) {
|
|
expect(disposed).toBeTrue();
|
|
expect(e).toBeInstanceOf(ExpectedError);
|
|
inCatch = true;
|
|
}
|
|
expect(disposed).toBeTrue();
|
|
expect(endOfTry).toBeTrue();
|
|
expect(inCatch).toBeTrue();
|
|
});
|
|
|
|
test.xfail("if block and using throw get suppressed error", () => {
|
|
let disposed = false;
|
|
let inCatch = false;
|
|
try {
|
|
expect(disposed).toBeFalse();
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
disposed = true;
|
|
throw new ExpectedError("dispose");
|
|
},
|
|
};
|
|
expect(disposed).toBeFalse();
|
|
throw new ExpectedError("throw");
|
|
} catch (e) {
|
|
expect(disposed).toBeTrue();
|
|
expect(e).toBeInstanceOf(SuppressedError);
|
|
expect(e.error).toBeInstanceOf(ExpectedError);
|
|
expect(e.error.name).toBe("dispose");
|
|
expect(e.suppressed).toBeInstanceOf(ExpectedError);
|
|
expect(e.suppressed.name).toBe("throw");
|
|
inCatch = true;
|
|
}
|
|
expect(disposed).toBeTrue();
|
|
expect(inCatch).toBeTrue();
|
|
});
|
|
|
|
test.xfail("multiple throwing disposes give suppressed error", () => {
|
|
let inCatch = false;
|
|
try {
|
|
{
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
throw new ExpectedError("a");
|
|
},
|
|
};
|
|
|
|
using b = {
|
|
[Symbol.dispose]() {
|
|
throw new ExpectedError("b");
|
|
},
|
|
};
|
|
}
|
|
|
|
expect().fail();
|
|
} catch (e) {
|
|
expect(e).toBeInstanceOf(SuppressedError);
|
|
expect(e.error).toBeInstanceOf(ExpectedError);
|
|
expect(e.error.name).toBe("a");
|
|
expect(e.suppressed).toBeInstanceOf(ExpectedError);
|
|
expect(e.suppressed.name).toBe("b");
|
|
inCatch = true;
|
|
}
|
|
expect(inCatch).toBeTrue();
|
|
});
|
|
|
|
test.xfail("3 throwing disposes give chaining suppressed error", () => {
|
|
let inCatch = false;
|
|
try {
|
|
{
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
throw new ExpectedError("a");
|
|
},
|
|
};
|
|
|
|
using b = {
|
|
[Symbol.dispose]() {
|
|
throw new ExpectedError("b");
|
|
},
|
|
};
|
|
|
|
using c = {
|
|
[Symbol.dispose]() {
|
|
throw new ExpectedError("c");
|
|
},
|
|
};
|
|
}
|
|
|
|
expect().fail();
|
|
} catch (e) {
|
|
expect(e).toBeInstanceOf(SuppressedError);
|
|
expect(e.error).toBeInstanceOf(ExpectedError);
|
|
expect(e.error.name).toBe("a");
|
|
expect(e.suppressed).toBeInstanceOf(SuppressedError);
|
|
|
|
const inner = e.suppressed;
|
|
|
|
expect(inner.error).toBeInstanceOf(ExpectedError);
|
|
expect(inner.error.name).toBe("b");
|
|
expect(inner.suppressed).toBeInstanceOf(ExpectedError);
|
|
expect(inner.suppressed.name).toBe("c");
|
|
inCatch = true;
|
|
}
|
|
expect(inCatch).toBeTrue();
|
|
});
|
|
|
|
test.xfail("normal error and multiple disposing errors give chaining suppressed errors", () => {
|
|
let inCatch = false;
|
|
try {
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
throw new ExpectedError("a");
|
|
},
|
|
};
|
|
|
|
using b = {
|
|
[Symbol.dispose]() {
|
|
throw new ExpectedError("b");
|
|
},
|
|
};
|
|
|
|
throw new ExpectedError("top");
|
|
} catch (e) {
|
|
expect(e).toBeInstanceOf(SuppressedError);
|
|
expect(e.error).toBeInstanceOf(ExpectedError);
|
|
expect(e.error.name).toBe("a");
|
|
expect(e.suppressed).toBeInstanceOf(SuppressedError);
|
|
|
|
const inner = e.suppressed;
|
|
|
|
expect(inner.error).toBeInstanceOf(ExpectedError);
|
|
expect(inner.error.name).toBe("b");
|
|
expect(inner.suppressed).toBeInstanceOf(ExpectedError);
|
|
expect(inner.suppressed.name).toBe("top");
|
|
inCatch = true;
|
|
}
|
|
expect(inCatch).toBeTrue();
|
|
});
|
|
});
|
|
|
|
describe("works in a bunch of scopes", () => {
|
|
test.xfail("works in block", () => {
|
|
let dispose = false;
|
|
expect(dispose).toBeFalse();
|
|
{
|
|
expect(dispose).toBeFalse();
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
dispose = true;
|
|
},
|
|
};
|
|
expect(dispose).toBeFalse();
|
|
}
|
|
expect(dispose).toBeTrue();
|
|
});
|
|
|
|
test.xfail("works in static class block", () => {
|
|
let dispose = false;
|
|
expect(dispose).toBeFalse();
|
|
class A {
|
|
static {
|
|
expect(dispose).toBeFalse();
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
dispose = true;
|
|
},
|
|
};
|
|
expect(dispose).toBeFalse();
|
|
}
|
|
}
|
|
expect(dispose).toBeTrue();
|
|
});
|
|
|
|
test.xfail("works in function", () => {
|
|
let dispose = [];
|
|
function f(val) {
|
|
const disposeLength = dispose.length;
|
|
using a = {
|
|
[Symbol.dispose]() {
|
|
dispose.push(val);
|
|
},
|
|
};
|
|
expect(dispose.length).toBe(disposeLength);
|
|
}
|
|
expect(dispose).toEqual([]);
|
|
f(0);
|
|
expect(dispose).toEqual([0]);
|
|
f(1);
|
|
expect(dispose).toEqual([0, 1]);
|
|
});
|
|
|
|
test.xfail("switch block is treated as full block in function", () => {
|
|
let disposeFull = [];
|
|
let disposeInner = false;
|
|
|
|
function pusher(val) {
|
|
return {
|
|
val,
|
|
[Symbol.dispose]() {
|
|
disposeFull.push(val);
|
|
},
|
|
};
|
|
}
|
|
|
|
switch (2) {
|
|
case 3:
|
|
using notDisposed = { [Symbol.dispose]() { expect().fail("not-disposed 1"); } };
|
|
case 2:
|
|
expect(disposeFull).toEqual([]);
|
|
using a = pusher("a");
|
|
expect(disposeFull).toEqual([]);
|
|
|
|
using b = pusher("b");
|
|
expect(disposeFull).toEqual([]);
|
|
expect(b.val).toBe("b");
|
|
|
|
expect(disposeInner).toBeFalse();
|
|
// fallthrough
|
|
case 1: {
|
|
expect(disposeFull).toEqual([]);
|
|
expect(disposeInner).toBeFalse();
|
|
|
|
using inner = { [Symbol.dispose]() { disposeInner = true; } }
|
|
|
|
expect(disposeInner).toBeFalse();
|
|
}
|
|
expect(disposeInner).toBeTrue();
|
|
using c = pusher("c");
|
|
expect(c.val).toBe("c");
|
|
break;
|
|
case 0:
|
|
using notDisposed2 = { [Symbol.dispose]() { expect().fail("not-disposed 2"); } };
|
|
}
|
|
|
|
expect(disposeInner).toBeTrue();
|
|
expect(disposeFull).toEqual(["c", "b", "a"]);
|
|
});
|
|
});
|
|
|
|
describe("invalid using bindings", () => {
|
|
test.xfail("nullish values do not throw", () => {
|
|
using a = null,
|
|
b = undefined;
|
|
expect(a).toBeNull();
|
|
expect(b).toBeUndefined();
|
|
});
|
|
|
|
test.xfail("non-object throws", () => {
|
|
[0, "a", true, NaN, 4n, Symbol.dispose].forEach(value => {
|
|
expect(() => {
|
|
using v = value;
|
|
}).toThrowWithMessage(TypeError, "is not an object");
|
|
});
|
|
});
|
|
|
|
test.xfail("object without dispose throws", () => {
|
|
expect(() => {
|
|
using a = {};
|
|
}).toThrowWithMessage(TypeError, "does not have dispose method");
|
|
});
|
|
|
|
test.xfail("object with non callable dispose throws", () => {
|
|
[0, "a", true, NaN, 4n, Symbol.dispose, [], {}].forEach(value => {
|
|
expect(() => {
|
|
using a = { [Symbol.dispose]: value };
|
|
}).toThrowWithMessage(TypeError, "is not a function");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("using is still a valid variable name", () => {
|
|
test("var", () => {
|
|
"use strict";
|
|
var using = 1;
|
|
expect(using).toBe(1);
|
|
});
|
|
|
|
test("const", () => {
|
|
"use strict";
|
|
const using = 1;
|
|
expect(using).toBe(1);
|
|
});
|
|
|
|
test("let", () => {
|
|
"use strict";
|
|
let using = 1;
|
|
expect(using).toBe(1);
|
|
});
|
|
|
|
test.xfail("using", () => {
|
|
"use strict";
|
|
using using = null;
|
|
expect(using).toBeNull();
|
|
});
|
|
|
|
test("function", () => {
|
|
"use strict";
|
|
function using() {
|
|
return 1;
|
|
}
|
|
expect(using()).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("syntax errors / weird artifacts which remain valid", () => {
|
|
test("no patterns in using", () => {
|
|
expect("using {a} = {}").not.toEval();
|
|
expect("using a, {a} = {}").not.toEval();
|
|
expect("using a = null, [b] = [null]").not.toEval();
|
|
});
|
|
|
|
test("using with array pattern is valid array access", () => {
|
|
const using = [0, 9999];
|
|
const a = 1;
|
|
|
|
expect(eval("using [a] = 1")).toBe(1);
|
|
expect(using[1]).toBe(1);
|
|
|
|
expect(eval("using [{a: a}, a] = 2")).toBe(2);
|
|
expect(using[1]).toBe(2);
|
|
|
|
expect(eval("using [a, a] = 3")).toBe(3);
|
|
expect(using[1]).toBe(3);
|
|
|
|
expect(eval("using [[a, a], a] = 4")).toBe(4);
|
|
expect(using[1]).toBe(4);
|
|
|
|
expect(eval("using [2, 1, a] = 5")).toBe(5);
|
|
expect(using[1]).toBe(5);
|
|
});
|
|
|
|
test("declaration without initializer", () => {
|
|
expect("using a").not.toEval();
|
|
});
|
|
|
|
test("no repeat declarations in single using", () => {
|
|
expect("using a = null, a = null;").not.toEval();
|
|
});
|
|
|
|
test("cannot have a using declaration named let", () => {
|
|
expect("using let = null").not.toEval();
|
|
});
|
|
});
|