Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Side by Side Diff: third_party/WebKit/LayoutTests/storage/indexeddb/rename-object-store.html

Issue 2276593002: Support renaming of IndexedDB indexes and object stores. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed feedback on tests. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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.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 });
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 const store = database.createObjectStore('not_books');
78 return store;
79 };
80
81 // Renames the 'books' store to 'renamed_books'.
82 //
83 // Returns a promise that resolves to an IndexedDB database. The caller must
84 // close the database.
85 const renameBooksStore = function(testCase) {
86 return migrateDatabase(testCase, 2, (database, transaction) => {
87 const store = transaction.objectStore('books');
88 store.name = 'renamed_books';
89 });
90 };
91
92 // Verifies that an object store's contents matches the contents used to create
93 // the books store in the test database's version 1.
94 const checkStoreContents = function(testCase, store) {
95 const request = store.get(123456);
96 const eventWatcher = requestWatcher(testCase, request);
97 return eventWatcher.wait_for('success').then(() => {
98 let result = request.result;
99 testCase.step(() => {
100 assert_equals(result.isbn, 123456);
101 assert_equals(result.author, 'Fred');
102 assert_equals(result.title, 'Quarry Memories');
103 });
104 });
105 };
106
107 // Verifies that an object store's index matches the index used to create the
108 // books store in the test database's version 1.
109 const checkStoreIndex = function(testCase, store) {
110 testCase.step(() => {
111 assert_array_equals(store.indexNames, ['by_author', 'by_title']);
112 });
113 const index = store.index('by_author');
114 const request = index.get('Barney');
115 const eventWatcher = requestWatcher(testCase, request);
116 return eventWatcher.wait_for('success').then(() => {
117 let result = request.result;
118 testCase.step(() => {
119 assert_equals(result.isbn, 345678);
120 assert_equals(result.title, 'Bedrock Nights');
121 });
122 });
123 };
124
125 // Verifies that an object store's key generator is in the same state as the
126 // key generator created for the books store in the test database's version 1.
127 const checkStoreGenerator = function(testCase, store, expectedKey) {
128 const request = store.put(
129 { title: 'Bedrock Nights ' + expectedKey, author: 'Barney' });
130 const eventWatcher = requestWatcher(testCase, request);
131 return eventWatcher.wait_for('success').then(() => {
132 let result = request.result;
133 testCase.step(() => {
134 assert_equals(result, expectedKey);
135 });
136 });
137 };
138
139 promise_test(testCase => {
140 let bookStore = null, bookStore2 = null;
141 let renamedBookStore = null, renamedBookStore2 = null;
142 return createDatabase(testCase, (database, transaction) => {
143 bookStore = createBooksStore(testCase, database);
144 }).then(database => {
145 testCase.step(() => {
146 assert_array_equals(database.objectStoreNames, ['books']);
147 });
148 const transaction = database.transaction('books', 'readonly');
149 bookStore2 = transaction.objectStore('books');
150 // If checkStoreContents fails here, its implementation is incorrect.
151 return checkStoreContents(testCase, bookStore2).then(() => {
152 database.close();
153 });
154 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
155 renamedBookStore = transaction.objectStore('books');
156 renamedBookStore.name = 'renamed_books';
157 testCase.step(() => {
158 assert_equals(renamedBookStore.name, 'renamed_books');
159 });
160 })).then(database => {
161 testCase.step(() => {
162 assert_array_equals(database.objectStoreNames, ['renamed_books']);
163 });
164 const transaction = database.transaction('renamed_books', 'readonly');
165 renamedBookStore2 = transaction.objectStore('renamed_books');
166 return checkStoreContents(testCase, renamedBookStore2).then(() => {
167 database.close();
168 });
169 }).then(() => {
170 testCase.step(() => {
171 assert_equals(bookStore.name, 'books');
172 assert_equals(bookStore2.name, 'books');
173 assert_equals(renamedBookStore.name, 'renamed_books');
174 assert_equals(renamedBookStore2.name, 'renamed_books');
175 });
176 });
177 }, 'IndexedDB object store rename in new transaction');
178
179 promise_test(testCase => {
180 let renamedBookStore = null, renamedBookStore2 = null;
181 return createDatabase(testCase, (database, transaction) => {
182 renamedBookStore = createBooksStore(testCase, database);
183 renamedBookStore.name = 'renamed_books';
184 testCase.step(() => {
185 assert_equals(renamedBookStore.name, 'renamed_books');
186 });
187 }).then(database => {
188 testCase.step(() => {
189 assert_array_equals(database.objectStoreNames, ['renamed_books']);
190 });
191 const transaction = database.transaction('renamed_books', 'readonly');
192 renamedBookStore2 = transaction.objectStore('renamed_books');
193 return checkStoreContents(testCase, renamedBookStore2).then(() => {
194 database.close();
195 });
196 }).then(() => {
197 testCase.step(() => {
198 assert_equals(renamedBookStore.name, 'renamed_books');
199 assert_equals(renamedBookStore2.name, 'renamed_books');
200 });
201 });
202 }, 'IndexedDB object store rename in the transaction where it is created');
203
204 promise_test(testCase => {
205 return createDatabase(testCase, (database, transaction) => {
206 createBooksStore(testCase, database);
207 }).then(database => {
208 const transaction = database.transaction('books', 'readonly');
209 const store = transaction.objectStore('books');
210 // If checkStoreIndex fails here, its implementation is incorrect.
211 return checkStoreIndex(testCase, store).then(() => {
212 database.close();
213 });
214 }).then(() => renameBooksStore(testCase)
215 ).then(database => {
216 const transaction = database.transaction('renamed_books', 'readonly');
217 const store = transaction.objectStore('renamed_books');
218 return checkStoreIndex(testCase, store).then(() => {
219 database.close();
220 });
221 });
222 }, 'IndexedDB object store rename covers index');
223
224 promise_test(testCase => {
225 return createDatabase(testCase, (database, transaction) => {
226 createBooksStore(testCase, database);
227 }).then(database => {
228 const transaction = database.transaction('books', 'readwrite');
229 // If checkStoreGenerator fails here, its implementation is incorrect.
230 const store = transaction.objectStore('books');
231 return checkStoreGenerator(testCase, store, 345679).then(() => {
232 database.close();
233 });
234 }).then(() => renameBooksStore(testCase)
235 ).then(database => {
236 const transaction = database.transaction('renamed_books', 'readwrite');
237 const store = transaction.objectStore('renamed_books');
238 return checkStoreGenerator(testCase, store, 345680).then(() => {
239 database.close();
240 });
241 });
242 }, 'IndexedDB object store rename covers key generator');
243
244 promise_test(testCase => {
245 const dbName = databaseName(testCase);
246 let bookStore = null, bookStore2 = null, bookStore3 = null;
247 return createDatabase(testCase, (database, transaction) => {
248 bookStore = createBooksStore(testCase, database);
249 }).then(database => {
250 database.close();
251 }).then(() => new Promise((resolve, reject) => {
252 const request = indexedDB.open(dbName, 2);
253 request.onupgradeneeded = (event) => {
254 const database = event.target.result;
255 const transaction = event.target.transaction;
256 bookStore2 = transaction.objectStore('books');
257 bookStore2.name = 'renamed_books';
258 testCase.step(() => {
259 assert_equals(bookStore.name, 'books');
260 assert_equals(bookStore2.name, 'renamed_books');
261 });
262 request.onerror = (event) => {
263 event.preventDefault();
264 resolve(event);
265 }
266 transaction.onabort = () => null;
267 transaction.onerror = () => null;
268 transaction.abort();
269 };
270 request.onerror = (event) => reject(event.error);
271 request.onsuccess = () => reject(new Error(
272 'indexedDB.open was not supposed to succeed'));
273 })).then(event => {
274 testCase.step(() => {
275 assert_equals(bookStore.name, 'books',
276 'object store rename not reverted after transaction abort');
277 });
278
279 const request = indexedDB.open(dbName, 1);
280 const eventWatcher = requestWatcher(testCase, request);
281 return eventWatcher.wait_for('success');
282 }).then(event => {
283 const database = event.target.result;
284 const transaction = database.transaction('books', 'readonly');
285 bookStore3 = transaction.objectStore('books');
286 return checkStoreContents(testCase, bookStore3).then(() => {
287 database.close();
288 });
289 }).then(() => {
290 testCase.step(() => {
291 assert_equals(bookStore.name, 'books');
292 assert_equals(bookStore2.name, 'books');
293 assert_equals(bookStore3.name, 'books');
294 });
295 });
296 }, 'IndexedDB object store rename in aborted transaction');
297
298 promise_test(testCase => {
299 return createDatabase(testCase, (database, transaction) => {
300 createBooksStore(testCase, database);
301 }).then(database => {
302 database.close();
303 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
304 const store = transaction.objectStore('books');
305 database.deleteObjectStore('books');
306 testCase.step(() => {
307 assert_throws('InvalidStateError',
308 () => { store.name = 'renamed_books'; });
309 });
310 })).then(database => {
311 database.close();
312 });
313 }, 'IndexedDB deleted object store rename throws');
314
315 promise_test(testCase => {
316 return createDatabase(testCase, (database, transaction) => {
317 createBooksStore(testCase, database);
318 }).then(database => {
319 const transaction = database.transaction('books', 'readonly');
320 const store = transaction.objectStore('books');
321
322 testCase.step(() => {
323 assert_throws('InvalidStateError',
324 () => { store.name = 'renamed_books'; });
325 });
326 database.close();
327 });
328 }, 'IndexedDB object store rename throws in a readonly transaction');
329
330 promise_test(testCase => {
331 return createDatabase(testCase, (database, transaction) => {
332 createBooksStore(testCase, database);
333 }).then(database => {
334 const transaction = database.transaction('books', 'readwrite');
335 const store = transaction.objectStore('books');
336
337 testCase.step(() => {
338 assert_throws('InvalidStateError',
339 () => { store.name = 'renamed_books'; });
340 });
341 database.close();
342 });
343 }, 'IndexedDB object store rename throws in a readwrite transaction');
344
345 promise_test(testCase => {
346 let bookStore = null;
347 return createDatabase(testCase, (database, transaction) => {
348 bookStore = createBooksStore(testCase, database);
349 }).then(database => {
350 testCase.step(() => {
351 assert_throws('TransactionInactiveError',
352 () => { bookStore.name = 'renamed_books'; });
353 });
354 database.close();
355 });
356 }, 'IndexedDB object store rename throws in an inactive transaction');
357
358 promise_test(testCase => {
359 return createDatabase(testCase, (database, transaction) => {
360 createBooksStore(testCase, database);
361 }).then(database => {
362 database.close();
363 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
364 const store = transaction.objectStore('books');
365 store.name = 'books';
366 })).then(database => {
367 testCase.step(() => {
368 assert_array_equals(database.objectStoreNames, ['books']);
369 });
370 const transaction = database.transaction('books', 'readonly');
371 const store = transaction.objectStore('books');
372 return checkStoreContents(testCase, store).then(() => {
373 database.close();
374 });
375 });
376 }, 'IndexedDB object store rename to the same name succeeds');
377
378 promise_test(testCase => {
379 return createDatabase(testCase, (database, transaction) => {
380 createBooksStore(testCase, database);
381 createNotBooksStore(testCase, database);
382 }).then(database => {
383 database.close();
384 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
385 const store = transaction.objectStore('books');
386 testCase.step(() => {
387 assert_throws('ConstraintError',
388 () => { store.name = 'not_books'; });
389 });
390 })).then(database => {
391 testCase.step(() => {
392 assert_array_equals(database.objectStoreNames,
393 ['books', 'not_books']);
394 });
395 const transaction = database.transaction('books', 'readonly');
396 const store = transaction.objectStore('books');
397 return checkStoreContents(testCase, store).then(() => {
398 database.close();
399 });
400 });
401 }, 'IndexedDB object store rename to the name of another store throws');
402
403 promise_test(testCase => {
404 return createDatabase(testCase, (database, transaction) => {
405 createBooksStore(testCase, database);
406 createNotBooksStore(testCase, database);
407 }).then(database => {
408 database.close();
409 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
410 const store = transaction.objectStore('books');
411 database.deleteObjectStore('not_books');
412 store.name = 'not_books';
413 })).then(database => {
414 testCase.step(() => {
415 assert_array_equals(database.objectStoreNames, ['not_books']);
416 });
417 const transaction = database.transaction('not_books', 'readonly');
418 const store = transaction.objectStore('not_books');
419 return checkStoreContents(testCase, store).then(() => {
420 database.close();
421 });
422 });
423 }, 'IndexedDB object store rename to the name of a deleted store succeeds');
424
425 promise_test(testCase => {
426 return createDatabase(testCase, (database, transaction) => {
427 createBooksStore(testCase, database);
428 }).then(database => {
429 database.close();
430 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
431 const store = transaction.objectStore('books');
432 testCase.step(() => {
433 store.name = 42;
434 assert_equals(store.name, "42");
435 store.name = true;
436 assert_equals(store.name, "true");
437 store.name = () => null;
438 assert_equals(store.name, "() => null");
439 store.name = undefined;
440 assert_equals(store.name, "undefined");
441 });
442 })).then(database => {
443 testCase.step(() => {
444 assert_array_equals(database.objectStoreNames, ['undefined']);
445 });
446 const transaction = database.transaction('undefined', 'readonly');
447 const store = transaction.objectStore('undefined');
448 return checkStoreContents(testCase, store).then(() => {
449 database.close();
450 });
451 });
452 }, 'IndexedDB object store rename stringifies non-string names');
453 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698