blob: a5eba425b6d22469c37ee50e2845dcaa87ff8967 [file] [log] [blame]
<!DOCTYPE html>
<meta charset="utf-8">
<title>KV Storage: IDL interface tests</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/WebIDLParser.js"></script>
<script src="/resources/idlharness.js"></script>
<script type="module">
import storage, { StorageArea } from "std:kv-storage";
// Web IDL/idlharness.js do not yet have support for the spec's IDL, which uses module {},
// async_iterator, and some new extended attributes. This IDL is a mutated version to work with the
// current idlharness.js to get some coverage.
//
// When the relevant Web IDL PRs land and idlharness.js gets updated, we can replace this file with
// a normal-style idlharness test, with the IDL scraped from the spec.
//
// Other tests in this file are similarly ones that should be generatable from the IDL, in theory.
const idl = `
[Constructor(DOMString name)]
interface StorageArea {
Promise<void> set(any key, any value);
Promise<any> get(any key);
Promise<void> delete(any key);
Promise<void> clear();
// async_iterable<any, any>;
[SameObject] readonly attribute object backingStore;
};
`;
// Define a global property because idlharness.js only understands
// global properties, not modules.
Object.defineProperty(window, "StorageArea", {
configurable: true,
enumerable: false,
writable: true,
value: StorageArea
});
test(t => {
window.testStorageArea = storage;
t.add_cleanup(() => {
delete window.testStorageArea;
});
const idlArray = new IdlArray();
idlArray.add_idls(idl);
idlArray.add_objects({ StorageArea: ["window.testStorageArea"] });
idlArray.test();
}, "idlharness basic interface tests");
test(() => {
assert_equals(storage instanceof StorageArea, true, "instanceof");
assert_equals(storage.constructor, StorageArea, ".constructor property");
assert_equals(Object.getPrototypeOf(storage), StorageArea.prototype, "[[Prototype]]");
}, "Built-in storage export is a StorageArea");
// These should be auto-tested by idlharness eventually, similar to
// https://github.com/web-platform-tests/wpt/blob/3725067ef0328c998be2ba93dbaeb0579586fccd/resources/idlharness.js#L2452
// for sync iterators (although this tests more than that does).
test(() => {
for (const methodName of ["keys", "values", "entries"]) {
const descriptor = Object.getOwnPropertyDescriptor(StorageArea.prototype, methodName);
assert_true(descriptor.writable, `${methodName} property should be writable`);
assert_true(descriptor.configurable, `${methodName} property should be configurable`);
// May need updating if https://github.com/heycam/webidl/issues/738 changes the spec
assert_true(descriptor.enumerable, `${methodName} property should be enumerable`);
assert_equals(typeof descriptor.value, "function",
`${methodName} property should be a function`);
assert_equals(descriptor.value.length, 0, `${methodName} function object length should be 0`);
assert_equals(descriptor.value.name, methodName,
`${methodName} function object should have the right name`);
assert_throws(new TypeError(), () => descriptor.value.call(StorageArea.prototype),
`${methodName} should throw when called on the prototype directly`);
assert_throws(new TypeError(), () => descriptor.value.call({}),
`${methodName} should throw when called on an empty object`);
}
const AsyncIteratorPrototype =
Object.getPrototypeOf(Object.getPrototypeOf(async function*() {}).prototype);
const asyncIteratorProto = Object.getPrototypeOf(storage.keys());
assert_equals(Object.getPrototypeOf(storage.values()), asyncIteratorProto,
"keys() and values() return values must have the same prototype");
assert_equals(Object.getPrototypeOf(storage.entries()), asyncIteratorProto,
"keys() and entries() return values must have the same prototype");
assert_equals(Object.getPrototypeOf(asyncIteratorProto), AsyncIteratorPrototype,
"[[Prototype]] must be the async iterator prototype");
assert_array_equals(Object.getOwnPropertyNames(asyncIteratorProto), ["next"],
`async iterator prototype object must have a next method`);
const nextMethodDescriptor = Object.getOwnPropertyDescriptor(asyncIteratorProto, "next");
assert_true(nextMethodDescriptor.writable, `async iterator next property should be writable`);
assert_true(nextMethodDescriptor.configurable,
`async iterator next property should be configurable`);
// May need updating if https://github.com/heycam/webidl/issues/739 changes the spec
assert_true(nextMethodDescriptor.enumerable,
`async iterator next property should be enumerable`);
assert_equals(typeof nextMethodDescriptor.value, "function",
`async iterator next property should be a function`);
assert_equals(nextMethodDescriptor.value.length, 0,
`async iterator next function object length should be 0`);
assert_equals(nextMethodDescriptor.value.name, "next",
`async iterator next function object should have the right name`);
assert_array_equals(Object.getOwnPropertySymbols(asyncIteratorProto), [Symbol.toStringTag]);
const toStringTagDescriptor = Object.getOwnPropertyDescriptor(
asyncIteratorProto,
Symbol.toStringTag
);
assert_false(toStringTagDescriptor.writable,
`async iterator @@toStringTag property should be non-writable`);
assert_true(toStringTagDescriptor.configurable,
`async iterator @@toStringTag property should be configurable`);
assert_false(toStringTagDescriptor.enumerable,
`async iterator @@toStringTag property should be non-enumerable`);
assert_equals(toStringTagDescriptor.value, "StorageArea AsyncIterator",
`async iterator @@toStringTag property should have the right value`);
assert_equals(StorageArea.prototype[Symbol.asyncIterator], StorageArea.prototype.entries,
"@@asyncIterator method should be the same as entries");
}, "@@asyncIterator tests");
promise_test(async t => {
const iframe = document.createElement("iframe");
iframe.src = "helpers/expose-as-global.html";
document.body.append(iframe);
await frameLoadPromise(iframe);
const OtherStorageArea = iframe.contentWindow.StorageArea;
await promise_rejects(t, new TypeError(),
OtherStorageArea.prototype.set.call(storage, "testkey", "testvalue"),
`set() must reject cross-realm`);
await promise_rejects(t, new TypeError(),
OtherStorageArea.prototype.get.call(storage, "testkey"),
`get() must reject cross-realm`);
await promise_rejects(t, new TypeError(),
OtherStorageArea.prototype.delete.call(storage, "testkey"),
`delete() must reject cross-realm`);
await promise_rejects(t, new TypeError(), OtherStorageArea.prototype.clear.call(storage),
`clear() must reject cross-realm`);
assert_throws(new TypeError(), () => OtherStorageArea.prototype.keys.call(storage),
`keys() must throw cross-realm`);
assert_throws(new TypeError(), () => OtherStorageArea.prototype.values.call(storage),
`values() must throw cross-realm`);
assert_throws(new TypeError(), () => OtherStorageArea.prototype.entries.call(storage),
`entries() must throw cross-realm`);
const otherBackingStoreGetter =
Object.getOwnPropertyDescriptor(OtherStorageArea.prototype, "backingStore").get;
assert_throws(new TypeError(), () => otherBackingStoreGetter.call(storage),
`backingStore must throw cross-realm`);
}, "Same-realm brand checks");
function frameLoadPromise(frame) {
return new Promise((resolve, reject) => {
frame.onload = resolve;
frame.onerror = () => reject(new Error(`${frame.src} failed to load`));
});
}
</script>