-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
IndexedDB: Add tests for HTML monkey patches
The IDB spec implicitly "monkey patches" HTML with imprecise prose when describing newly created transactions: "When control is returned to the event loop, the implementation must unset the active flag." The plan is replace that with a proper hook. Adding tests first to ensure the expected behavior is captured appropriately. w3c/IndexedDB#87
- Loading branch information
1 parent
19564bf
commit 57aa2ac
Showing
4 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
<!DOCTYPE html> | ||
<meta charset=utf-8> | ||
<title>Transaction active flag is set during event dispatch</title> | ||
<link rel="help" href="https://w3c.github.io/IndexedDB/#fire-success-event"> | ||
<link rel="help" href="https://w3c.github.io/IndexedDB/#fire-error-event"> | ||
<script src=/resources/testharness.js></script> | ||
<script src=/resources/testharnessreport.js></script> | ||
<script src=support.js></script> | ||
<script> | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
const tx = db.transaction('store'); | ||
const release_tx = keep_alive(tx, 'store'); | ||
|
||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
|
||
const request = tx.objectStore('store').get(0); | ||
request.onerror = t.unreached_func('request should succeed'); | ||
request.onsuccess = () => { | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active during success handler'); | ||
|
||
let saw_handler_promise = false; | ||
Promise.resolve().then(t.step_func(() => { | ||
saw_handler_promise = true; | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in handler\'s microtasks'); | ||
})); | ||
|
||
setTimeout(t.step_func(() => { | ||
assert_true(saw_handler_promise); | ||
assert_false(is_transaction_active(tx, 'store'), | ||
'Transaction should be inactive in next task'); | ||
release_tx(); | ||
t.done(); | ||
}), 0); | ||
}; | ||
}, | ||
'Transactions are active during success handlers'); | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
const tx = db.transaction('store'); | ||
const release_tx = keep_alive(tx, 'store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
|
||
const request = tx.objectStore('store').get(0); | ||
request.onerror = t.unreached_func('request should succeed'); | ||
request.addEventListener('success', () => { | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active during success listener'); | ||
|
||
let saw_listener_promise = false; | ||
Promise.resolve().then(t.step_func(() => { | ||
saw_listener_promise = true; | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in listener\'s microtasks'); | ||
})); | ||
|
||
setTimeout(t.step_func(() => { | ||
assert_true(saw_listener_promise); | ||
assert_false(is_transaction_active(tx, 'store'), | ||
'Transaction should be inactive in next task'); | ||
release_tx(); | ||
t.done(); | ||
}), 0); | ||
}); | ||
}, | ||
'Transactions are active during success listeners'); | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
const tx = db.transaction('store', 'readwrite'); | ||
const release_tx = keep_alive(tx, 'store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
|
||
tx.objectStore('store').put(0, 0); | ||
const request = tx.objectStore('store').add(0, 0); | ||
request.onsuccess = t.unreached_func('request should fail'); | ||
request.onerror = e => { | ||
e.preventDefault(); | ||
|
||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active during error handler'); | ||
|
||
let saw_handler_promise = false; | ||
Promise.resolve().then(t.step_func(() => { | ||
saw_handler_promise = true; | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in handler\'s microtasks'); | ||
})); | ||
|
||
setTimeout(t.step_func(() => { | ||
assert_true(saw_handler_promise); | ||
assert_false(is_transaction_active(tx, 'store'), | ||
'Transaction should be inactive in next task'); | ||
release_tx(); | ||
t.done(); | ||
}), 0); | ||
}; | ||
}, | ||
'Transactions are active during error handlers'); | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
const tx = db.transaction('store', 'readwrite'); | ||
const release_tx = keep_alive(tx, 'store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
|
||
tx.objectStore('store').put(0, 0); | ||
const request = tx.objectStore('store').add(0, 0); | ||
request.onsuccess = t.unreached_func('request should fail'); | ||
request.addEventListener('error', e => { | ||
e.preventDefault(); | ||
|
||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active during error listener'); | ||
|
||
let saw_listener_promise = false; | ||
Promise.resolve().then(t.step_func(() => { | ||
saw_listener_promise = true; | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in listener\'s microtasks'); | ||
})); | ||
|
||
setTimeout(t.step_func(() => { | ||
assert_true(saw_listener_promise); | ||
assert_false(is_transaction_active(tx, 'store'), | ||
'Transaction should be inactive in next task'); | ||
release_tx(); | ||
t.done(); | ||
}), 0); | ||
}); | ||
}, | ||
'Transactions are active during error listeners'); | ||
|
||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<!DOCTYPE html> | ||
<meta charset=utf-8> | ||
<title>Transactions deactivation timing</title> | ||
<link rel="help" href="https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction"> | ||
<script src=/resources/testharness.js></script> | ||
<script src=/resources/testharnessreport.js></script> | ||
<script src=support.js></script> | ||
<script> | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
const tx = db.transaction('store'); | ||
const release_tx = keep_alive(tx, 'store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
|
||
setTimeout(t.step_func(() => { | ||
assert_false(is_transaction_active(tx, 'store'), | ||
'Transaction should be inactive in next task'); | ||
release_tx(); | ||
t.done(); | ||
}), 0); | ||
}, | ||
'New transactions are deactivated before next task'); | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
const tx = db.transaction('store'); | ||
const release_tx = keep_alive(tx, 'store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
|
||
Promise.resolve().then(t.step_func(() => { | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in microtask checkpoint'); | ||
release_tx(); | ||
t.done(); | ||
})); | ||
}, | ||
'New transactions are not deactivated until after the microtask checkpoint'); | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
let tx, release_tx; | ||
|
||
Promise.resolve().then(t.step_func(() => { | ||
tx = db.transaction('store'); | ||
release_tx = keep_alive(tx, 'store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
})); | ||
|
||
setTimeout(t.step_func(() => { | ||
assert_false(is_transaction_active(tx, 'store'), | ||
'Transaction should be inactive in next task'); | ||
release_tx(); | ||
t.done(); | ||
}), 0); | ||
}, | ||
'New transactions from microtask are deactivated before next task'); | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
let tx, release_tx; | ||
|
||
Promise.resolve().then(t.step_func(() => { | ||
tx = db.transaction('store'); | ||
release_tx = keep_alive(tx, 'store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
})); | ||
|
||
Promise.resolve().then(t.step_func(() => { | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in microtask checkpoint'); | ||
release_tx(); | ||
t.done(); | ||
})); | ||
}, | ||
'New transactions from microtask are still active through the ' + | ||
'microtask checkpoint'); | ||
|
||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
}, | ||
(t, db) => { | ||
// This transaction serves as the source of an event seen by multiple | ||
// listeners. A DOM event with multiple listeners could be used instead, | ||
// but not via dispatchEvent() because (drumroll...) that happens | ||
// synchronously so microtasks don't run between steps. | ||
const tx = db.transaction('store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active after creation'); | ||
|
||
const request = tx.objectStore('store').get(0); | ||
let new_tx; | ||
let first_listener_ran = false; | ||
let microtasks_ran = false; | ||
request.addEventListener('success', t.step_func(() => { | ||
first_listener_ran = true; | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in callback'); | ||
|
||
// We check to see if this transaction is active across unrelated event | ||
// dispatch steps. | ||
new_tx = db.transaction('store'); | ||
assert_true(is_transaction_active(new_tx, 'store'), | ||
'New transaction should be active after creation'); | ||
|
||
Promise.resolve().then(t.step_func(() => { | ||
microtasks_ran = true; | ||
assert_true(is_transaction_active(new_tx, 'store'), | ||
'New transaction is still active in microtask checkpoint'); | ||
})); | ||
|
||
})); | ||
request.addEventListener('success', t.step_func(() => { | ||
assert_true(first_listener_ran, 'first listener ran first'); | ||
assert_true(microtasks_ran, 'microtasks ran before second listener'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in callback'); | ||
assert_false(is_transaction_active(new_tx, 'store'), | ||
'New transaction should be inactive in unrelated callback'); | ||
t.done(); | ||
})); | ||
}, | ||
'Deactivation of new transactions happens at end of invocation'); | ||
|
||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<!DOCTYPE html> | ||
<meta charset=utf-8> | ||
<title>Upgrade transaction deactivation timing</title> | ||
<link rel="help" href="http://localhost:4201/#upgrade-transaction-steps"> | ||
<script src=/resources/testharness.js></script> | ||
<script src=/resources/testharnessreport.js></script> | ||
<script src=support.js></script> | ||
<script> | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in upgradeneeded callback'); | ||
}, | ||
(t, db) => { t.done(); }, | ||
'Upgrade transactions are active in upgradeneeded callback'); | ||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in upgradeneeded callback'); | ||
|
||
Promise.resolve().then(t.step_func(() => { | ||
assert_true(is_transaction_active(tx, 'store'), | ||
'Transaction should be active in microtask checkpoint'); | ||
})); | ||
}, | ||
(t, db) => { t.done(); }, | ||
'Upgrade transactions are active in upgradeneeded callback and microtasks'); | ||
|
||
|
||
indexeddb_test( | ||
(t, db, tx) => { | ||
db.createObjectStore('store'); | ||
const release_tx = keep_alive(tx, 'store'); | ||
|
||
setTimeout(t.step_func(() => { | ||
assert_false(is_transaction_active(tx, 'store'), | ||
'Transaction should be inactive in next task'); | ||
release_tx(); | ||
}), 0); | ||
}, | ||
(t, db) => { t.done(); }, | ||
'Upgrade transactions are deactivated before next task'); | ||
|
||
</script> |