OLD | NEW |
---|---|
(Empty) | |
1 <!DOCTYPE html> | |
2 <title>IndexedDB v2: object store renaming support</title> | |
jsbell
2016/08/26 23:20:50
nit: I wouldn't bother with the 'v2' here. (I thin
pwnall
2016/08/29 19:24:12
Done.
| |
3 <script src='../../resources/testharness.js'></script> | |
4 <link rel="help" | |
5 href="https://w3c.github.io/IndexedDB/#dom-idbobjectstore-name"> | |
6 <link rel="author" href="pwnall@chromium.org" title="Victor Costan"> | |
7 <script src='../../resources/testharnessreport.js'></script> | |
8 <script> | |
9 | |
10 // Returns an IndexedDB database name likely to be unique to the test case. | |
11 const databaseName = function(testCase) { | |
12 return 'db' + self.location.pathname + '-' + testCase.name; | |
13 }; | |
14 | |
15 // Creates an EventWatcher covering all the events that can be issued by | |
16 // IndexedDB requests and transactions. | |
17 const requestWatcher = function(testCase, request) { | |
18 return new EventWatcher(testCase, request, | |
19 ['abort', 'complete', 'error', 'success', 'upgradeneeded']); | |
jsbell
2016/08/26 23:20:50
This is only used for requests here, so I'd remove
pwnall
2016/08/29 19:24:12
Done.
| |
20 }; | |
21 | |
22 // Migrates an IndexedDB database whose name is unique for the test case. | |
23 // | |
24 // setupCallback will be called during a versionchange transaction, and will be | |
25 // given the created database and the versionchange transaction. | |
26 // | |
27 // Returns a promise that yields an IndexedDB open success event. | |
28 const migrateDatabase = function(testCase, newVersion, setupCallback) { | |
29 // We cannot use eventWatcher.wait_for('upgradeneeded') here, because | |
30 // the versionchange transaction auto-commits before the Promise's then | |
31 // callback gets called. | |
32 return new Promise((resolve, reject) => { | |
33 const request = indexedDB.open(databaseName(testCase), newVersion); | |
34 request.onupgradeneeded = (event) => { | |
jsbell
2016/08/26 23:20:50
nit: don't need () around single argument (here an
pwnall
2016/08/29 19:24:12
Done.
| |
35 const eventWatcher = requestWatcher(testCase, request); | |
36 const database = event.target.result; | |
37 const transaction = event.target.transaction; | |
38 setupCallback(database, transaction); | |
39 resolve(eventWatcher.wait_for('success')); | |
40 }; | |
41 request.onerror = | |
jsbell
2016/08/26 23:20:50
Do you need to reject() here or does that get hand
pwnall
2016/08/29 19:24:12
Done.
You were right, I needed the reject(). Witho
| |
42 testCase.unreached_func('indexedDB.open issued an error event'); | |
43 }); | |
44 }; | |
45 | |
46 // Creates an IndexedDB database whose name is unique for the test case. | |
47 // | |
48 // setupCallback will be called during a versionchange transaction, and will be | |
49 // given the created database and the versionchange transaction. | |
50 // | |
51 // Returns a promise that yields an IndexedDB open success event. | |
jsbell
2016/08/26 23:20:50
In every use of this, only the result is used not
pwnall
2016/08/29 19:24:12
Done.
| |
52 const createDatabase = function(testCase, setupCallback) { | |
53 const request = indexedDB.deleteDatabase(databaseName(testCase)); | |
54 const eventWatcher = requestWatcher(testCase, request); | |
55 | |
56 return eventWatcher.wait_for('success').then((event) => { | |
57 return migrateDatabase(testCase, 1, setupCallback); | |
jsbell
2016/08/26 23:20:50
... => { return y; }
can be simplified to:
... =
pwnall
2016/08/29 19:24:12
Done.
I did this simplification whenever possible
| |
58 }); | |
59 }; | |
60 | |
61 // Creates a 'books' object store whose contents closely resembles the first | |
62 // example in the IndexedDB specification. | |
63 const createBooksStore = function(testCase, database) { | |
64 const store = database.createObjectStore('books', | |
65 { keyPath: 'isbn', autoIncrement: true }); | |
66 const index = store.createIndex('by_author', 'author'); | |
67 store.put({title: 'Quarry Memories', author: 'Fred', isbn: 123456}); | |
68 store.put({title: 'Water Buffaloes', author: 'Fred', isbn: 234567}); | |
69 store.put({title: 'Bedrock Nights', author: 'Barney', isbn: 345678}); | |
70 return store; | |
71 }; | |
72 | |
73 // Creates a 'not_books' object store used to test renaming into existing or | |
74 // deleted store names. | |
75 const createNotBooksStore = function(testCase, database) { | |
76 const store = database.createObjectStore('not_books'); | |
77 return store; | |
78 }; | |
79 | |
80 // Renames the 'books' store into a 'not books' store. | |
81 // | |
82 // Returns a Promise that yields an IndexedDB open success event. | |
83 const renameBooksStore = function(testCase) { | |
84 return migrateDatabase(testCase, 2, (database, transaction) => { | |
85 const store = transaction.objectStore('books'); | |
86 store.name = 'renamed_books'; | |
87 }); | |
88 }; | |
89 | |
90 // Verifies that an object store's contents matches the contents used to create | |
91 // the books store in the test database's version 1. | |
92 const checkStoreContents = function(testCase, store) { | |
93 const request = store.get(123456); | |
94 const eventWatcher = requestWatcher(testCase, request); | |
95 return eventWatcher.wait_for('success').then(() => { | |
96 let result = request.result; | |
97 testCase.step(() => { | |
98 assert_equals(result.isbn, 123456); | |
99 assert_equals(result.author, 'Fred'); | |
100 assert_equals(result.title, 'Quarry Memories'); | |
101 }); | |
102 }); | |
103 }; | |
104 | |
105 // Verifies that an object store's index matches the index used to create the | |
106 // books store in the test database's version 1. | |
107 const checkStoreIndex = function(testCase, store) { | |
108 testCase.step(() => { | |
109 assert_array_equals(store.indexNames, ['by_author']); | |
110 }); | |
111 const index = store.index('by_author'); | |
112 const request = index.get('Barney'); | |
113 const eventWatcher = requestWatcher(testCase, request); | |
114 return eventWatcher.wait_for('success').then(() => { | |
115 let result = request.result; | |
116 testCase.step(() => { | |
117 assert_equals(result.isbn, 345678); | |
118 assert_equals(result.title, 'Bedrock Nights'); | |
119 }); | |
120 }); | |
121 }; | |
122 | |
123 // Verifies that an object store's key generator is in the same state as the | |
124 // key generator created for the books store in the test database's version 1. | |
125 const checkStoreGenerator = function(testCase, store, expectedKey) { | |
126 const request = store.put({title: 'Bedrock Nights II', author: 'Barney'}); | |
127 const eventWatcher = requestWatcher(testCase, request); | |
128 return eventWatcher.wait_for('success').then(() => { | |
129 let result = request.result; | |
130 testCase.step(() => { | |
131 assert_equals(result, expectedKey); | |
132 }); | |
133 }); | |
134 }; | |
135 | |
136 promise_test(testCase => { | |
137 let bookStore = null, bookStore2 = null; | |
138 let renamedBookStore = null, renamedBookStore2 = null; | |
139 return createDatabase(testCase, (database, transaction) => { | |
140 bookStore = createBooksStore(testCase, database); | |
141 }).then((event) => { | |
142 const database = event.target.result; | |
143 testCase.step(() => { | |
144 assert_array_equals(database.objectStoreNames, ['books']); | |
145 }); | |
146 const transaction = database.transaction('books', 'readonly'); | |
147 bookStore2 = transaction.objectStore('books'); | |
148 // If checkStoreContents fails here, its implementation is incorrect. | |
149 return checkStoreContents(testCase, bookStore2).then(() => { | |
150 database.close(); | |
151 }); | |
152 }).then(() => { | |
153 return migrateDatabase(testCase, 2, (database, transaction) => { | |
154 renamedBookStore = transaction.objectStore('books'); | |
155 renamedBookStore.name = 'renamed_books'; | |
156 testCase.step(() => { | |
157 assert_equals(renamedBookStore.name, 'renamed_books'); | |
158 }); | |
159 }); | |
160 }).then((event) => { | |
161 const database = event.target.result; | |
162 testCase.step(() => { | |
163 assert_array_equals(database.objectStoreNames, ['renamed_books']); | |
164 }); | |
165 const transaction = database.transaction('renamed_books', 'readonly'); | |
166 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
167 return checkStoreContents(testCase, renamedBookStore2).then(() => { | |
168 database.close(); | |
169 }); | |
170 }).then(() => { | |
171 testCase.step(() => { | |
172 assert_equals(bookStore.name, 'books'); | |
173 assert_equals(bookStore2.name, 'books'); | |
174 assert_equals(renamedBookStore.name, 'renamed_books'); | |
175 assert_equals(renamedBookStore2.name, 'renamed_books'); | |
176 }); | |
177 }); | |
178 }, 'IndexedDB object store rename in new transaction', { | |
179 assert: 'An object store can be renamed in a new version change transaction' , | |
jsbell
2016/08/26 23:20:50
Does this assert: test property add much? They're
pwnall
2016/08/29 19:24:12
I removed all of them. I was following the guide b
| |
180 }); | |
181 | |
182 promise_test(testCase => { | |
183 let renamedBookStore = null, renamedBookStore2 = null; | |
184 return createDatabase(testCase, (database, transaction) => { | |
185 renamedBookStore = createBooksStore(testCase, database); | |
186 renamedBookStore.name = 'renamed_books'; | |
187 testCase.step(() => { | |
188 assert_equals(renamedBookStore.name, 'renamed_books'); | |
189 }); | |
190 }).then((event) => { | |
191 const database = event.target.result; | |
192 testCase.step(() => { | |
193 assert_array_equals(database.objectStoreNames, ['renamed_books']); | |
194 }); | |
195 const transaction = database.transaction('renamed_books', 'readonly'); | |
196 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
197 return checkStoreContents(testCase, renamedBookStore2).then(() => { | |
198 database.close(); | |
199 }); | |
200 }).then(() => { | |
201 testCase.step(() => { | |
202 assert_equals(renamedBookStore.name, 'renamed_books'); | |
203 assert_equals(renamedBookStore2.name, 'renamed_books'); | |
204 }); | |
205 }); | |
206 }, 'IndexedDB object store rename in the transaction where it is created', { | |
207 assert: | |
208 'An object store can be renamed in the transaction where it is created', | |
209 }); | |
210 | |
211 promise_test(testCase => { | |
212 return createDatabase(testCase, (database, transaction) => { | |
213 createBooksStore(testCase, database); | |
214 }).then((event) => { | |
215 const database = event.target.result; | |
216 const transaction = database.transaction('books', 'readonly'); | |
217 const store = transaction.objectStore('books'); | |
218 // If checkStoreIndex fails here, its implementation is incorrect. | |
219 return checkStoreIndex(testCase, store).then(() => { | |
220 database.close(); | |
221 }); | |
222 }).then(() => { | |
223 return renameBooksStore(testCase); | |
224 }).then((event) => { | |
225 const database = event.target.result; | |
226 const transaction = database.transaction('renamed_books', 'readonly'); | |
227 const store = transaction.objectStore('renamed_books'); | |
228 return checkStoreIndex(testCase, store).then(() => { | |
229 database.close(); | |
230 }); | |
231 }); | |
232 }, 'IndexedDB object store rename covers index', { | |
233 assert: 'Renaming an object store maintains its indexes', | |
234 }); | |
235 | |
236 promise_test(testCase => { | |
237 return createDatabase(testCase, (database, transaction) => { | |
238 createBooksStore(testCase, database); | |
239 }).then((event) => { | |
240 const database = event.target.result; | |
241 const transaction = database.transaction('books', 'readwrite'); | |
242 // If checkStoreGenerator fails here, its implementation is incorrect. | |
243 const store = transaction.objectStore('books'); | |
244 return checkStoreGenerator(testCase, store, 345679).then(() => { | |
245 database.close(); | |
246 }); | |
247 }).then(() => { | |
248 return renameBooksStore(testCase); | |
249 }).then((event) => { | |
250 const database = event.target.result; | |
251 const transaction = database.transaction('renamed_books', 'readwrite'); | |
252 const store = transaction.objectStore('renamed_books'); | |
253 return checkStoreGenerator(testCase, store, 345680).then(() => { | |
254 database.close(); | |
255 }); | |
256 }); | |
257 }, 'IndexedDB object store rename covers key generator', { | |
258 assert: 'Renaming an object store maintains its key generator', | |
259 }); | |
260 | |
261 promise_test(testCase => { | |
262 const dbName = databaseName(testCase); | |
263 let bookStore = null, bookStore2 = null, bookStore3 = null; | |
264 return createDatabase(testCase, (database, transaction) => { | |
265 bookStore = createBooksStore(testCase, database); | |
266 }).then((event) => { | |
267 const database = event.target.result; | |
268 database.close(); | |
269 }).then(() => { | |
270 return new Promise((resolve, reject) => { | |
271 const request = indexedDB.open(dbName, 2); | |
272 request.onupgradeneeded = (event) => { | |
273 const database = event.target.result; | |
274 const transaction = event.target.transaction; | |
275 bookStore2 = transaction.objectStore('books'); | |
276 bookStore2.name = 'renamed_books'; | |
277 testCase.step(() => { | |
278 assert_equals(bookStore.name, 'books'); | |
279 assert_equals(bookStore2.name, 'renamed_books'); | |
280 }); | |
281 request.onerror = (event) => { | |
282 event.preventDefault(); | |
283 resolve(event); | |
284 } | |
285 transaction.onabort = () => null; | |
286 transaction.onerror = () => null; | |
287 transaction.abort(); | |
288 }; | |
289 request.onerror = | |
290 testCase.unreached_func('indexedDB.open issued an error event'); | |
291 request.onsuccess = testCase.unreached_func( | |
292 'indexedDB.open was not supposed to succeed'); | |
293 }); | |
294 }).then((event) => { | |
295 testCase.step(() => { | |
296 assert_equals(bookStore.name, 'books', | |
297 'object store rename not reverted after transaction abort'); | |
298 }); | |
299 | |
300 const request = indexedDB.open(dbName, 1); | |
301 const eventWatcher = requestWatcher(testCase, request); | |
302 return eventWatcher.wait_for('success'); | |
303 }).then((event) => { | |
304 const database = event.target.result; | |
305 const transaction = database.transaction('books', 'readonly'); | |
306 bookStore3 = transaction.objectStore('books'); | |
307 return checkStoreContents(testCase, bookStore3).then(() => { | |
308 database.close(); | |
309 }); | |
310 }).then(() => { | |
311 testCase.step(() => { | |
312 assert_equals(bookStore.name, 'books'); | |
313 assert_equals(bookStore2.name, 'books'); | |
314 assert_equals(bookStore3.name, 'books'); | |
315 }); | |
316 }); | |
317 }, 'IndexedDB object store rename in aborted transaction', { | |
318 assert: 'An object store rename is correctly reverted', | |
319 }); | |
320 | |
321 promise_test(testCase => { | |
322 return createDatabase(testCase, (database, transaction) => { | |
323 createBooksStore(testCase, database); | |
324 }).then((event) => { | |
325 const database = event.target.result; | |
326 database.close(); | |
327 }).then(() => { | |
328 return migrateDatabase(testCase, 2, (database, transaction) => { | |
329 const store = transaction.objectStore('books'); | |
330 database.deleteObjectStore('books'); | |
331 testCase.step(() => { | |
332 assert_throws('InvalidStateError', | |
333 () => { store.name = 'renamed_books'; }); | |
334 }); | |
335 }); | |
336 }).then((event) => { | |
337 const database = event.target.result; | |
338 database.close(); | |
339 }); | |
340 }, 'IndexedDB object store rename covers key generator', { | |
341 assert: 'Renaming an object store maintains its key generator', | |
342 }); | |
343 | |
344 promise_test(testCase => { | |
345 return createDatabase(testCase, (database, transaction) => { | |
346 createBooksStore(testCase, database); | |
347 }).then((event) => { | |
348 const database = event.target.result; | |
349 const transaction = database.transaction('books', 'readonly'); | |
350 const store = transaction.objectStore('books'); | |
351 | |
352 testCase.step(() => { | |
353 assert_throws('InvalidStateError', | |
354 () => { store.name = 'renamed_books'; }); | |
355 }); | |
356 database.close(); | |
357 }); | |
358 }, 'IndexedDB deleted object store rename throws', { | |
359 assert: 'Renaming a deleted object store throws InvalidStateError', | |
360 }); | |
361 | |
362 promise_test(testCase => { | |
363 return createDatabase(testCase, (database, transaction) => { | |
364 createBooksStore(testCase, database); | |
365 }).then((event) => { | |
366 const database = event.target.result; | |
367 const transaction = database.transaction('books', 'readonly'); | |
368 const store = transaction.objectStore('books'); | |
369 | |
370 testCase.step(() => { | |
371 assert_throws('InvalidStateError', | |
372 () => { store.name = 'renamed_books'; }); | |
373 }); | |
374 database.close(); | |
375 }); | |
376 }, 'IndexedDB object store rename throws in a readwrite transaction', { | |
377 assert: 'Renaming an object store in a readwrite transaction throws InvalidS tateError', | |
378 }); | |
379 | |
380 promise_test(testCase => { | |
381 let bookStore = null; | |
382 return createDatabase(testCase, (database, transaction) => { | |
383 bookStore = createBooksStore(testCase, database); | |
384 }).then((event) => { | |
385 testCase.step(() => { | |
386 assert_throws('TransactionInactiveError', | |
387 () => { bookStore.name = 'renamed_books'; }); | |
388 }); | |
389 const database = event.target.result; | |
390 database.close(); | |
391 }); | |
392 }, 'IndexedDB object store rename throws in an inactive transaction', { | |
393 assert: 'Renaming an object store in an inactive transaction throws Transact ionInactiveError', | |
394 }); | |
395 | |
396 promise_test(testCase => { | |
397 return createDatabase(testCase, (database, transaction) => { | |
398 createBooksStore(testCase, database); | |
399 }).then((event) => { | |
400 const database = event.target.result; | |
401 database.close(); | |
402 }).then(() => { | |
403 return migrateDatabase(testCase, 2, (database, transaction) => { | |
404 const store = transaction.objectStore('books'); | |
405 store.name = 'books'; | |
406 }); | |
407 }).then((event) => { | |
408 const database = event.target.result; | |
409 testCase.step(() => { | |
410 assert_array_equals(database.objectStoreNames, ['books']); | |
411 }); | |
412 const transaction = database.transaction('books', 'readonly'); | |
413 const store = transaction.objectStore('books'); | |
414 return checkStoreContents(testCase, store).then(() => { | |
415 database.close(); | |
416 }); | |
417 }); | |
418 }, 'IndexedDB object store rename to the same name succeeds', { | |
419 assert: 'Renaming an object store to the same name does not throw', | |
420 }); | |
421 | |
422 promise_test(testCase => { | |
423 return createDatabase(testCase, (database, transaction) => { | |
424 createBooksStore(testCase, database); | |
425 createNotBooksStore(testCase, database); | |
426 }).then((event) => { | |
427 const database = event.target.result; | |
428 database.close(); | |
429 }).then(() => { | |
430 return migrateDatabase(testCase, 2, (database, transaction) => { | |
431 const store = transaction.objectStore('books'); | |
432 | |
433 testCase.step(() => { | |
434 assert_throws('ConstraintError', | |
435 () => { store.name = 'not_books'; }); | |
436 }); | |
437 }); | |
438 }).then((event) => { | |
439 const database = event.target.result; | |
440 testCase.step(() => { | |
441 assert_array_equals(database.objectStoreNames, | |
442 ['books', 'not_books']); | |
443 }); | |
444 const transaction = database.transaction('books', 'readonly'); | |
445 const store = transaction.objectStore('books'); | |
446 return checkStoreContents(testCase, store).then(() => { | |
447 database.close(); | |
448 }); | |
449 }); | |
450 }, 'IndexedDB object store rename to the name of another store throws', { | |
451 assert: 'Renaming an object store to the name of another store throws Constr aintError', | |
452 }); | |
453 | |
454 promise_test(testCase => { | |
455 return createDatabase(testCase, (database, transaction) => { | |
456 createBooksStore(testCase, database); | |
457 createNotBooksStore(testCase, database); | |
458 }).then((event) => { | |
459 const database = event.target.result; | |
460 database.close(); | |
461 }).then(() => { | |
462 return migrateDatabase(testCase, 2, (database, transaction) => { | |
463 const store = transaction.objectStore('books'); | |
464 database.deleteObjectStore('not_books'); | |
465 store.name = 'not_books'; | |
466 }); | |
467 }).then((event) => { | |
468 const database = event.target.result; | |
469 testCase.step(() => { | |
470 assert_array_equals(database.objectStoreNames, ['not_books']); | |
471 }); | |
472 const transaction = database.transaction('not_books', 'readonly'); | |
473 const store = transaction.objectStore('not_books'); | |
474 return checkStoreContents(testCase, store).then(() => { | |
475 database.close(); | |
476 }); | |
477 }); | |
478 }, 'IndexedDB object store rename to the name of a deleted store succeeds', { | |
479 assert: 'Renaming an object store to the name of a deleted store does not th row', | |
480 }); | |
481 | |
482 promise_test(testCase => { | |
483 return createDatabase(testCase, (database, transaction) => { | |
484 createBooksStore(testCase, database); | |
485 createNotBooksStore(testCase, database); | |
486 }).then((event) => { | |
487 const database = event.target.result; | |
488 database.close(); | |
489 }).then(() => { | |
490 return migrateDatabase(testCase, 2, (database, transaction) => { | |
491 const store = transaction.objectStore('books'); | |
492 | |
493 testCase.step(() => { | |
494 store.name = 42; | |
495 assert_equals(store.name, "42"); | |
496 store.name = true; | |
497 assert_equals(store.name, "true"); | |
498 store.name = () => null; | |
499 assert_equals(store.name, "() => null"); | |
500 store.name = undefined; | |
501 assert_equals(store.name, "undefined"); | |
502 }); | |
503 }); | |
504 }).then((event) => { | |
505 const database = event.target.result; | |
506 testCase.step(() => { | |
507 assert_array_equals(database.objectStoreNames, | |
508 ['not_books', 'undefined']); | |
509 }); | |
510 const transaction = database.transaction('undefined', 'readonly'); | |
511 const store = transaction.objectStore('undefined'); | |
512 return checkStoreContents(testCase, store).then(() => { | |
513 database.close(); | |
514 }); | |
515 }); | |
516 }, 'IndexedDB object store rename to non-string name XXX', { | |
517 assert: 'Renaming an object store to the name of another store throws Constr aintError', | |
518 }); | |
519 </script> | |
OLD | NEW |