OLD | NEW |
| (Empty) |
1 // Returns an IndexedDB database name likely to be unique to the test case. | |
2 const databaseName = (testCase) => { | |
3 return 'db' + self.location.pathname + '-' + testCase.name; | |
4 }; | |
5 | |
6 // Creates an EventWatcher covering all the events that can be issued by | |
7 // IndexedDB requests and transactions. | |
8 const requestWatcher = (testCase, request) => { | |
9 return new EventWatcher(testCase, request, | |
10 ['error', 'success', 'upgradeneeded']); | |
11 }; | |
12 | |
13 // Migrates an IndexedDB database whose name is unique for the test case. | |
14 // | |
15 // newVersion must be greater than the database's current version. | |
16 // | |
17 // migrationCallback will be called during a versionchange transaction and will | |
18 // be given the created database and the versionchange transaction. | |
19 // | |
20 // Returns a promise. If the versionchange transaction goes through, the promise | |
21 // resolves to an IndexedDB database that must be closed by the caller. If the | |
22 // versionchange transaction is aborted, the promise resolves to an error. | |
23 const migrateDatabase = (testCase, newVersion, migrationCallback) => { | |
24 // We cannot use eventWatcher.wait_for('upgradeneeded') here, because | |
25 // the versionchange transaction auto-commits before the Promise's then | |
26 // callback gets called. | |
27 return new Promise((resolve, reject) => { | |
28 const request = indexedDB.open(databaseName(testCase), newVersion); | |
29 request.onupgradeneeded = testCase.step_func(event => { | |
30 const database = event.target.result; | |
31 const transaction = event.target.transaction; | |
32 let abortCalled = false; | |
33 | |
34 // We wrap IDBTransaction.abort so we can set up the correct event | |
35 // listeners and expectations if the test chooses to abort the | |
36 // versionchange transaction. | |
37 const transactionAbort = transaction.abort.bind(transaction); | |
38 transaction.abort = () => { | |
39 request.onerror = event => { | |
40 event.preventDefault(); | |
41 resolve(event); | |
42 }; | |
43 request.onsuccess = () => reject(new Error( | |
44 'indexedDB.open should not succeed after the ' + | |
45 'versionchange transaction is aborted')); | |
46 transactionAbort(); | |
47 abortCalled = true; | |
48 } | |
49 | |
50 migrationCallback(database, transaction); | |
51 if (!abortCalled) { | |
52 request.onsuccess = null; | |
53 resolve(requestWatcher(testCase, request).wait_for('success')); | |
54 } | |
55 }); | |
56 request.onerror = event => reject(event.target.error); | |
57 request.onsuccess = () => reject(new Error( | |
58 'indexedDB.open should not succeed without creating a ' + | |
59 'versionchange transaction')); | |
60 }).then(event => event.target.result || event.target.error); | |
61 }; | |
62 | |
63 // Creates an IndexedDB database whose name is unique for the test case. | |
64 // | |
65 // setupCallback will be called during a versionchange transaction, and will be | |
66 // given the created database and the versionchange transaction. | |
67 // | |
68 // Returns a promise that resolves to an IndexedDB database. The caller must | |
69 // close the database. | |
70 const createDatabase = (testCase, setupCallback) => { | |
71 const request = indexedDB.deleteDatabase(databaseName(testCase)); | |
72 const eventWatcher = requestWatcher(testCase, request); | |
73 | |
74 return eventWatcher.wait_for('success').then(event => | |
75 migrateDatabase(testCase, 1, setupCallback)); | |
76 }; | |
77 | |
78 // Opens an IndexedDB database without performing schema changes. | |
79 // | |
80 // The given version number must match the database's current version. | |
81 // | |
82 // Returns a promise that resolves to an IndexedDB database. The caller must | |
83 // close the database. | |
84 const openDatabase = (testCase, version) => { | |
85 const request = indexedDB.open(databaseName(testCase), version); | |
86 const eventWatcher = requestWatcher(testCase, request); | |
87 return eventWatcher.wait_for('success').then( | |
88 event => event.target.result); | |
89 } | |
90 | |
91 // The data in the 'books' object store records in the first example of the | |
92 // IndexedDB specification. | |
93 const BOOKS_RECORD_DATA = [ | |
94 { title: 'Quarry Memories', author: 'Fred', isbn: 123456 }, | |
95 { title: 'Water Buffaloes', author: 'Fred', isbn: 234567 }, | |
96 { title: 'Bedrock Nights', author: 'Barney', isbn: 345678 }, | |
97 ]; | |
98 | |
99 // Creates a 'books' object store whose contents closely resembles the first | |
100 // example in the IndexedDB specification. | |
101 const createBooksStore = (testCase, database) => { | |
102 const store = database.createObjectStore('books', | |
103 { keyPath: 'isbn', autoIncrement: true }); | |
104 store.createIndex('by_author', 'author'); | |
105 store.createIndex('by_title', 'title', { unique: true }); | |
106 for (let record of BOOKS_RECORD_DATA) | |
107 store.put(record); | |
108 return store; | |
109 }; | |
110 | |
111 // Creates a 'not_books' object store used to test renaming into existing or | |
112 // deleted store names. | |
113 const createNotBooksStore = (testCase, database) => { | |
114 const store = database.createObjectStore('not_books'); | |
115 store.createIndex('not_by_author', 'author'); | |
116 store.createIndex('not_by_title', 'title', { unique: true }); | |
117 return store; | |
118 }; | |
119 | |
120 // Verifies that an object store's indexes match the indexes used to create the | |
121 // books store in the test database's version 1. | |
122 // | |
123 // The errorMessage is used if the assertions fail. It can state that the | |
124 // IndexedDB implementation being tested is incorrect, or that the testing code | |
125 // is using it incorrectly. | |
126 const checkStoreIndexes = (testCase, store, errorMessage) => { | |
127 assert_array_equals( | |
128 store.indexNames, ['by_author', 'by_title'], errorMessage); | |
129 const authorIndex = store.index('by_author'); | |
130 const titleIndex = store.index('by_title'); | |
131 return Promise.all([ | |
132 checkAuthorIndexContents(testCase, authorIndex, errorMessage), | |
133 checkTitleIndexContents(testCase, titleIndex, errorMessage), | |
134 ]); | |
135 }; | |
136 | |
137 // Verifies that an object store's key generator is in the same state as the | |
138 // key generator created for the books store in the test database's version 1. | |
139 // | |
140 // The errorMessage is used if the assertions fail. It can state that the | |
141 // IndexedDB implementation being tested is incorrect, or that the testing code | |
142 // is using it incorrectly. | |
143 const checkStoreGenerator = (testCase, store, expectedKey, errorMessage) => { | |
144 const request = store.put( | |
145 { title: 'Bedrock Nights ' + expectedKey, author: 'Barney' }); | |
146 const eventWatcher = requestWatcher(testCase, request); | |
147 return eventWatcher.wait_for('success').then(() => { | |
148 const result = request.result; | |
149 assert_equals(result, expectedKey, errorMessage); | |
150 }); | |
151 }; | |
152 | |
153 // Verifies that an object store's contents matches the contents used to create | |
154 // the books store in the test database's version 1. | |
155 // | |
156 // The errorMessage is used if the assertions fail. It can state that the | |
157 // IndexedDB implementation being tested is incorrect, or that the testing code | |
158 // is using it incorrectly. | |
159 const checkStoreContents = (testCase, store, errorMessage) => { | |
160 const request = store.get(123456); | |
161 const eventWatcher = requestWatcher(testCase, request); | |
162 return eventWatcher.wait_for('success').then(() => { | |
163 const result = request.result; | |
164 assert_equals(result.isbn, BOOKS_RECORD_DATA[0].isbn, errorMessage); | |
165 assert_equals(result.author, BOOKS_RECORD_DATA[0].author, errorMessage); | |
166 assert_equals(result.title, BOOKS_RECORD_DATA[0].title, errorMessage); | |
167 }); | |
168 }; | |
169 | |
170 // Verifies that index matches the 'by_author' index used to create the | |
171 // by_author books store in the test database's version 1. | |
172 // | |
173 // The errorMessage is used if the assertions fail. It can state that the | |
174 // IndexedDB implementation being tested is incorrect, or that the testing code | |
175 // is using it incorrectly. | |
176 const checkAuthorIndexContents = (testCase, index, errorMessage) => { | |
177 const request = index.get(BOOKS_RECORD_DATA[2].author); | |
178 const eventWatcher = requestWatcher(testCase, request); | |
179 return eventWatcher.wait_for('success').then(() => { | |
180 const result = request.result; | |
181 assert_equals(result.isbn, BOOKS_RECORD_DATA[2].isbn, errorMessage); | |
182 assert_equals(result.title, BOOKS_RECORD_DATA[2].title, errorMessage); | |
183 }); | |
184 }; | |
185 | |
186 // Verifies that an index matches the 'by_title' index used to create the books | |
187 // store in the test database's version 1. | |
188 // | |
189 // The errorMessage is used if the assertions fail. It can state that the | |
190 // IndexedDB implementation being tested is incorrect, or that the testing code | |
191 // is using it incorrectly. | |
192 const checkTitleIndexContents = (testCase, index, errorMessage) => { | |
193 const request = index.get(BOOKS_RECORD_DATA[2].title); | |
194 const eventWatcher = requestWatcher(testCase, request); | |
195 return eventWatcher.wait_for('success').then(() => { | |
196 const result = request.result; | |
197 assert_equals(result.isbn, BOOKS_RECORD_DATA[2].isbn, errorMessage); | |
198 assert_equals(result.author, BOOKS_RECORD_DATA[2].author, errorMessage); | |
199 }); | |
200 }; | |
OLD | NEW |