|  | // META: title=IndexedDB: scoping for database / object store / index names, and index keys | 
|  | // META: global=window,worker | 
|  | // META: script=resources/support-promises.js | 
|  |  | 
|  | // Spec: https://w3c.github.io/IndexedDB/#constructs | 
|  |  | 
|  | 'use strict'; | 
|  |  | 
|  | // Creates the structure inside a test database. | 
|  | // | 
|  | // The structure includes two stores with identical indexes and nearly-similar | 
|  | // records. The records differ in the "path" attribute values, which are used to | 
|  | // verify that IndexedDB returns the correct records when queried. | 
|  | // | 
|  | // databaseName appears redundant, but we don't want to rely on database.name. | 
|  | const buildStores = (database, databaseName, useUniqueKeys) => { | 
|  | for (let storeName of ['x', 'y']) { | 
|  | const store = database.createObjectStore( | 
|  | storeName, {keyPath: 'pKey', autoIncrement: true}); | 
|  | for (let indexName of ['x', 'y']) { | 
|  | store.createIndex(indexName, `${indexName}Key`, {unique: useUniqueKeys}); | 
|  | } | 
|  |  | 
|  | for (let xKeyRoot of ['x', 'y']) { | 
|  | for (let yKeyRoot of ['x', 'y']) { | 
|  | let xKey, yKey; | 
|  | if (useUniqueKeys) { | 
|  | xKey = `${xKeyRoot}${yKeyRoot}`; | 
|  | yKey = `${yKeyRoot}${xKeyRoot}`; | 
|  | } else { | 
|  | xKey = xKeyRoot; | 
|  | yKey = yKeyRoot; | 
|  | } | 
|  | const path = `${databaseName}-${storeName}-${xKeyRoot}-${yKeyRoot}`; | 
|  | store.put({xKey: xKey, yKey: yKey, path: path}); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Creates two databases with identical structures. | 
|  | const buildDatabases = (testCase, useUniqueKeys) => { | 
|  | return createNamedDatabase( | 
|  | testCase, 'x', | 
|  | database => buildStores(database, 'x', useUniqueKeys)) | 
|  | .then(database => database.close()) | 
|  | .then( | 
|  | () => createNamedDatabase( | 
|  | testCase, 'y', | 
|  | database => buildStores(database, 'y', useUniqueKeys))) | 
|  | .then(database => database.close()); | 
|  | }; | 
|  |  | 
|  | // Reads all the store's values using an index. | 
|  | // | 
|  | // Returns a Promise that resolves with an array of values. | 
|  | const readIndex = | 
|  | (testCase, index) => { | 
|  | return new Promise((resolve, reject) => { | 
|  | const results = []; | 
|  | const request = index.openCursor(IDBKeyRange.bound('a', 'z'), 'next'); | 
|  | request.onsuccess = testCase.step_func(() => { | 
|  | const cursor = request.result; | 
|  | if (cursor) { | 
|  | results.push(cursor.value); | 
|  | cursor.continue(); | 
|  | } else { | 
|  | resolve(results); | 
|  | } | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Verifies that a database contains the expected records. | 
|  | const checkDatabaseContent = | 
|  | (testCase, database, databaseName, usedUniqueKeys) => { | 
|  | const promises = []; | 
|  | const transaction = database.transaction(['x', 'y'], 'readonly'); | 
|  | for (let storeName of ['x', 'y']) { | 
|  | const store = transaction.objectStore(storeName); | 
|  | for (let indexName of ['x', 'y']) { | 
|  | const index = store.index(indexName); | 
|  |  | 
|  | const promise = readIndex(testCase, index).then((results) => { | 
|  | assert_array_equals( | 
|  | results.map(result => `${result.path}:${result.pKey}`).sort(), | 
|  | [ | 
|  | `${databaseName}-${storeName}-x-x:1`, | 
|  | `${databaseName}-${storeName}-x-y:2`, | 
|  | `${databaseName}-${storeName}-y-x:3`, | 
|  | `${databaseName}-${storeName}-y-y:4` | 
|  | ], | 
|  | 'The results should include all records put into the store'); | 
|  |  | 
|  | let expectedKeys = (usedUniqueKeys) ? | 
|  | ['xx:xx', 'xy:yx', 'yx:xy', 'yy:yy'] : | 
|  | ['x:x', 'x:y', 'y:x', 'y:y']; | 
|  | assert_array_equals( | 
|  | results.map(result => `${result.xKey}:${result.yKey}`).sort(), | 
|  | expectedKeys, | 
|  | 'The results should include all the index keys put in the store'); | 
|  |  | 
|  | assert_array_equals( | 
|  | results.map(result => result[`${indexName}Key`]), | 
|  | results.map(result => result[`${indexName}Key`]).sort(), | 
|  | 'The results should be sorted by the index key'); | 
|  | }); | 
|  | promises.push(promise); | 
|  | } | 
|  | } | 
|  |  | 
|  | return Promise.all(promises).then(() => database); | 
|  | } | 
|  |  | 
|  | promise_test(testCase => { | 
|  | return buildDatabases(testCase, false) | 
|  | .then(() => openNamedDatabase(testCase, 'x', 1)) | 
|  | .then(database => checkDatabaseContent(testCase, database, 'x', false)) | 
|  | .then(database => database.close()) | 
|  | .then(() => openNamedDatabase(testCase, 'y', 1)) | 
|  | .then(database => checkDatabaseContent(testCase, database, 'y', false)) | 
|  | .then(database => database.close()); | 
|  | }, 'Non-unique index keys'); | 
|  |  | 
|  | promise_test(testCase => { | 
|  | return buildDatabases(testCase, true) | 
|  | .then(() => openNamedDatabase(testCase, 'x', 1)) | 
|  | .then(database => checkDatabaseContent(testCase, database, 'x', true)) | 
|  | .then(database => database.close()) | 
|  | .then(() => openNamedDatabase(testCase, 'y', 1)) | 
|  | .then(database => checkDatabaseContent(testCase, database, 'y', true)) | 
|  | .then(database => database.close()); | 
|  | }, 'Unique index keys'); |