OLD | NEW |
---|---|
(Empty) | |
1 <!DOCTYPE html> | |
2 <title>IndexedDB v2: 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 ['abort', 'complete', 'error', 'success', 'upgradeneeded']); | |
20 }; | |
21 | |
22 // Creates 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 createDatabase = function(testCase, setupCallback) { | |
29 const dbName = databaseName(testCase); | |
30 const request = indexedDB.deleteDatabase(dbName); | |
31 const eventWatcher = requestWatcher(testCase, request); | |
32 | |
33 return eventWatcher.wait_for('success').then((event) => { | |
34 // We cannot use eventWatcher.wait_for('upgradeneeded') here, because | |
35 // the versionchange transaction auto-commits before the Promise's then | |
36 // callback gets called. | |
37 return new Promise((resolve, reject) => { | |
38 const request = indexedDB.open(dbName, 1); | |
39 request.onupgradeneeded = (event) => { | |
40 const eventWatcher = requestWatcher(testCase, request); | |
41 const database = event.target.result; | |
42 const transaction = event.target.transaction; | |
43 setupCallback(database, transaction); | |
44 resolve(eventWatcher.wait_for('success')); | |
45 }; | |
46 request.onerror = | |
47 testCase.unreached_func('indexedDB.open issued an error event'); | |
48 }); | |
49 }); | |
50 }; | |
51 | |
52 // Creates a 'books' object store whose contents closely resembles the first | |
53 // example in the IndexedDB specification. | |
54 const createBooksStore = function(testCase, database) { | |
55 const store = database.createObjectStore('books', | |
56 { keyPath: 'isbn', autoIncrement: true }); | |
57 const index = store.createIndex('by_author', 'author'); | |
58 store.put({title: 'Quarry Memories', author: 'Fred', isbn: 123456}); | |
59 store.put({title: 'Water Buffaloes', author: 'Fred', isbn: 234567}); | |
60 store.put({title: 'Bedrock Nights', author: 'Barney', isbn: 345678}); | |
61 return store; | |
62 }; | |
63 | |
64 // Creates a 'not_books' object store used to test renaming into existing or | |
65 // deleted store names. | |
66 const createNotBooksStore = function(testCase, database) { | |
cmumford
2016/08/26 21:23:52
You need to call this somewhere too right? Also, c
pwnall
2016/08/26 22:23:17
Done.
I wrote createNotBooksStore to test the ren
| |
67 const store = database.createObjectStore('not_books'); | |
68 return store; | |
69 }; | |
70 | |
71 // Renames the 'books' store into a 'not books' store. | |
72 // | |
73 // Returns a Promise that yields an IndexedDB open success event. | |
74 const renameBooksStore = function(testCase) { | |
75 return new Promise((resolve, reject) => { | |
76 const request = indexedDB.open(databaseName(testCase), 2); | |
77 request.onupgradeneeded = (event) => { | |
78 const eventWatcher = requestWatcher(testCase, request); | |
79 const database = event.target.result; | |
80 const transaction = event.target.transaction; | |
81 const store = transaction.objectStore('books'); | |
82 store.name = 'renamed_books'; | |
83 resolve(eventWatcher.wait_for('success')); | |
84 }; | |
85 request.onerror = | |
86 testCase.unreached_func('indexedDB.open issued an error event'); | |
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 let request = store.get(123456); | |
94 let 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 let index = store.index('by_author'); | |
109 let request = index.get('Barney'); | |
110 let eventWatcher = requestWatcher(testCase, request); | |
111 return eventWatcher.wait_for('success').then(() => { | |
112 let result = request.result; | |
113 testCase.step(() => { | |
114 assert_equals(result.isbn, 345678); | |
115 assert_equals(result.title, 'Bedrock Nights'); | |
116 }); | |
117 }); | |
118 }; | |
119 | |
120 // Verifies that an object store's key generator is in the same state as the | |
121 // key generator created for the books store in the test database's version 1. | |
122 const checkStoreGenerator = function(testCase, store, expectedKey) { | |
123 let request = store.put({title: 'Bedrock Nights II', author: 'Barney'}); | |
124 let eventWatcher = requestWatcher(testCase, request); | |
125 return eventWatcher.wait_for('success').then(() => { | |
126 let result = request.result; | |
127 testCase.step(() => { | |
128 assert_equals(result, expectedKey); | |
129 }); | |
130 }); | |
131 }; | |
132 | |
133 promise_test(testCase => { | |
134 let bookStore = null, bookStore2 = null; | |
135 let renamedBookStore = null, renamedBookStore2 = null; | |
136 return createDatabase(testCase, (database, transaction) => { | |
137 bookStore = createBooksStore(testCase, database); | |
138 }).then((event) => { | |
139 const database = event.target.result; | |
140 testCase.step(() => { | |
141 assert_array_equals(database.objectStoreNames, ['books']); | |
142 }); | |
143 const transaction = database.transaction('books', 'readonly'); | |
144 bookStore2 = transaction.objectStore('books'); | |
145 // If checkStoreContents fails here, its implementation is incorrect. | |
146 return checkStoreContents(testCase, bookStore2).then(() => { | |
147 database.close(); | |
148 }); | |
149 }).then(() => { | |
150 return new Promise((resolve, reject) => { | |
151 const request = indexedDB.open(databaseName(testCase), 2); | |
152 request.onupgradeneeded = (event) => { | |
153 const eventWatcher = requestWatcher(testCase, request); | |
154 const database = event.target.result; | |
155 const transaction = event.target.transaction; | |
156 renamedBookStore = transaction.objectStore('books'); | |
157 renamedBookStore.name = 'renamed_books'; | |
158 testCase.step(() => { | |
159 assert_equals(renamedBookStore.name, 'renamed_books'); | |
160 }); | |
161 resolve(eventWatcher.wait_for('success')); | |
162 }; | |
163 request.onerror = | |
164 testCase.unreached_func('indexedDB.open issued an error event'); | |
165 }); | |
166 }).then((event) => { | |
167 const database = event.target.result; | |
168 testCase.step(() => { | |
169 assert_array_equals(database.objectStoreNames, ['renamed_books']); | |
170 }); | |
171 const transaction = database.transaction('renamed_books', 'readonly'); | |
172 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
173 return checkStoreContents(testCase, renamedBookStore2).then(() => { | |
174 database.close(); | |
175 }); | |
176 }).then(() => { | |
177 testCase.step(() => { | |
178 assert_equals(bookStore.name, 'books'); | |
179 assert_equals(bookStore2.name, 'books'); | |
180 assert_equals(renamedBookStore.name, 'renamed_books'); | |
181 assert_equals(renamedBookStore2.name, 'renamed_books'); | |
182 }); | |
183 }); | |
184 }, 'IndexedDB object store rename in new transaction', { | |
185 assert: 'An object store can be renamed in a new version change transaction' , | |
186 }); | |
187 | |
188 promise_test(testCase => { | |
189 let renamedBookStore = null, renamedBookStore2 = null; | |
190 return createDatabase(testCase, (database, transaction) => { | |
191 renamedBookStore = createBooksStore(testCase, database); | |
192 renamedBookStore.name = 'renamed_books'; | |
193 testCase.step(() => { | |
194 assert_equals(renamedBookStore.name, 'renamed_books'); | |
195 }); | |
196 }).then((event) => { | |
197 const database = event.target.result; | |
198 testCase.step(() => { | |
199 assert_array_equals(database.objectStoreNames, ['renamed_books']); | |
200 }); | |
201 const transaction = database.transaction('renamed_books', 'readonly'); | |
202 renamedBookStore2 = transaction.objectStore('renamed_books'); | |
203 return checkStoreContents(testCase, renamedBookStore2).then(() => { | |
204 database.close(); | |
205 }); | |
206 }).then(() => { | |
207 testCase.step(() => { | |
208 assert_equals(renamedBookStore.name, 'renamed_books'); | |
209 assert_equals(renamedBookStore2.name, 'renamed_books'); | |
210 }); | |
211 }); | |
212 }, 'IndexedDB object store rename in the transaction where it is created', { | |
213 assert: | |
214 'An object store can be renamed in the transaction where it is created', | |
215 }); | |
216 | |
217 promise_test(testCase => { | |
218 return createDatabase(testCase, (database, transaction) => { | |
219 createBooksStore(testCase, database); | |
220 }).then((event) => { | |
221 const database = event.target.result; | |
222 const transaction = database.transaction('books', 'readonly'); | |
223 const store = transaction.objectStore('books'); | |
224 // If checkStoreIndex fails here, its implementation is incorrect. | |
225 return checkStoreIndex(testCase, store).then(() => { | |
226 database.close(); | |
227 }); | |
228 }).then(() => { | |
229 return renameBooksStore(testCase); | |
230 }).then((event) => { | |
231 const database = event.target.result; | |
232 const transaction = database.transaction('renamed_books', 'readonly'); | |
233 const store = transaction.objectStore('renamed_books'); | |
234 return checkStoreIndex(testCase, store).then(() => { | |
235 database.close(); | |
236 }); | |
237 }); | |
238 }, 'IndexedDB object store rename covers index', { | |
239 assert: 'Renaming an object store maintains its indexes', | |
240 }); | |
241 | |
242 promise_test(testCase => { | |
243 return createDatabase(testCase, (database, transaction) => { | |
244 createBooksStore(testCase, database); | |
245 }).then((event) => { | |
246 const database = event.target.result; | |
247 const transaction = database.transaction('books', 'readwrite'); | |
248 // If checkStoreGenerator fails here, its implementation is incorrect. | |
249 const store = transaction.objectStore('books'); | |
250 return checkStoreGenerator(testCase, store, 345679).then(() => { | |
251 database.close(); | |
252 }); | |
253 }).then(() => { | |
254 return renameBooksStore(testCase); | |
255 }).then((event) => { | |
256 const database = event.target.result; | |
257 const transaction = database.transaction('renamed_books', 'readwrite'); | |
258 const store = transaction.objectStore('renamed_books'); | |
259 return checkStoreGenerator(testCase, store, 345680).then(() => { | |
260 database.close(); | |
261 }); | |
262 }); | |
263 }, 'IndexedDB object store rename covers key generator', { | |
264 assert: 'Renaming an object store maintains its key generator', | |
265 }); | |
266 | |
267 promise_test(testCase => { | |
268 const dbName = databaseName(testCase); | |
269 let bookStore = null, bookStore2 = null, bookStore3 = null; | |
270 return createDatabase(testCase, (database, transaction) => { | |
271 bookStore = createBooksStore(testCase, database); | |
272 }).then((event) => { | |
273 const database = event.target.result; | |
274 database.close(); | |
275 }).then((event) => { | |
276 return new Promise((resolve, reject) => { | |
277 const request = indexedDB.open(dbName, 2); | |
278 request.onupgradeneeded = (event) => { | |
279 const database = event.target.result; | |
280 const transaction = event.target.transaction; | |
281 bookStore2 = transaction.objectStore('books'); | |
282 bookStore2.name = 'renamed_books'; | |
283 testCase.step(() => { | |
284 assert_equals(bookStore.name, 'books'); | |
285 assert_equals(bookStore2.name, 'renamed_books'); | |
286 }); | |
287 request.onerror = (event) => { | |
288 event.preventDefault(); | |
289 resolve(event); | |
290 } | |
291 transaction.onabort = () => null; | |
292 transaction.onerror = () => null; | |
293 transaction.abort(); | |
294 }; | |
295 request.onerror = | |
296 testCase.unreached_func('indexedDB.open issued an error event'); | |
297 request.onsuccess = testCase.unreached_func( | |
298 'indexedDB.open was not supposed to succeed'); | |
299 }); | |
300 }).then((event) => { | |
301 testCase.step(() => { | |
302 assert_equals(bookStore.name, 'books', | |
303 'object store rename not reverted after transaction abort'); | |
304 }); | |
305 | |
306 const request = indexedDB.open(dbName, 1); | |
307 const eventWatcher = requestWatcher(testCase, request); | |
308 return eventWatcher.wait_for('success'); | |
309 }).then((event) => { | |
310 const database = event.target.result; | |
311 const transaction = database.transaction('books', 'readonly'); | |
312 bookStore3 = transaction.objectStore('books'); | |
313 return checkStoreContents(testCase, bookStore3).then(() => { | |
314 database.close(); | |
315 }); | |
316 }).then(() => { | |
317 testCase.step(() => { | |
318 assert_equals(bookStore.name, 'books'); | |
319 assert_equals(bookStore2.name, 'books'); | |
320 assert_equals(bookStore3.name, 'books'); | |
321 }); | |
322 }); | |
323 }, 'IndexedDB object store rename in aborted transaction', { | |
324 assert: 'An object store rename is correctly reverted', | |
325 }); | |
326 | |
327 </script> | |
OLD | NEW |