OLD | NEW |
---|---|
(Empty) | |
1 <!DOCTYPE html> | |
2 <title>IndexedDB: object store renaming support</title> | |
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 ['error', 'success', 'upgradeneeded']); | |
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 resolves to an IndexedDB database. The caller must | |
28 // close the database. | |
29 const migrateDatabase = function(testCase, newVersion, setupCallback) { | |
30 // We cannot use eventWatcher.wait_for('upgradeneeded') here, because | |
31 // the versionchange transaction auto-commits before the Promise's then | |
32 // callback gets called. | |
33 return new Promise((resolve, reject) => { | |
34 const request = indexedDB.open(databaseName(testCase), newVersion); | |
35 request.onupgradeneeded = event => { | |
36 const eventWatcher = requestWatcher(testCase, request); | |
37 const database = event.target.result; | |
38 const transaction = event.target.transaction; | |
39 setupCallback(database, transaction); | |
40 resolve(eventWatcher.wait_for('success')); | |
41 }; | |
42 request.onerror = event => reject(event.target.error); | |
43 }).then(event => event.target.result); | |
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 resolves to an IndexedDB database. The caller must | |
52 // close the database. | |
53 const createDatabase = function(testCase, setupCallback) { | |
54 const request = indexedDB.deleteDatabase(databaseName(testCase)); | |
55 const eventWatcher = requestWatcher(testCase, request); | |
56 | |
57 return eventWatcher.wait_for('success').then(event => | |
58 migrateDatabase(testCase, 1, setupCallback)); | |
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 store.createIndex('by_author', 'author'); | |
67 store.createIndex('by_title', 'title', { unique: true }); | |
jsbell
2016/09/02 22:57:37
nit: extra space
pwnall
2016/09/03 06:01:19
Done.
| |
68 store.put({ title: 'Quarry Memories', author: 'Fred', isbn: 123456 }); | |
69 store.put({ title: 'Water Buffaloes', author: 'Fred', isbn: 234567 }); | |
70 store.put({ title: 'Bedrock Nights', author: 'Barney', isbn: 345678 }); | |
71 return store; | |
72 }; | |
73 | |
74 // Creates a 'not_books' object store used to test renaming into existing or | |
75 // deleted store names. | |
76 const createNotBooksStore = function(testCase, database) { | |
77 return database.createObjectStore('not_books'); | |
78 }; | |
79 | |
80 // Renames the 'books' store to 'renamed_books'. | |
81 // | |
82 // Returns a promise that resolves to an IndexedDB database. The caller must | |
83 // close the database. | |
84 const renameBooksStore = function(testCase) { | |
85 return migrateDatabase(testCase, 2, (database, transaction) => { | |
jsbell
2016/09/02 22:57:37
nit: extra space
pwnall
2016/09/03 06:01:20
Done.
| |
86 const store = transaction.objectStore('books'); | |
87 store.name = 'renamed_books'; | |
88 }); | |
89 }; | |
90 | |
91 // Verifies that an object store's contents matches the contents used to create | |
92 // the books store in the test database's version 1. | |
93 // | |
94 // The errorMessage is used if the assertions fail. It can state that the | |
95 // IndexedDB implementation being tested is incorrect, or that the testing code | |
96 // is using it incorrectly. | |
97 const checkStoreContents = function(testCase, store, errorMessage) { | |
98 const request = store.get(123456); | |
99 const eventWatcher = requestWatcher(testCase, request); | |
100 return eventWatcher.wait_for('success').then(() => { | |
101 const result = request.result; | |
102 assert_equals(result.isbn, 123456, errorMessage); | |
103 assert_equals(result.author, 'Fred', errorMessage); | |
104 assert_equals(result.title, 'Quarry Memories', errorMessage); | |
105 }); | |
106 }; | |
107 | |
108 // Verifies that an object store's index matches the index used to create the | |
109 // books store in the test database's version 1. | |
110 // | |
111 // The errorMessage is used if the assertions fail. It can state that the | |
112 // IndexedDB implementation being tested is incorrect, or that the testing code | |
113 // is using it incorrectly. | |
114 const checkStoreIndex = function(testCase, store, errorMessage) { | |
115 assert_array_equals( | |
116 store.indexNames, ['by_author', 'by_title'], errorMessage); | |
117 const index = store.index('by_author'); | |
118 const request = index.get('Barney'); | |
119 const eventWatcher = requestWatcher(testCase, request); | |
120 return eventWatcher.wait_for('success').then(() => { | |
121 const result = request.result; | |
122 assert_equals(result.isbn, 345678, errorMessage); | |
123 assert_equals(result.title, 'Bedrock Nights', errorMessage); | |
124 }); | |
125 }; | |
126 | |
127 // Verifies that an object store's key generator is in the same state as the | |
128 // key generator created for the books store in the test database's version 1. | |
129 // | |
130 // The errorMessage is used if the assertions fail. It can state that the | |
131 // IndexedDB implementation being tested is incorrect, or that the testing code | |
132 // is using it incorrectly. | |
133 const checkStoreGenerator = function(testCase, | |
134 store, | |
135 expectedKey, | |
136 errorMessage) { | |
137 const request = store.put( | |
138 { title: 'Bedrock Nights ' + expectedKey, author: 'Barney' }); | |
139 const eventWatcher = requestWatcher(testCase, request); | |
140 return eventWatcher.wait_for('success').then(() => { | |
141 const result = request.result; | |
142 assert_equals(result, expectedKey, errorMessage); | |
143 }); | |
144 }; | |
145 | |
146 promise_test(testCase => { | |
147 let bookStore = null, bookStore2 = null; | |
148 let renamedBookStore = null, renamedBookStore2 = null; | |
149 return createDatabase(testCase, (database, transaction) => { | |
150 bookStore = createBooksStore(testCase, database); | |
151 }).then(database => { | |
152 assert_array_equals( | |
153 database.objectStoreNames, ['books'], | |
154 'Test setup should have created a "books" object store'); | |
155 const transaction = database.transaction('books', 'readonly'); | |
156 bookStore2 = transaction.objectStore('books'); | |
157 return checkStoreContents( | |
158 testCase, bookStore2, | |
159 'The store content checks should pass before any renaming').then( | |
160 () => database.close()); | |
161 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
162 renamedBookStore = transaction.objectStore('books'); | |
163 renamedBookStore.name = 'renamed_books'; | |
164 | |
165 assert_equals( | |
166 renamedBookStore.name, 'renamed_books', | |
167 'IDBObjectStore name should change immediately after a rename'); | |
168 assert_array_equals( | |
169 database.objectStoreNames, ['renamed_books'], | |
170 'IDBDatabase.objectStoreNames should immediately reflect the ' + | |
171 'rename'); | |
172 assert_equals( | |
173 transaction.objectStore('renamed_books'), renamedBookStore, | |
174 'IDBTransaction.objectStore should return the renamed object ' + | |
175 'store when queried using the new name immediately after the ' + | |
176 'rename'); | |
177 assert_throws( | |
178 'NotFoundError', () => transaction.objectStore('books'), | |
179 'IDBTransaction.objectStore should throw when queried using the ' + | |
180 "renamed object store's old name immediately after the rename"); | |
181 })).then(database => { | |
182 assert_array_equals( | |
183 database.objectStoreNames, ['renamed_books'], | |
184 'IDBDatabase.objectStoreNames should still reflect the rename ' + | |
185 'after the versionchange transaction commits'); | |
186 const transaction = database.transaction('renamed_books', 'readonly'); | |
187 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
188 return checkStoreContents( | |
189 testCase, renamedBookStore2, | |
190 'Renaming an object store should not change its records').then( | |
191 () => database.close()); | |
192 }).then(() => { | |
193 assert_equals( | |
194 bookStore.name, 'books', | |
195 'IDBObjectStore obtained before the rename transaction should ' + | |
196 'not reflect the rename'); | |
197 assert_equals( | |
198 bookStore2.name, 'books', | |
199 'IDBObjectStore obtained before the rename transaction should ' + | |
200 'not reflect the rename'); | |
201 assert_equals( | |
202 renamedBookStore.name, 'renamed_books', | |
203 'IDBObjectStore used in the rename transaction should keep ' + | |
204 'reflecting the new name after the transaction is committed'); | |
205 assert_equals( | |
206 renamedBookStore2.name, 'renamed_books', | |
207 'IDBObjectStore obtained after the rename transaction should ' + | |
208 'reflect the new name'); | |
209 }); | |
210 }, 'IndexedDB object store rename in new transaction'); | |
211 | |
212 promise_test(testCase => { | |
213 let renamedBookStore = null, renamedBookStore2 = null; | |
214 return createDatabase(testCase, (database, transaction) => { | |
215 renamedBookStore = createBooksStore(testCase, database); | |
216 renamedBookStore.name = 'renamed_books'; | |
217 | |
218 assert_equals( | |
219 renamedBookStore.name, 'renamed_books', | |
220 'IDBObjectStore name should change immediately after a rename'); | |
221 assert_array_equals( | |
222 database.objectStoreNames, ['renamed_books'], | |
223 'IDBDatabase.objectStoreNames should immediately reflect the ' + | |
224 'rename'); | |
225 assert_equals( | |
226 transaction.objectStore('renamed_books'), renamedBookStore, | |
227 'IDBTransaction.objectStore should return the renamed object ' + | |
228 'store when queried using the new name immediately after the ' + | |
229 'rename'); | |
230 assert_throws( | |
231 'NotFoundError', () => transaction.objectStore('books'), | |
232 'IDBTransaction.objectStore should throw when queried using the ' + | |
233 "renamed object store's old name immediately after the rename"); | |
234 }).then(database => { | |
235 assert_array_equals( | |
236 database.objectStoreNames, ['renamed_books'], | |
237 'IDBDatabase.objectStoreNames should still reflect the rename ' + | |
238 'after the versionchange transaction commits'); | |
239 const transaction = database.transaction('renamed_books', 'readonly'); | |
240 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
241 return checkStoreContents( | |
242 testCase, renamedBookStore2, | |
243 'Renaming an object store should not change its records').then( | |
244 () => database.close()); | |
245 }).then(() => { | |
246 assert_equals( | |
247 renamedBookStore.name, 'renamed_books', | |
248 'IDBObjectStore used in the rename transaction should keep ' + | |
249 'reflecting the new name after the transaction is committed'); | |
250 assert_equals( | |
251 renamedBookStore2.name, 'renamed_books', | |
252 'IDBObjectStore obtained after the rename transaction should ' + | |
253 'reflect the new name'); | |
254 }); | |
255 }, 'IndexedDB object store rename in the transaction where it is created'); | |
256 | |
257 promise_test(testCase => { | |
258 return createDatabase(testCase, (database, transaction) => { | |
259 createBooksStore(testCase, database); | |
260 }).then(database => { | |
261 const transaction = database.transaction('books', 'readonly'); | |
262 const store = transaction.objectStore('books'); | |
263 return checkStoreIndex( | |
264 testCase, store, | |
265 'The object store index should pass before any renaming').then( | |
266 () => database.close()); | |
267 }).then(() => renameBooksStore(testCase) | |
268 ).then(database => { | |
269 const transaction = database.transaction('renamed_books', 'readonly'); | |
270 const store = transaction.objectStore('renamed_books'); | |
271 return checkStoreIndex( | |
272 testCase, store, | |
273 'Renaming an object store should not change its indexes').then( | |
274 () => database.close()); | |
275 }); | |
276 }, 'IndexedDB object store rename covers index'); | |
277 | |
278 promise_test(testCase => { | |
279 return createDatabase(testCase, (database, transaction) => { | |
280 createBooksStore(testCase, database); | |
281 }).then(database => { | |
282 const transaction = database.transaction('books', 'readwrite'); | |
283 const store = transaction.objectStore('books'); | |
284 return checkStoreGenerator( | |
285 testCase, store, 345679, | |
286 'The object store key generator state test should pass before ' + | |
287 'any renaming').then(() => database.close()); | |
jsbell
2016/09/02 22:57:37
nit: extra space
pwnall
2016/09/03 06:01:20
Done.
| |
288 }).then(() => renameBooksStore(testCase) | |
289 ).then(database => { | |
290 const transaction = database.transaction('renamed_books', 'readwrite'); | |
291 const store = transaction.objectStore('renamed_books'); | |
292 return checkStoreGenerator( | |
293 testCase, store, 345680, | |
294 'Renaming an object store should not change the state of its key ' + | |
295 ' generator').then(() => database.close()); | |
296 }); | |
297 }, 'IndexedDB object store rename covers key generator'); | |
298 | |
299 promise_test(testCase => { | |
300 return createDatabase(testCase, (database, transaction) => { | |
301 createBooksStore(testCase, database); | |
302 }).then(database => { | |
303 database.close(); | |
304 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
305 const store = transaction.objectStore('books'); | |
306 database.deleteObjectStore('books'); | |
307 assert_throws('InvalidStateError', () => store.name = 'renamed_books'); | |
308 })).then(database => { | |
309 database.close(); | |
310 }); | |
311 }, 'IndexedDB deleted object store rename throws'); | |
312 | |
313 promise_test(testCase => { | |
314 return createDatabase(testCase, (database, transaction) => { | |
315 createBooksStore(testCase, database); | |
316 }).then(database => { | |
317 const transaction = database.transaction('books', 'readonly'); | |
318 const store = transaction.objectStore('books'); | |
319 assert_throws('InvalidStateError', () => store.name = 'renamed_books'); | |
320 database.close(); | |
321 }); | |
322 }, 'IndexedDB object store rename throws in a readonly transaction'); | |
323 | |
324 promise_test(testCase => { | |
325 return createDatabase(testCase, (database, transaction) => { | |
326 createBooksStore(testCase, database); | |
327 }).then(database => { | |
328 const transaction = database.transaction('books', 'readwrite'); | |
329 const store = transaction.objectStore('books'); | |
330 | |
331 assert_throws('InvalidStateError', () => store.name = 'renamed_books'); | |
332 database.close(); | |
333 }); | |
334 }, 'IndexedDB object store rename throws in a readwrite transaction'); | |
335 | |
336 promise_test(testCase => { | |
337 let bookStore = null; | |
338 return createDatabase(testCase, (database, transaction) => { | |
339 bookStore = createBooksStore(testCase, database); | |
340 }).then(database => { | |
341 assert_throws('TransactionInactiveError', | |
342 () => { bookStore.name = 'renamed_books'; }); | |
343 database.close(); | |
344 }); | |
345 }, 'IndexedDB object store rename throws in an inactive transaction'); | |
346 | |
347 promise_test(testCase => { | |
348 return createDatabase(testCase, (database, transaction) => { | |
349 createBooksStore(testCase, database); | |
350 }).then(database => { | |
351 database.close(); | |
352 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
353 const store = transaction.objectStore('books'); | |
354 store.name = 'books'; | |
355 assert_array_equals( | |
356 database.objectStoreNames, ['books'], | |
357 'Renaming a store to the same name should not change ' + | |
358 "the store's IDBDatabase.objectStoreNames"); | |
359 })).then(database => { | |
360 assert_array_equals( | |
361 database.objectStoreNames, ['books'], | |
362 'Committing a transaction that renames a store to the same name ' + | |
363 "should not change the store's IDBDatabase.objectStoreNames"); | |
364 const transaction = database.transaction('books', 'readonly'); | |
365 const store = transaction.objectStore('books'); | |
366 return checkStoreContents( | |
367 testCase, store, | |
368 'Committing a transaction that renames a store to the same name ' + | |
369 "should not change the store's contents").then( | |
370 () => database.close()); | |
371 }); | |
372 }, 'IndexedDB object store rename to the same name succeeds'); | |
373 | |
374 promise_test(testCase => { | |
375 return createDatabase(testCase, (database, transaction) => { | |
376 createBooksStore(testCase, database); | |
377 createNotBooksStore(testCase, database); | |
378 }).then(database => { | |
379 database.close(); | |
380 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
381 const store = transaction.objectStore('books'); | |
382 assert_throws('ConstraintError', () => store.name = 'not_books'); | |
383 assert_array_equals( | |
384 database.objectStoreNames, ['books', 'not_books'], | |
385 'A store rename that throws an exception should not change the ' + | |
386 "store's IDBDatabase.objectStoreNames"); | |
387 })).then(database => { | |
388 assert_array_equals( | |
389 database.objectStoreNames, ['books', 'not_books'], | |
390 'Committing a transaction with a failed store rename attempt ' + | |
391 "should not change the store's IDBDatabase.objectStoreNames"); | |
392 const transaction = database.transaction('books', 'readonly'); | |
393 const store = transaction.objectStore('books'); | |
394 return checkStoreContents( | |
395 testCase, store, | |
396 'Committing a transaction with a failed rename attempt should not' + | |
397 "change the store's contents").then(() => database.close()); | |
398 }); | |
399 }, 'IndexedDB object store rename to the name of another store throws'); | |
400 | |
401 promise_test(testCase => { | |
402 return createDatabase(testCase, (database, transaction) => { | |
403 createBooksStore(testCase, database); | |
404 createNotBooksStore(testCase, database); | |
405 }).then(database => { | |
406 database.close(); | |
407 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
408 const store = transaction.objectStore('books'); | |
409 database.deleteObjectStore('not_books'); | |
410 store.name = 'not_books'; | |
411 assert_array_equals( | |
412 database.objectStoreNames, ['not_books'], | |
413 'IDBDatabase.objectStoreNames should immediately reflect the ' + | |
414 'rename'); | |
415 })).then(database => { | |
416 assert_array_equals( | |
417 database.objectStoreNames, ['not_books'], | |
418 'IDBDatabase.objectStoreNames should still reflect the rename ' + | |
419 'after the versionchange transaction commits'); | |
420 const transaction = database.transaction('not_books', 'readonly'); | |
421 const store = transaction.objectStore('not_books'); | |
422 return checkStoreContents( | |
423 testCase, store, | |
424 'Renaming an object store should not change its records').then( | |
425 () => database.close()); | |
426 }); | |
427 }, 'IndexedDB object store rename to the name of a deleted store succeeds'); | |
428 | |
429 promise_test(testCase => { | |
430 return createDatabase(testCase, (database, transaction) => { | |
431 createBooksStore(testCase, database); | |
432 createNotBooksStore(testCase, database); | |
433 }).then(database => { | |
434 database.close(); | |
435 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
436 const bookStore = transaction.objectStore('books'); | |
437 const notBookStore = transaction.objectStore('not_books'); | |
438 | |
439 transaction.objectStore('books').name = 'tmp'; | |
440 transaction.objectStore('not_books').name = 'books'; | |
441 transaction.objectStore('tmp').name = 'not_books'; | |
442 | |
443 assert_array_equals( | |
444 database.objectStoreNames, ['books', 'not_books'], | |
445 'IDBDatabase.objectStoreNames should immediately reflect the swap'); | |
446 | |
447 assert_equals( | |
448 transaction.objectStore('books'), notBookStore, | |
449 'IDBTransaction.objectStore should return the original "books" ' + | |
450 'store when queried with "not_books" after the swap'); | |
451 assert_equals( | |
452 transaction.objectStore('not_books'), bookStore, | |
453 'IDBTransaction.objectStore should return the original ' + | |
454 '"not_books" store when queried with "books" after the swap'); | |
455 })).then(database => { | |
456 assert_array_equals( | |
457 database.objectStoreNames, ['books', 'not_books'], | |
458 'IDBDatabase.objectStoreNames should still reflect the swap ' + | |
459 'after the versionchange transaction commits'); | |
460 const transaction = database.transaction('not_books', 'readonly'); | |
461 const store = transaction.objectStore('not_books'); | |
462 assert_array_equals( | |
463 store.indexNames, ['by_author', 'by_title'], | |
464 '"not_books" index names should still reflect the swap after the ' + | |
465 'versionchange transaction commits'); | |
466 return checkStoreContents( | |
467 testCase, store, | |
468 'Swapping two object stores should not change their records').then( | |
469 () => database.close()); | |
470 }); | |
471 }, 'IndexedDB object store swapping via renames succeeds'); | |
472 | |
473 promise_test(testCase => { | |
474 return createDatabase(testCase, (database, transaction) => { | |
475 createBooksStore(testCase, database); | |
476 }).then(database => { | |
477 database.close(); | |
478 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
479 const store = transaction.objectStore('books'); | |
480 | |
481 store.name = 42; | |
482 assert_equals(store.name, '42', | |
483 'IDBObjectStore name should change immediately after a ' + | |
484 'rename to a number'); | |
485 assert_array_equals( | |
486 database.objectStoreNames, ['42'], | |
487 'IDBDatabase.objectStoreNames should immediately reflect the ' + | |
488 'stringifying rename'); | |
489 | |
490 store.name = true; | |
491 assert_equals(store.name, 'true', | |
492 'IDBObjectStore name should change immediately after a ' + | |
493 'rename to a boolean'); | |
494 | |
495 store.name = {}; | |
496 assert_equals(store.name, '[object Object]', | |
497 'IDBObjectStore name should change immediately after a ' + | |
498 'rename to an object'); | |
499 | |
500 store.name = () => null; | |
501 assert_equals(store.name, '() => null', | |
502 'IDBObjectStore name should change immediately after a ' + | |
503 'rename to a function'); | |
504 | |
505 store.name = undefined; | |
506 assert_equals(store.name, 'undefined', | |
507 'IDBObjectStore name should change immediately after a ' + | |
508 'rename to undefined'); | |
509 })).then(database => { | |
510 assert_array_equals( | |
511 database.objectStoreNames, ['undefined'], | |
512 'IDBDatabase.objectStoreNames should reflect the last rename ' + | |
513 'after the versionchange transaction commits'); | |
514 const transaction = database.transaction('undefined', 'readonly'); | |
515 const store = transaction.objectStore('undefined'); | |
516 return checkStoreContents( | |
517 testCase, store, | |
518 'Renaming an object store should not change its records').then( | |
519 () => database.close()); | |
520 }); | |
521 }, 'IndexedDB object store rename stringifies non-string names'); | |
522 | |
523 promise_test(testCase => { | |
524 return createDatabase(testCase, (database, transaction) => { | |
525 createBooksStore(testCase, database); | |
526 }).then(database => { | |
527 database.close(); | |
528 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
529 const store = transaction.objectStore('books'); | |
530 assert_throws( | |
531 { name: 'Custom stringifying error'}, | |
532 () => { | |
533 store.name = { | |
534 toString: () => { throw { name: 'Custom stringifying error'}; } | |
535 }; | |
536 }, 'IDBObjectStore rename should re-raise toString() exception'); | |
537 assert_array_equals( | |
538 database.objectStoreNames, ['books'], | |
539 'A store rename that throws an exception should not change the ' + | |
540 "store's IDBDatabase.objectStoreNames"); | |
541 })).then(database => { | |
542 assert_array_equals( | |
543 database.objectStoreNames, ['books'], | |
544 'Committing a transaction with a failed store rename attempt ' + | |
545 "should not change the store's IDBDatabase.objectStoreNames"); | |
546 const transaction = database.transaction('books', 'readonly'); | |
547 const store = transaction.objectStore('books'); | |
548 return checkStoreContents( | |
549 testCase, store, | |
550 'Committing a transaction with a failed rename attempt should not' + | |
551 "change the store's contents").then(() => database.close()); | |
552 }); | |
553 }, 'IndexedDB object store rename handles exceptions when stringifying names'); | |
554 | |
555 for (let escapedName of ['', '\\u0000', '\\uDC00\\uD800']) ((escapedName) => { | |
556 const name = JSON.parse('"' + escapedName + '"'); | |
557 promise_test(testCase => { | |
558 return createDatabase(testCase, (database, transaction) => { | |
559 createBooksStore(testCase, database); | |
560 }).then(database => { | |
561 database.close(); | |
562 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => { | |
563 const store = transaction.objectStore('books'); | |
564 | |
565 store.name = name; | |
566 assert_equals(store.name, name, | |
567 'IDBObjectStore name should change immediately after the ' + | |
568 'rename'); | |
569 assert_array_equals( | |
570 database.objectStoreNames, [name], | |
571 'IDBDatabase.objectStoreNames should immediately reflect the ' + | |
572 'rename'); | |
573 })).then(database => { | |
574 assert_array_equals( | |
575 database.objectStoreNames, [name], | |
576 'IDBDatabase.objectStoreNames should reflect the rename ' + | |
577 'after the versionchange transaction commits'); | |
578 const transaction = database.transaction(name, 'readonly'); | |
579 const store = transaction.objectStore(name); | |
580 return checkStoreContents( | |
581 testCase, store, | |
582 'Renaming an object store should not change its records').then( | |
583 () => database.close()); | |
584 }); | |
585 }, 'IndexedDB object store can be renamed to "' + escapedName + '"'); | |
586 })(escapedName); | |
587 | |
588 promise_test(testCase => { | |
589 const dbName = databaseName(testCase); | |
590 let bookStore = null, bookStore2 = null; | |
591 return createDatabase(testCase, (database, transaction) => { | |
592 createBooksStore(testCase, database); | |
593 }).then(database => { | |
594 database.close(); | |
595 }).then(() => new Promise((resolve, reject) => { | |
596 const request = indexedDB.open(dbName, 2); | |
597 request.onupgradeneeded = event => { | |
598 const database = event.target.result; | |
599 const transaction = event.target.transaction; | |
600 bookStore = transaction.objectStore('books'); | |
601 bookStore.name = 'renamed_books'; | |
602 | |
603 request.onerror = event => { | |
604 event.preventDefault(); | |
605 resolve(event); | |
606 }; | |
607 transaction.abort(); | |
608 | |
609 assert_equals( | |
610 bookStore.name, 'books', | |
611 'IDBObjectStore.name should not reflect the rename anymore ' + | |
612 'immediately after transaction.abort() returns'); | |
613 assert_array_equals( | |
614 database.objectStoreNames, ['books'], | |
615 'IDBDatabase.objectStoreNames should not reflect the rename ' + | |
616 'anymore immediately after transaction.abort() returns'); | |
617 }; | |
618 request.onerror = event => reject(event.target.error); | |
619 request.onsuccess = () => reject(new Error( | |
620 'indexedDB.open was not supposed to succeed')); | |
621 })).then(event => { | |
622 assert_equals(bookStore.name, 'books', | |
623 'IDBObjectStore.name should not reflect the rename anymore ' + | |
624 'after the versionchange transaction is aborted'); | |
625 const request = indexedDB.open(dbName, 1); | |
626 return requestWatcher(testCase, request).wait_for('success'); | |
627 }).then(event => { | |
628 const database = event.target.result; | |
629 assert_array_equals( | |
630 database.objectStoreNames, ['books'], | |
631 'IDBDatabase.objectStoreNames should not reflect the rename ' + | |
632 'after the versionchange transaction is aborted'); | |
633 | |
634 const transaction = database.transaction('books', 'readonly'); | |
635 bookStore2 = transaction.objectStore('books'); | |
636 return checkStoreContents( | |
637 testCase, bookStore2, | |
638 'Aborting an object store rename transaction should not change ' + | |
639 "the store's records").then(() => database.close()); | |
640 }).then(() => { | |
641 assert_equals( | |
642 bookStore.name, 'books', | |
643 'IDBObjectStore used in aborted rename transaction should not ' + | |
644 'reflect the rename after the transaction is aborted'); | |
645 assert_equals( | |
646 bookStore2.name, 'books', | |
647 'IDBObjectStore obtained after an aborted rename transaction ' + | |
648 'should not reflect the rename'); | |
649 }); | |
650 }, 'IndexedDB object store rename in aborted transaction'); | |
651 </script> | |
OLD | NEW |