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

Side by Side Diff: third_party/WebKit/LayoutTests/storage/indexeddb/rename-index.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
« no previous file with comments | « no previous file | third_party/WebKit/LayoutTests/storage/indexeddb/rename-object-store.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // Verifies that an object store's index matches the index used to create the
75 // books store in the test database's version 1.
76 const checkIndexContents = function(testCase, index) {
77 const request = index.get('Barney');
78 const eventWatcher = requestWatcher(testCase, request);
79 return eventWatcher.wait_for('success').then(() => {
80 let result = request.result;
jsbell 2016/08/29 20:13:48 nit: const
pwnall 2016/09/01 23:34:15 Done. I went through my other lets as well.
81 testCase.step(() => {
jsbell 2016/08/29 20:13:48 testCase.step() should not be necessary in promise
pwnall 2016/09/01 23:34:15 Done. Removed.
82 assert_equals(result.isbn, 345678);
83 assert_equals(result.title, 'Bedrock Nights');
84 });
85 });
86 };
87
88 promise_test(testCase => {
89 let authorIndex = null, authorIndex2 = null;
90 let renamedAuthorIndex = null, renamedAuthorIndex2 = null;
91 return createDatabase(testCase, (database, transaction) => {
92 const store = createBooksStore(testCase, database);
93 authorIndex = store.index('by_author');
94 }).then(database => {
95 const transaction = database.transaction('books', 'readonly');
96 const store = transaction.objectStore('books');
97 testCase.step(() => {
98 assert_array_equals(store.indexNames, ['by_author', 'by_title']);
99 });
100 authorIndex2 = store.index('by_author');
101 // If checkIndexContents fails here, its implementation is incorrect.
jsbell 2016/08/29 20:13:49 Ideally, turn comments like this into assertion me
pwnall 2016/09/01 23:34:15 Done.
102 return checkIndexContents(testCase, authorIndex2).then(() => {
103 database.close();
104 });
105 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
106 const store = transaction.objectStore('books');
107 renamedAuthorIndex = store.index('by_author');
108 renamedAuthorIndex.name = 'renamed_by_author';
109 testCase.step(() => {
110 assert_equals(renamedAuthorIndex.name, 'renamed_by_author');
jsbell 2016/08/29 20:13:49 Try to have a message for each assert (we're not c
pwnall 2016/09/01 23:34:15 Done.
111 });
112 })).then(database => {
113 const transaction = database.transaction('books', 'readonly');
114 const store = transaction.objectStore('books');
115 testCase.step(() => {
116 assert_array_equals(store.indexNames,
117 ['by_title', 'renamed_by_author']);
118 });
119 renamedAuthorIndex2 = store.index('renamed_by_author');
120 return checkIndexContents(testCase, renamedAuthorIndex2).then(() => {
121 database.close();
122 });
123 }).then(() => {
124 testCase.step(() => {
125 assert_equals(authorIndex.name, 'by_author');
jsbell 2016/08/29 20:13:49 sample message: "IDBIndex from earlier transaction
pwnall 2016/09/01 23:34:15 Done. I ended up with a somewhat different messag
126 assert_equals(authorIndex2.name, 'by_author');
127 assert_equals(renamedAuthorIndex.name, 'renamed_by_author');
128 assert_equals(renamedAuthorIndex2.name, 'renamed_by_author');
129 });
130 });
131 }, 'IndexedDB index rename in new transaction');
132
133 promise_test(testCase => {
134 let renamedAuthorIndex = null, renamedAuthorIndex2 = null;
135 return createDatabase(testCase, (database, transaction) => {
136 const store = createBooksStore(testCase, database);
137 renamedAuthorIndex = store.index('by_author');
138 renamedAuthorIndex.name = 'renamed_by_author';
139 testCase.step(() => {
140 assert_equals(renamedAuthorIndex.name, 'renamed_by_author');
141 });
142 }).then(database => {
143 const transaction = database.transaction('books', 'readonly');
144 const store = transaction.objectStore('books');
145 testCase.step(() => {
146 assert_array_equals(store.indexNames,
147 ['by_title', 'renamed_by_author']);
148 });
149 renamedAuthorIndex2 = store.index('renamed_by_author');
150 return checkIndexContents(testCase, renamedAuthorIndex2).then(() => {
151 database.close();
152 });
153 }).then(() => {
154 testCase.step(() => {
155 assert_equals(renamedAuthorIndex.name, 'renamed_by_author');
156 assert_equals(renamedAuthorIndex2.name, 'renamed_by_author');
157 });
158 });
159 }, 'IndexedDB index rename in the transaction where it is created');
160
161 promise_test(testCase => {
162 const dbName = databaseName(testCase);
163 let authorIndex = null, authorIndex2 = null, authorIndex3 = null;
164 return createDatabase(testCase, (database, transaction) => {
165 const store = createBooksStore(testCase, database);
166 authorIndex = store.index('by_author');
167 }).then(database => {
168 database.close();
169 }).then(() => new Promise((resolve, reject) => {
170 const request = indexedDB.open(dbName, 2);
171 request.onupgradeneeded = (event) => {
jsbell 2016/08/29 20:13:48 nit: Don't need ()
pwnall 2016/09/01 23:34:15 Done. I made a cleaning pass for these.
172 const database = event.target.result;
173 const transaction = event.target.transaction;
174 const store = transaction.objectStore('books');
175 authorIndex2 = store.index('by_author');
176 authorIndex2.name = 'renamed_by_author';
177 testCase.step(() => {
178 assert_equals(authorIndex.name, 'by_author');
179 assert_equals(authorIndex2.name, 'renamed_by_author');
180 });
181 request.onerror = (event) => {
182 event.preventDefault();
183 resolve(event);
184 }
185 transaction.onabort = () => null;
jsbell 2016/08/29 20:13:48 Needed?
pwnall 2016/09/01 23:34:15 Done. Removed :)
186 transaction.onerror = () => null;
jsbell 2016/08/29 20:13:49 Needed?
pwnall 2016/09/01 23:34:15 Done.
187 transaction.abort();
jsbell 2016/08/29 20:13:49 Do we expect the index name to synchronously rever
pwnall 2016/09/01 23:34:15 Done. Added tests.
188 };
189 request.onerror = (event) => reject(event.error);
jsbell 2016/08/29 20:13:49 nit: Don't need ()
pwnall 2016/09/01 23:34:15 Done.
190 request.onsuccess = () => reject(new Error(
191 'indexedDB.open was not supposed to succeed'));
192 })).then(event => {
193 testCase.step(() => {
194 assert_equals(authorIndex2.name, 'by_author',
195 'index rename not reverted after transaction abort');
jsbell 2016/08/29 20:13:49 Message should describe the *expected* behavior, i
pwnall 2016/09/01 23:34:15 Done.
196 });
197
198 const request = indexedDB.open(dbName, 1);
199 const eventWatcher = requestWatcher(testCase, request);
jsbell 2016/08/29 20:13:49 These two lines could be combined, e.g.: return r
pwnall 2016/09/01 23:34:15 Done. I did this change in the two places where it
200 return eventWatcher.wait_for('success');
201 }).then(event => {
202 const database = event.target.result;
203 const transaction = database.transaction('books', 'readonly');
204 const store = transaction.objectStore('books');
205 authorIndex3 = store.index('by_author');
206 return checkIndexContents(testCase, authorIndex3).then(() => {
207 database.close();
208 });
209 }).then(() => {
210 testCase.step(() => {
211 assert_equals(authorIndex.name, 'by_author');
212 assert_equals(authorIndex2.name, 'by_author');
213 assert_equals(authorIndex3.name, 'by_author');
214 });
215 });
216 }, 'IndexedDB object store rename in aborted transaction');
217
218 promise_test(testCase => {
219 return createDatabase(testCase, (database, transaction) => {
220 createBooksStore(testCase, database);
221 }).then(database => {
222 database.close();
223 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
224 const store = transaction.objectStore('books');
225 const index = store.index('by_author');
226 store.deleteIndex('by_author');
227 testCase.step(() => {
228 assert_throws('InvalidStateError',
229 () => { index.name = 'renamed_by_author'; });
230 });
231 })).then(database => {
232 database.close();
233 });
234 }, 'IndexedDB deleted index rename throws');
235
236 promise_test(testCase => {
237 return createDatabase(testCase, (database, transaction) => {
238 createBooksStore(testCase, database);
239 }).then(database => {
240 const transaction = database.transaction('books', 'readonly');
241 const store = transaction.objectStore('books');
242 const index = store.index('by_author');
243
244 testCase.step(() => {
245 assert_throws('InvalidStateError',
246 () => { index.name = 'renamed_by_author'; });
247 });
248 database.close();
249 });
250 }, 'IndexedDB index rename throws in a readonly transaction');
251
252 promise_test(testCase => {
253 return createDatabase(testCase, (database, transaction) => {
254 createBooksStore(testCase, database);
255 }).then(database => {
256 const transaction = database.transaction('books', 'readwrite');
257 const store = transaction.objectStore('books');
258 const index = store.index('by_author');
259
260 testCase.step(() => {
261 assert_throws('InvalidStateError',
262 () => { index.name = 'renamed_books'; });
263 });
264 database.close();
265 });
266 }, 'IndexedDB index rename throws in a readwrite transaction');
267
268 promise_test(testCase => {
269 let authorIndex = null;
270 return createDatabase(testCase, (database, transaction) => {
271 const store = createBooksStore(testCase, database);
272 authorIndex = store.index('by_author');
273 }).then(database => {
274 testCase.step(() => {
275 assert_throws('TransactionInactiveError',
276 () => { authorIndex.name = 'renamed_by_author'; });
277 });
278 database.close();
279 });
280 }, 'IndexedDB index rename throws in an inactive transaction');
281
282 promise_test(testCase => {
283 return createDatabase(testCase, (database, transaction) => {
284 createBooksStore(testCase, database);
285 }).then(database => {
286 database.close();
287 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
288 const store = transaction.objectStore('books');
289 const index = store.index('by_author');
290 index.name = 'by_author';
291 })).then(database => {
292 const transaction = database.transaction('books', 'readonly');
293 const store = transaction.objectStore('books');
294 testCase.step(() => {
295 assert_array_equals(store.indexNames, ['by_author', 'by_title']);
296 });
297 const index = store.index('by_author');
298 return checkIndexContents(testCase, index).then(() => {
299 database.close();
300 });
301 });
302 }, 'IndexedDB index rename to the same name succeeds');
303
304 promise_test(testCase => {
305 return createDatabase(testCase, (database, transaction) => {
306 createBooksStore(testCase, database);
307 }).then(database => {
308 database.close();
309 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
310 const store = transaction.objectStore('books');
311 const index = store.index('by_author');
312
313 testCase.step(() => {
314 assert_throws('ConstraintError',
315 () => { index.name = 'by_title'; });
316 });
317 })).then(database => {
318 const transaction = database.transaction('books', 'readonly');
319 const store = transaction.objectStore('books');
320 testCase.step(() => {
321 assert_array_equals(store.indexNames, ['by_author', 'by_title']);
322 });
323 const index = store.index('by_author');
324 return checkIndexContents(testCase, index).then(() => {
325 database.close();
326 });
327 });
328 }, 'IndexedDB index rename to the name of another index throws');
jsbell 2016/08/29 20:13:49 Can you add a test case for swapping names, i.e.:
pwnall 2016/09/01 23:34:15 Done.
329
330 promise_test(testCase => {
331 return createDatabase(testCase, (database, transaction) => {
332 createBooksStore(testCase, database);
333 }).then(database => {
334 database.close();
335 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
336 const store = transaction.objectStore('books');
337 const index = store.index('by_author');
338 store.deleteIndex('by_title');
339 index.name = 'by_title';
340 })).then(database => {
341 const transaction = database.transaction('books', 'readonly');
342 const store = transaction.objectStore('books');
343 testCase.step(() => {
344 assert_array_equals(store.indexNames, ['by_title']);
345 });
346 const index = store.index('by_title');
347 return checkIndexContents(testCase, index).then(() => {
348 database.close();
349 });
350 });
351 }, 'IndexedDB index rename to the name of a deleted index succeeds');
352
353 promise_test(testCase => {
354 return createDatabase(testCase, (database, transaction) => {
355 createBooksStore(testCase, database);
356 }).then(database => {
357 database.close();
358 }).then(() => migrateDatabase(testCase, 2, (database, transaction) => {
359 const store = transaction.objectStore('books');
360 const index = store.index('by_author');
361
362 testCase.step(() => {
363 index.name = 42;
364 assert_equals(index.name, "42");
365 index.name = true;
366 assert_equals(index.name, "true");
367 index.name = () => null;
368 assert_equals(index.name, "() => null");
jsbell 2016/08/29 20:13:48 Other fun cases: index.name = {toString: () => {
pwnall 2016/09/01 23:34:15 Done. Firefox fails with '\uDC00\uD800'. Should I
jsbell 2016/09/01 23:47:09 In what way does it fail?
pwnall 2016/09/03 06:01:19 Sorry I forgot to answer this question! https://bu
369 index.name = undefined;
370 assert_equals(index.name, "undefined");
371 });
372 })).then(database => {
373 const transaction = database.transaction('books', 'readonly');
374 const store = transaction.objectStore('books');
375 testCase.step(() => {
376 assert_array_equals(store.indexNames, ['by_title', 'undefined']);
377 });
378 const index = store.index('undefined');
379 return checkIndexContents(testCase, index).then(() => {
380 database.close();
381 });
382 });
383 }, 'IndexedDB object store rename stringifies non-string names');
384 </script>
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/LayoutTests/storage/indexeddb/rename-object-store.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698