idbobjectstore_createIndex.any.js (20089B)
1 // META: title=IDBObjectStore.createIndex() 2 // META: global=window,worker 3 // META: script=resources/support.js 4 5 'use strict'; 6 7 async_test(t => { 8 let db; 9 10 let open_rq = createdb(t); 11 open_rq.onupgradeneeded = function (e) { 12 db = e.target.result; 13 let objStore = db.createObjectStore("store"); 14 let index = objStore.createIndex("index", "indexedProperty", { unique: true }); 15 16 assert_true(index instanceof IDBIndex, "IDBIndex"); 17 assert_equals(index.name, "index", "name"); 18 assert_equals(index.objectStore, objStore, "objectStore"); 19 assert_equals(index.keyPath, "indexedProperty", "keyPath"); 20 assert_true(index.unique, "unique"); 21 assert_false(index.multiEntry, "multiEntry"); 22 23 t.done(); 24 }; 25 }, "Returns an IDBIndex and the properties are set correctly"); 26 27 async_test(t => { 28 let db, aborted, 29 record = { indexedProperty: "bar" }; 30 31 let open_rq = createdb(t); 32 open_rq.onupgradeneeded = function (e) { 33 db = e.target.result; 34 let txn = e.target.transaction, 35 objStore = db.createObjectStore("store"); 36 37 objStore.add(record, 1); 38 objStore.add(record, 2); 39 let index = objStore.createIndex("index", "indexedProperty", { unique: true }); 40 41 assert_true(index instanceof IDBIndex, "IDBIndex"); 42 43 e.target.transaction.onabort = t.step_func(function (e) { 44 aborted = true; 45 assert_equals(e.type, "abort", "event type"); 46 }); 47 48 db.onabort = function (e) { 49 assert_true(aborted, "transaction.abort event has fired"); 50 t.done(); 51 }; 52 53 e.target.transaction.oncomplete = fail(t, "got complete, expected abort"); 54 }; 55 }, "Attempt to create an index that requires unique values on an object store already contains duplicates"); 56 57 async_test(t => { 58 let db, aborted; 59 60 let open_rq = createdb(t); 61 open_rq.onupgradeneeded = function (e) { 62 db = e.target.result; 63 let txn = e.target.transaction, 64 objStore = db.createObjectStore("store", { keyPath: 'key' }); 65 66 for (let i = 0; i < 100; i++) 67 objStore.add({ key: "key_" + i, indexedProperty: "indexed_" + i }); 68 69 let idx = objStore.createIndex("index", "indexedProperty") 70 71 idx.get('indexed_99').onsuccess = t.step_func(function (e) { 72 assert_equals(e.target.result.key, 'key_99', 'key'); 73 }); 74 idx.get('indexed_9').onsuccess = t.step_func(function (e) { 75 assert_equals(e.target.result.key, 'key_9', 'key'); 76 }); 77 } 78 79 open_rq.onsuccess = function () { 80 t.done(); 81 } 82 }, "The index is usable right after being made"); 83 84 async_test(t => { 85 let db, 86 events = []; 87 88 let open_rq = createdb(t); 89 open_rq.onupgradeneeded = function (e) { 90 db = e.target.result; 91 e.target.transaction.oncomplete = log("transaction.complete"); 92 93 let txn = e.target.transaction, 94 objStore = db.createObjectStore("store"); 95 96 let rq_add1 = objStore.add({ animal: "Unicorn" }, 1); 97 rq_add1.onsuccess = log("rq_add1.success"); 98 rq_add1.onerror = log("rq_add1.error"); 99 100 objStore.createIndex("index", "animal", { unique: true }); 101 102 let rq_add2 = objStore.add({ animal: "Unicorn" }, 2); 103 rq_add2.onsuccess = log("rq_add2.success"); 104 rq_add2.onerror = function (e) { 105 log("rq_add2.error")(e); 106 e.preventDefault(); 107 e.stopPropagation(); 108 } 109 110 objStore.deleteIndex("index"); 111 112 let rq_add3 = objStore.add({ animal: "Unicorn" }, 3); 113 rq_add3.onsuccess = log("rq_add3.success"); 114 rq_add3.onerror = log("rq_add3.error"); 115 } 116 117 open_rq.onsuccess = function (e) { 118 log("open_rq.success")(e); 119 assert_array_equals(events, ["rq_add1.success", 120 "rq_add2.error: ConstraintError", 121 "rq_add3.success", 122 123 "transaction.complete", 124 125 "open_rq.success"], 126 "events"); 127 t.done(); 128 } 129 130 function log(msg) { 131 return function (e) { 132 if (e && e.target && e.target.error) 133 events.push(msg + ": " + e.target.error.name); 134 else 135 events.push(msg); 136 }; 137 } 138 }, "Event ordering for a later deleted index"); 139 140 async_test(t => { 141 let db, aborted; 142 143 let open_rq = createdb(t); 144 open_rq.onupgradeneeded = function (e) { 145 db = e.target.result; 146 let txn = e.target.transaction, 147 objStore = db.createObjectStore("store"); 148 149 for (let i = 0; i < 5; i++) 150 objStore.add("object_" + i, i); 151 152 let rq = objStore.createIndex("index", "") 153 rq.onerror = function () { assert_unreached("error: " + rq.error.name); } 154 rq.onsuccess = function () { } 155 156 objStore.index("index") 157 .get('object_4') 158 .onsuccess = t.step_func(function (e) { 159 assert_equals(e.target.result, 'object_4', 'result'); 160 }); 161 } 162 163 open_rq.onsuccess = function () { 164 t.done(); 165 } 166 }, "Empty keyPath"); 167 168 async_test(t => { 169 // Transaction may fire window.onerror in some implementations. 170 setup({ allow_uncaught_exception: true }); 171 172 let db, 173 events = []; 174 175 let open_rq = createdb(t); 176 open_rq.onupgradeneeded = function (e) { 177 db = e.target.result; 178 db.onerror = log("db.error"); 179 db.onabort = log("db.abort"); 180 e.target.transaction.onabort = log("transaction.abort") 181 e.target.transaction.onerror = log("transaction.error") 182 e.target.transaction.oncomplete = log("transaction.complete") 183 184 let txn = e.target.transaction, 185 objStore = db.createObjectStore("store"); 186 187 let rq_add1 = objStore.add({ animal: "Unicorn" }, 1); 188 rq_add1.onsuccess = log("rq_add1.success"); 189 rq_add1.onerror = log("rq_add1.error"); 190 191 let rq_add2 = objStore.add({ animal: "Unicorn" }, 2); 192 rq_add2.onsuccess = log("rq_add2.success"); 193 rq_add2.onerror = log("rq_add2.error"); 194 195 objStore.createIndex("index", "animal", { unique: true }) 196 197 let rq_add3 = objStore.add({ animal: "Unicorn" }, 3); 198 rq_add3.onsuccess = log("rq_add3.success"); 199 rq_add3.onerror = log("rq_add3.error"); 200 } 201 202 open_rq.onerror = function (e) { 203 log("open_rq.error")(e); 204 assert_array_equals(events, ["rq_add1.success", 205 "rq_add2.success", 206 207 "rq_add3.error: AbortError", 208 "transaction.error: AbortError", 209 "db.error: AbortError", 210 211 "transaction.abort: ConstraintError", 212 "db.abort: ConstraintError", 213 214 "open_rq.error: AbortError"], 215 "events"); 216 t.done(); 217 } 218 219 function log(msg) { 220 return function (e) { 221 if (e && e.target && e.target.error) 222 events.push(msg + ": " + e.target.error.name); 223 else 224 events.push(msg); 225 }; 226 } 227 }, "Event order when unique constraint is triggered"); 228 229 async_test(t => { 230 setup({ allow_uncaught_exception: true }); 231 232 let db, 233 events = []; 234 235 const open_rq = createdb(t); 236 open_rq.onupgradeneeded = function (e) { 237 db = e.target.result; 238 let txn = e.target.transaction; 239 db.onerror = log("db.error"); 240 db.onabort = log("db.abort"); 241 txn.onabort = log("transaction.abort") 242 txn.onerror = log("transaction.error") 243 txn.oncomplete = log("transaction.complete") 244 245 let objStore = db.createObjectStore("store"); 246 247 let rq_add1 = objStore.add({ animal: "Unicorn" }, 1); 248 rq_add1.onsuccess = log("rq_add1.success"); 249 rq_add1.onerror = log("rq_add1.error"); 250 251 objStore.createIndex("index", "animal", { unique: true }) 252 253 let rq_add2 = objStore.add({ animal: "Unicorn" }, 2); 254 rq_add2.onsuccess = log("rq_add2.success"); 255 rq_add2.onerror = log("rq_add2.error"); 256 257 let rq_add3 = objStore.add({ animal: "Horse" }, 3); 258 rq_add3.onsuccess = log("rq_add3.success"); 259 rq_add3.onerror = log("rq_add3.error"); 260 } 261 262 open_rq.onerror = function (e) { 263 log("open_rq.error")(e); 264 assert_array_equals(events, ["rq_add1.success", 265 266 "rq_add2.error: ConstraintError", 267 "transaction.error: ConstraintError", 268 "db.error: ConstraintError", 269 270 "rq_add3.error: AbortError", 271 "transaction.error: AbortError", 272 "db.error: AbortError", 273 274 "transaction.abort: ConstraintError", 275 "db.abort: ConstraintError", 276 277 "open_rq.error: AbortError"], 278 "events"); 279 t.done(); 280 } 281 282 function log(msg) { 283 return function (e) { 284 if (e && e.target && e.target.error) 285 events.push(msg + ": " + e.target.error.name); 286 else 287 events.push(msg); 288 }; 289 } 290 }, "Event ordering for ConstraintError on request"); 291 292 async_test(t => { 293 let db, 294 now = new Date(), 295 mar18 = new Date(1111111111111), 296 ar = ["Yay", 2, -Infinity], 297 num = 1337; 298 299 const open_rq = createdb(t); 300 open_rq.onupgradeneeded = function (e) { 301 db = e.target.result; 302 let txn = e.target.transaction, 303 objStore = db.createObjectStore("store", { keyPath: 'key' }); 304 305 objStore.add({ key: "now", i: now }); 306 objStore.add({ key: "mar18", i: mar18 }); 307 objStore.add({ key: "array", i: ar }); 308 objStore.add({ key: "number", i: num }); 309 310 let idx = objStore.createIndex("index", "i") 311 312 idx.get(now).onsuccess = t.step_func(function (e) { 313 assert_equals(e.target.result.key, 'now', 'key'); 314 assert_equals(e.target.result.i.getTime(), now.getTime(), 'getTime'); 315 }); 316 idx.get(mar18).onsuccess = t.step_func(function (e) { 317 assert_equals(e.target.result.key, 'mar18', 'key'); 318 assert_equals(e.target.result.i.getTime(), mar18.getTime(), 'getTime'); 319 }); 320 idx.get(ar).onsuccess = t.step_func(function (e) { 321 assert_equals(e.target.result.key, 'array', 'key'); 322 assert_array_equals(e.target.result.i, ar, 'array is the same'); 323 }); 324 idx.get(num).onsuccess = t.step_func(function (e) { 325 assert_equals(e.target.result.key, 'number', 'key'); 326 assert_equals(e.target.result.i, num, 'number is the same'); 327 }); 328 } 329 330 open_rq.onsuccess = function () { 331 t.done(); 332 } 333 }, "Index can be valid keys"); 334 335 async_test(t => { 336 let db; 337 338 const open_rq = createdb(t); 339 open_rq.onupgradeneeded = function (e) { 340 db = e.target.result 341 let store = db.createObjectStore("store") 342 343 for (let i = 0; i < 5; i++) 344 store.add({ idx: "object_" + i }, i) 345 346 store.createIndex("", "idx") 347 348 store.index("") 349 .get('object_4') 350 .onsuccess = t.step_func(function (e) { 351 assert_equals(e.target.result.idx, 'object_4', 'result') 352 }) 353 assert_equals(store.indexNames[0], "", "indexNames[0]") 354 assert_equals(store.indexNames.length, 1, "indexNames.length") 355 } 356 357 open_rq.onsuccess = function () { 358 let store = db.transaction("store", "readonly").objectStore("store") 359 360 assert_equals(store.indexNames[0], "", "indexNames[0]") 361 assert_equals(store.indexNames.length, 1, "indexNames.length") 362 363 t.done() 364 } 365 }, "IDBObjectStore.createIndex() - empty name"); 366 367 async_test(t => { 368 const open_rq = createdb(t); 369 370 open_rq.onupgradeneeded = function (e) { 371 let db = e.target.result; 372 let ostore = db.createObjectStore("store"); 373 ostore.createIndex("a", "a"); 374 assert_throws_dom("ConstraintError", function () { 375 ostore.createIndex("a", "a"); 376 }); 377 t.done(); 378 } 379 }, "If an index with the name name already exists in this object store, the implementation must throw a DOMException of type ConstraintError"); 380 381 async_test(t => { 382 const open_rq = createdb(t); 383 384 open_rq.onupgradeneeded = function (e) { 385 let db = e.target.result; 386 let ostore = db.createObjectStore("store"); 387 assert_throws_dom("SyntaxError", function () { 388 ostore.createIndex("ab", "."); 389 }); 390 t.done(); 391 } 392 }, "If keyPath is not a valid key path, the implementation must throw a DOMException of type SyntaxError"); 393 394 async_test(t => { 395 let db, ostore; 396 397 let open_rq = createdb(t); 398 open_rq.onupgradeneeded = function (event) { 399 db = event.target.result; 400 ostore = db.createObjectStore("store"); 401 db.deleteObjectStore("store"); 402 } 403 404 open_rq.onsuccess = function (event) { 405 t.step(function () { 406 assert_throws_dom("InvalidStateError", function () { 407 ostore.createIndex("index", "indexedProperty"); 408 }); 409 }); 410 t.done(); 411 } 412 }, "If the object store has been deleted, the implementation must throw a DOMException of type InvalidStateError"); 413 414 async_test(t => { 415 let db; 416 417 const open_rq = createdb(t); 418 open_rq.onupgradeneeded = function (event) { 419 db = event.target.result; 420 db.createObjectStore("store"); 421 } 422 423 open_rq.onsuccess = function (event) { 424 let txn = db.transaction("store", "readwrite"); 425 let ostore = txn.objectStore("store"); 426 t.step(function () { 427 assert_throws_dom("InvalidStateError", function () { 428 ostore.createIndex("index", "indexedProperty"); 429 }); 430 }); 431 t.done(); 432 } 433 }, "Operate out versionchange throw InvalidStateError"); 434 435 /* IndexedDB: Exception Order of IDBObjectStore.createIndex() */ 436 indexeddb_test( 437 function (t, db, txn) { 438 let store = db.createObjectStore("s"); 439 }, 440 function (t, db) { 441 let txn = db.transaction("s", "readonly"); 442 let store = txn.objectStore("s"); 443 txn.oncomplete = function () { 444 assert_throws_dom("InvalidStateError", function () { 445 store.createIndex("index", "foo"); 446 }); 447 t.done(); 448 }; 449 }, 450 "InvalidStateError(Incorrect mode) vs. TransactionInactiveError. Mode check should precede state check of the transaction." 451 ); 452 453 let gDeletedObjectStore; 454 indexeddb_test( 455 function (t, db, txn) { 456 gDeletedObjectStore = db.createObjectStore("s"); 457 db.deleteObjectStore("s"); 458 txn.oncomplete = function () { 459 assert_throws_dom("InvalidStateError", function () { 460 gDeletedObjectStore.createIndex("index", "foo"); 461 }); 462 t.done(); 463 }; 464 }, 465 null, 466 "InvalidStateError(Deleted ObjectStore) vs. TransactionInactiveError. Deletion check should precede transaction-state check." 467 ); 468 469 indexeddb_test( 470 function (t, db, txn) { 471 let store = db.createObjectStore("s"); 472 store.createIndex("index", "foo"); 473 txn.oncomplete = function () { 474 assert_throws_dom("TransactionInactiveError", function () { 475 store.createIndex("index", "foo"); 476 }); 477 t.done(); 478 }; 479 }, 480 null, 481 "TransactionInactiveError vs. ConstraintError. Transaction-state check should precede index name check." 482 ); 483 484 indexeddb_test( 485 function (t, db) { 486 let store = db.createObjectStore("s"); 487 store.createIndex("index", "foo"); 488 assert_throws_dom("ConstraintError", function () { 489 store.createIndex("index", "invalid key path"); 490 }); 491 assert_throws_dom("ConstraintError", function () { 492 store.createIndex("index", 493 ["invalid key path 1", "invalid key path 2"]); 494 }); 495 t.done(); 496 }, 497 null, 498 "ConstraintError vs. SyntaxError. Index name check should precede syntax check of the key path" 499 ); 500 501 indexeddb_test( 502 function (t, db) { 503 let store = db.createObjectStore("s"); 504 assert_throws_dom("SyntaxError", function () { 505 store.createIndex("index", 506 ["invalid key path 1", "invalid key path 2"], 507 { multiEntry: true }); 508 }); 509 t.done(); 510 }, 511 null, 512 "SyntaxError vs. InvalidAccessError. Syntax check should precede multiEntry check of the key path." 513 ); 514 515 /* AutoIncrement in Compound Index */ 516 indexeddb_test( 517 function (t, db, txn) { 518 // No auto-increment 519 let store = db.createObjectStore("Store1", { keyPath: "id" }); 520 store.createIndex("CompoundKey", ["num", "id"]); 521 522 // Add data 523 store.put({ id: 1, num: 100 }); 524 }, 525 function (t, db) { 526 let store = db.transaction("Store1", "readwrite").objectStore("Store1"); 527 528 store.openCursor().onsuccess = t.step_func(function (e) { 529 let item = e.target.result.value; 530 store.index("CompoundKey").get([item.num, item.id]).onsuccess = t.step_func(function (e) { 531 assert_equals(e.target.result ? e.target.result.num : null, 100, 'Expected 100.'); 532 t.done(); 533 }); 534 }); 535 }, 536 "Explicit Primary Key" 537 ); 538 539 indexeddb_test( 540 function (t, db, txn) { 541 // Auto-increment 542 let store = db.createObjectStore("Store2", { keyPath: "id", autoIncrement: true }); 543 store.createIndex("CompoundKey", ["num", "id"]); 544 545 // Add data 546 store.put({ num: 100 }); 547 }, 548 function (t, db) { 549 let store = db.transaction("Store2", "readwrite").objectStore("Store2"); 550 store.openCursor().onsuccess = t.step_func(function (e) { 551 let item = e.target.result.value; 552 store.index("CompoundKey").get([item.num, item.id]).onsuccess = t.step_func(function (e) { 553 assert_equals(e.target.result ? e.target.result.num : null, 100, 'Expected 100.'); 554 t.done(); 555 }); 556 }); 557 }, 558 "Auto-Increment Primary Key" 559 ); 560 561 indexeddb_test( 562 function (t, db, txn) { 563 // Auto-increment 564 let store = db.createObjectStore("Store3", { keyPath: "id", autoIncrement: true }); 565 store.createIndex("CompoundKey", ["num", "id", "other"]); 566 567 let num = 100; 568 569 // Add data to Store3 - valid keys 570 // Objects will be stored in Store3 and keys will get added 571 // to the CompoundKeys index. 572 store.put({ num: num++, other: 0 }); 573 store.put({ num: num++, other: [0] }); 574 575 // Add data - missing key 576 // Objects will be stored in Store3 but keys won't get added to 577 // the CompoundKeys index because the 'other' keypath doesn't 578 // resolve to a value. 579 store.put({ num: num++ }); 580 581 // Add data to Store3 - invalid keys 582 // Objects will be stored in Store3 but keys won't get added to 583 // the CompoundKeys index because the 'other' property values 584 // aren't valid keys. 585 store.put({ num: num++, other: null }); 586 store.put({ num: num++, other: {} }); 587 store.put({ num: num++, other: [null] }); 588 store.put({ num: num++, other: [{}] }); 589 }, 590 function (t, db) { 591 let store = db.transaction("Store3", "readwrite").objectStore("Store3"); 592 const keys = []; 593 let count; 594 store.count().onsuccess = t.step_func(e => { count = e.target.result; }); 595 store.index("CompoundKey").openCursor().onsuccess = t.step_func(function (e) { 596 const cursor = e.target.result; 597 if (cursor !== null) { 598 keys.push(cursor.key); 599 cursor.continue(); 600 return; 601 } 602 603 // Done iteration, check results. 604 assert_equals(count, 7, 'Expected all 7 records to be stored.'); 605 assert_equals(keys.length, 2, 'Expected exactly two index entries.'); 606 assert_array_equals(keys[0], [100, 1, 0]); 607 assert_object_equals(keys[1], [101, 2, [0]]); 608 t.done(); 609 }); 610 }, 611 "Auto-Increment Primary Key - invalid key values elsewhere" 612 );