| 'use strict'; |
| |
| // Should be large enough to trigger large value handling in the IndexedDB |
| // engines that have special code paths for large values. |
| const wrapThreshold = 128 * 1024; |
| |
| // Returns an IndexedDB value created from a descriptor. |
| // |
| // See the bottom of the file for descriptor samples. |
| function createValue(descriptor) { |
| if (typeof(descriptor) != 'object') |
| return descriptor; |
| |
| if (Array.isArray(descriptor)) |
| return descriptor.map((element) => createValue(element)); |
| |
| if (!descriptor.hasOwnProperty('type')) { |
| const value = {}; |
| for (let property of Object.getOwnPropertyNames(descriptor)) |
| value[property] = createValue(descriptor[property]); |
| return value; |
| } |
| |
| switch (descriptor.type) { |
| case 'blob': |
| return new Blob( |
| [largeValue(descriptor.size, descriptor.seed)], |
| { type: descriptor.mimeType }); |
| case 'buffer': |
| return largeValue(descriptor.size, descriptor.seed); |
| } |
| } |
| |
| // Checks an IndexedDB value against a descriptor. |
| // |
| // Returns a Promise that resolves if the value passes the check. |
| // |
| // See the bottom of the file for descriptor samples. |
| function checkValue(testCase, value, descriptor) { |
| if (typeof(descriptor) != 'object') { |
| assert_equals( |
| descriptor, value, |
| 'IndexedDB result should match put() argument'); |
| return Promise.resolve(); |
| } |
| |
| if (Array.isArray(descriptor)) { |
| assert_true( |
| Array.isArray(value), |
| 'IndexedDB result type should match put() argument'); |
| assert_equals( |
| descriptor.length, value.length, |
| 'IndexedDB result array size should match put() argument'); |
| |
| const subChecks = []; |
| for (let i = 0; i < descriptor.length; ++i) |
| subChecks.push(checkValue(testCase, value[i], descriptor[i])); |
| return Promise.all(subChecks); |
| } |
| |
| if (!descriptor.hasOwnProperty('type')) { |
| assert_array_equals( |
| Object.getOwnPropertyNames(value).sort(), |
| Object.getOwnPropertyNames(descriptor).sort(), |
| 'IndexedDB result object properties should match put() argument'); |
| const subChecks = []; |
| return Promise.all(Object.getOwnPropertyNames(descriptor).map(property => |
| checkValue(testCase, value[property], descriptor[property]))); |
| } |
| |
| switch (descriptor.type) { |
| case 'blob': |
| assert_class_string( |
| value, 'Blob', |
| 'IndexedDB result class should match put() argument'); |
| assert_equals( |
| descriptor.mimeType, value.type, |
| 'IndexedDB result Blob MIME type should match put() argument'); |
| assert_equals(descriptor.size, value.size, 'incorrect Blob size'); |
| return new Promise((resolve, reject) => { |
| const reader = new FileReader(); |
| reader.onloadend = testCase.step_func(() => { |
| if (reader.error) { |
| reject(reader.error); |
| return; |
| } |
| const view = new Uint8Array(reader.result); |
| assert_equals( |
| view.join(','), |
| largeValue(descriptor.size, descriptor.seed).join(','), |
| 'IndexedDB result Blob content should match put() argument'); |
| resolve(); |
| }); |
| reader.readAsArrayBuffer(value); |
| }); |
| |
| case 'buffer': |
| assert_class_string( |
| value, 'Uint8Array', |
| 'IndexedDB result type should match put() argument'); |
| assert_equals( |
| value.join(','), |
| largeValue(descriptor.size, descriptor.seed).join(','), |
| 'IndexedDB result typed array content should match put() argument'); |
| return Promise.resolve(); |
| } |
| } |
| |
| function cloningTestInternal(label, valueDescriptors, options) { |
| promise_test(testCase => { |
| return createDatabase(testCase, (database, transaction) => { |
| let store; |
| if (options.useKeyGenerator) { |
| store = database.createObjectStore( |
| 'test-store', { keyPath: 'primaryKey', autoIncrement: true }); |
| } else { |
| store = database.createObjectStore('test-store'); |
| } |
| for (let i = 0; i < valueDescriptors.length; ++i) { |
| if (options.useKeyGenerator) { |
| store.put(createValue(valueDescriptors[i])); |
| } else { |
| store.put(createValue(valueDescriptors[i]), i + 1); |
| } |
| } |
| }).then(database => { |
| const transaction = database.transaction(['test-store'], 'readonly'); |
| const store = transaction.objectStore('test-store'); |
| const subChecks = []; |
| let resultIndex = 0; |
| for (let i = 0; i < valueDescriptors.length; ++i) { |
| subChecks.push(new Promise((resolve, reject) => { |
| const requestIndex = i; |
| const primaryKey = requestIndex + 1; |
| const request = store.get(primaryKey); |
| request.onerror = |
| testCase.step_func(() => { reject(request.error); }); |
| request.onsuccess = testCase.step_func(() => { |
| assert_equals( |
| resultIndex, requestIndex, |
| 'IDBRequest success events should be fired in request order'); |
| ++resultIndex; |
| |
| const result = request.result; |
| if (options.useKeyGenerator) { |
| assert_equals( |
| result.primaryKey, primaryKey, |
| 'IndexedDB result should have auto-incremented primary key'); |
| delete result.primaryKey; |
| } |
| resolve(checkValue( |
| testCase, result, valueDescriptors[requestIndex])); |
| }); |
| })); |
| } |
| |
| subChecks.push(new Promise((resolve, reject) => { |
| const requestIndex = valueDescriptors.length; |
| const request = store.getAll(); |
| request.onerror = |
| testCase.step_func(() => { reject(request.error); }); |
| request.onsuccess = testCase.step_func(() => { |
| assert_equals( |
| resultIndex, requestIndex, |
| 'IDBRequest success events should be fired in request order'); |
| ++resultIndex; |
| const result = request.result; |
| if (options.useKeyGenerator) { |
| for (let i = 0; i < valueDescriptors.length; ++i) { |
| const primaryKey = i + 1; |
| assert_equals( |
| result[i].primaryKey, primaryKey, |
| 'IndexedDB result should have auto-incremented primary key'); |
| delete result[i].primaryKey; |
| } |
| } |
| resolve(checkValue(testCase, result, valueDescriptors)); |
| }); |
| })); |
| |
| return Promise.all(subChecks); |
| }); |
| }, label); |
| } |
| |
| // Performs a series of put()s and verifies that get()s and getAll() match. |
| // |
| // Each element of the valueDescriptors array is fed into createValue(), and the |
| // resulting value is written to IndexedDB via a put() request. After the writes |
| // complete, the values are read in the same order in which they were written. |
| // Last, all the results are read one more time via a getAll(). |
| // |
| // The test verifies that the get() / getAll() results match the arguments to |
| // put() and that the order in which the get() result events are fired matches |
| // the order of the get() requests. |
| function cloningTest(label, valueDescriptors) { |
| cloningTestInternal(label, valueDescriptors, { useKeyGenerator: false }); |
| } |
| |
| // cloningTest, with coverage for key generators. |
| // |
| // This creates two tests. One test performs a series of put()s and verifies |
| // that get()s and getAll() match, exactly like cloningTestWithoutKeyGenerator. |
| // The other test performs the same put()s in an object store with a key |
| // generator, and checks that the key generator works properly. |
| function cloningTestWithKeyGenerator(label, valueDescriptors) { |
| cloningTestInternal(label, valueDescriptors, { useKeyGenerator: false }); |
| cloningTestInternal( |
| label + " with key generator", valueDescriptors, |
| { useKeyGenerator: true }); |
| } |