207 lines
6.9 KiB
JavaScript
207 lines
6.9 KiB
JavaScript
// META: global=window,worker
|
|
// META: title=IndexedDB: Exceptions thrown during key conversion
|
|
// META: script=resources/support.js
|
|
// META: timeout=long
|
|
|
|
'use strict';
|
|
|
|
// Convenience function for tests that only need to run code in onupgradeneeded.
|
|
function indexeddb_upgrade_only_test(upgrade_callback, description) {
|
|
indexeddb_test(upgrade_callback, t => t.done(), description);
|
|
}
|
|
|
|
// Key that throws during conversion.
|
|
function throwing_key(name) {
|
|
const throws = [];
|
|
throws.length = 1;
|
|
const err = new Error('throwing from getter');
|
|
err.name = name;
|
|
Object.defineProperty(throws, '0', {
|
|
get: function() {
|
|
throw err;
|
|
},
|
|
enumerable: true,
|
|
});
|
|
return [throws, err];
|
|
}
|
|
|
|
const valid_key = [];
|
|
const invalid_key = {};
|
|
|
|
// Calls method on receiver with the specified number of args (default 1)
|
|
// and asserts that the method fails appropriately (rethrowing if
|
|
// conversion throws, or DataError if not a valid key), and that
|
|
// the first argument is fully processed before the second argument
|
|
// (if appropriate).
|
|
function check_method(receiver, method, args) {
|
|
args = args || 1;
|
|
if (args < 2) {
|
|
const [key, err] = throwing_key('getter');
|
|
assert_throws_exactly(err, () => {
|
|
receiver[method](key);
|
|
}, 'key conversion with throwing getter should rethrow');
|
|
|
|
assert_throws_dom('DataError', () => {
|
|
receiver[method](invalid_key);
|
|
}, 'key conversion with invalid key should throw DataError');
|
|
} else {
|
|
const [key1, err1] = throwing_key('getter 1');
|
|
const [key2, err2] = throwing_key('getter 2');
|
|
assert_throws_exactly(err1, () => {
|
|
receiver[method](key1, key2);
|
|
}, 'first key conversion with throwing getter should rethrow');
|
|
|
|
assert_throws_dom('DataError', () => {
|
|
receiver[method](invalid_key, key2);
|
|
}, 'first key conversion with invalid key should throw DataError');
|
|
|
|
assert_throws_exactly(err2, () => {
|
|
receiver[method](valid_key, key2);
|
|
}, 'second key conversion with throwing getter should rethrow');
|
|
|
|
assert_throws_dom('DataError', () => {
|
|
receiver[method](valid_key, invalid_key);
|
|
}, 'second key conversion with invalid key should throw DataError');
|
|
}
|
|
}
|
|
|
|
// Static key comparison utility on IDBFactory.
|
|
test(
|
|
t => check_method(indexedDB, 'cmp', 2),
|
|
'IDBFactory cmp() static with throwing/invalid keys');
|
|
|
|
// Continue methods on IDBCursor.
|
|
indexeddb_upgrade_only_test((t, db) => {
|
|
const store = db.createObjectStore('store');
|
|
store.put('a', 1).onerror = t.unreached_func('put should succeed');
|
|
|
|
const request = store.openCursor();
|
|
request.onerror = t.unreached_func('openCursor should succeed');
|
|
request.onsuccess = t.step_func(() => {
|
|
const cursor = request.result;
|
|
assert_not_equals(cursor, null, 'cursor should find a value');
|
|
check_method(cursor, 'continue');
|
|
});
|
|
}, 'IDBCursor continue() method with throwing/invalid keys');
|
|
|
|
indexeddb_upgrade_only_test((t, db) => {
|
|
const store = db.createObjectStore('store');
|
|
const index = store.createIndex('index', 'prop');
|
|
store.put({prop: 'a'}, 1).onerror = t.unreached_func('put should succeed');
|
|
|
|
const request = index.openCursor();
|
|
request.onerror = t.unreached_func('openCursor should succeed');
|
|
request.onsuccess = t.step_func(() => {
|
|
const cursor = request.result;
|
|
assert_not_equals(cursor, null, 'cursor should find a value');
|
|
|
|
check_method(cursor, 'continuePrimaryKey', 2);
|
|
});
|
|
}, null, 'IDBCursor continuePrimaryKey() method with throwing/invalid keys');
|
|
|
|
// Mutation methods on IDBCursor.
|
|
indexeddb_upgrade_only_test((t, db) => {
|
|
const store = db.createObjectStore('store', {keyPath: 'prop'});
|
|
store.put({prop: 1}).onerror = t.unreached_func('put should succeed');
|
|
|
|
const request = store.openCursor();
|
|
request.onerror = t.unreached_func('openCursor should succeed');
|
|
request.onsuccess = t.step_func(() => {
|
|
const cursor = request.result;
|
|
assert_not_equals(cursor, null, 'cursor should find a value');
|
|
|
|
const value = {};
|
|
let err;
|
|
[value.prop, err] = throwing_key('getter');
|
|
assert_throws_exactly(err, () => {
|
|
cursor.update(value);
|
|
}, 'throwing getter should rethrow during clone');
|
|
|
|
// Throwing from the getter during key conversion is
|
|
// not possible since (1) a clone is used, (2) only own
|
|
// properties are cloned, and (3) only own properties
|
|
// are used for key path evaluation.
|
|
|
|
value.prop = invalid_key;
|
|
assert_throws_dom('DataError', () => {
|
|
cursor.update(value);
|
|
}, 'key conversion with invalid key should throw DataError');
|
|
});
|
|
}, 'IDBCursor update() method with throwing/invalid keys');
|
|
|
|
// Static constructors on IDBKeyRange
|
|
['only', 'lowerBound', 'upperBound'].forEach((method) => {
|
|
test(
|
|
t => check_method(IDBKeyRange, method),
|
|
'IDBKeyRange ' + method + '() static with throwing/invalid keys');
|
|
});
|
|
|
|
test(
|
|
t => check_method(IDBKeyRange, 'bound', 2),
|
|
'IDBKeyRange bound() static with throwing/invalid keys');
|
|
|
|
// Insertion methods on IDBObjectStore.
|
|
['add', 'put'].forEach((method) => {
|
|
indexeddb_upgrade_only_test((t, db) => {
|
|
const out_of_line = db.createObjectStore('out-of-line keys');
|
|
const in_line = db.createObjectStore('in-line keys', {keyPath: 'prop'});
|
|
let [key, err] = throwing_key('getter');
|
|
assert_throws_exactly(err, () => {
|
|
out_of_line[method]('value', key);
|
|
}, 'key conversion with throwing getter should rethrow');
|
|
|
|
assert_throws_dom('DataError', () => {
|
|
out_of_line[method]('value', invalid_key);
|
|
}, 'key conversion with invalid key should throw DataError');
|
|
|
|
const value = {};
|
|
[value.prop, err] = throwing_key('getter');
|
|
assert_throws_exactly(err, () => {
|
|
in_line[method](value);
|
|
}, 'throwing getter should rethrow during clone');
|
|
|
|
// Throwing from the getter during key conversion is
|
|
// not possible since (1) a clone is used, (2) only own
|
|
// properties are cloned, and (3) only own properties
|
|
// are used for key path evaluation.
|
|
|
|
value.prop = invalid_key;
|
|
assert_throws_dom('DataError', () => {
|
|
in_line[method](value);
|
|
}, 'key conversion with invalid key should throw DataError');
|
|
}, `IDBObjectStore ${method}() method with throwing/invalid keys`);
|
|
});
|
|
|
|
// Generic (key-or-key-path) methods on IDBObjectStore.
|
|
['delete',
|
|
'get',
|
|
'getKey',
|
|
'getAll',
|
|
'getAllKeys',
|
|
'count',
|
|
'openCursor',
|
|
'openKeyCursor',
|
|
].forEach(method => {
|
|
indexeddb_upgrade_only_test((t, db) => {
|
|
const store = db.createObjectStore('store');
|
|
|
|
check_method(store, method);
|
|
}, `IDBObjectStore ${method}() method with throwing/invalid keys`);
|
|
});
|
|
|
|
// Generic (key-or-key-path) methods on IDBIndex.
|
|
['get',
|
|
'getKey',
|
|
'getAll',
|
|
'getAllKeys',
|
|
'count',
|
|
'openCursor',
|
|
'openKeyCursor',
|
|
].forEach((method) => {
|
|
indexeddb_upgrade_only_test((t, db) => {
|
|
const store = db.createObjectStore('store');
|
|
const index = store.createIndex('index', 'keyPath');
|
|
|
|
check_method(index, method);
|
|
}, `IDBIndex ${method}() method with throwing/invalid keys`);
|
|
});
|