test_addressRecords.js (12828B)
1 /** 2 * Tests FormAutofillStorage object with addresses records. 3 */ 4 5 "use strict"; 6 7 const TEST_STORE_FILE_NAME = "test-profile.json"; 8 const COLLECTION_NAME = "addresses"; 9 10 const TEST_ADDRESS_1 = { 11 name: "Timothy John Berners-Lee", 12 organization: "World Wide Web Consortium", 13 "street-address": "32 Vassar Street\nMIT Room 32-G524", 14 "address-level2": "Cambridge", 15 "address-level1": "MA", 16 "postal-code": "02139", 17 country: "US", 18 tel: "+16172535702", 19 email: "timbl@w3.org", 20 "unknown-1": "an unknown field from another client", 21 }; 22 23 const TEST_ADDRESS_2 = { 24 "street-address": "Some Address", 25 country: "US", 26 }; 27 28 const TEST_ADDRESS_3 = { 29 name: "Timothy Berners-Lee", 30 "street-address": "Other Address", 31 "postal-code": "12345", 32 }; 33 34 const TEST_ADDRESS_4 = { 35 name: "Timothy Berners-Lee", 36 "street-address": "32 Vassar Street", 37 "postal-code": "12345", 38 email: "timbl@w3.org", 39 }; 40 41 const TEST_ADDRESS_WITH_EMPTY_FIELD = { 42 name: "Tim Berners", 43 "street-address": "", 44 }; 45 46 const TEST_ADDRESS_WITH_EMPTY_COMPUTED_FIELD = { 47 "given-name": "", 48 "additional-name": "", 49 "family-name": "", 50 "address-line1": "", 51 "address-line2": "", 52 "address-line3": "", 53 "country-name": "", 54 "tel-country-code": "", 55 "tel-national": "", 56 "tel-area-code": "", 57 "tel-local": "", 58 "tel-local-prefix": "", 59 "tel-local-suffix": "", 60 email: "timbl@w3.org", 61 }; 62 63 const TEST_ADDRESS_WITH_INVALID_FIELD = { 64 "street-address": "Another Address", 65 email: { email: "invalidemail" }, 66 }; 67 68 const TEST_ADDRESS_EMPTY_AFTER_NORMALIZE = { 69 country: "XXXXXX", 70 }; 71 72 ChromeUtils.defineESModuleGetters(this, { 73 Preferences: "resource://gre/modules/Preferences.sys.mjs", 74 }); 75 76 let do_check_record_matches = (recordWithMeta, record) => { 77 for (let key in record) { 78 Assert.equal(recordWithMeta[key], record[key]); 79 } 80 }; 81 82 add_task(async function test_initialize() { 83 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME); 84 85 Assert.equal(profileStorage._store.data.version, 1); 86 Assert.equal(profileStorage._store.data.addresses.length, 0); 87 88 let data = profileStorage._store.data; 89 Assert.deepEqual(data.addresses, []); 90 91 await profileStorage._saveImmediately(); 92 93 profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME); 94 95 Assert.deepEqual(profileStorage._store.data, data); 96 for (let { _sync } of profileStorage._store.data.addresses) { 97 Assert.ok(_sync); 98 Assert.equal(_sync.changeCounter, 1); 99 } 100 }); 101 102 add_task(async function test_getAll() { 103 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [ 104 TEST_ADDRESS_1, 105 TEST_ADDRESS_2, 106 TEST_ADDRESS_4, 107 ]); 108 109 let addresses = await profileStorage.addresses.getAll(); 110 111 Assert.equal(addresses.length, 3); 112 do_check_record_matches(addresses[0], TEST_ADDRESS_1); 113 do_check_record_matches(addresses[1], TEST_ADDRESS_2); 114 115 // Check computed fields. 116 Assert.equal(addresses[0]["given-name"], "Timothy"); 117 Assert.equal(addresses[0]["additional-name"], "John"); 118 Assert.equal(addresses[0]["family-name"], "Berners-Lee"); 119 Assert.equal(addresses[0]["address-line1"], "32 Vassar Street"); 120 Assert.equal(addresses[0]["address-line2"], "MIT Room 32-G524"); 121 // Until we resolve Bug 1964405 with computing house numbers from addresses containing newline characters, we'll test it using an additional profile. 122 // Assert.equal(addresses[0]["address-housenumber"], 32); <- This is what should be computed. 123 Assert.equal(addresses[2]["address-housenumber"], "32"); 124 125 // Test with rawData set. 126 addresses = await profileStorage.addresses.getAll({ rawData: true }); 127 // For backward-compatibility, we keep *-name fields when `rawData` is true 128 Assert.equal(addresses[0]["given-name"], "Timothy"); 129 Assert.equal(addresses[0]["additional-name"], "John"); 130 Assert.equal(addresses[0]["family-name"], "Berners-Lee"); 131 Assert.equal(addresses[0]["address-line1"], undefined); 132 Assert.equal(addresses[0]["address-line2"], undefined); 133 134 // Modifying output shouldn't affect the storage. 135 addresses[0].organization = "test"; 136 do_check_record_matches( 137 (await profileStorage.addresses.getAll())[0], 138 TEST_ADDRESS_1 139 ); 140 }); 141 142 add_task(async function test_get() { 143 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [ 144 TEST_ADDRESS_1, 145 TEST_ADDRESS_2, 146 ]); 147 148 let addresses = await profileStorage.addresses.getAll(); 149 let guid = addresses[0].guid; 150 151 let address = await profileStorage.addresses.get(guid); 152 do_check_record_matches(address, TEST_ADDRESS_1); 153 154 // Test with rawData set. 155 address = await profileStorage.addresses.get(guid, { rawData: true }); 156 // For backward-compatibility, we keep *-name fields when `rawData` is true 157 Assert.equal(address["given-name"], "Timothy"); 158 Assert.equal(address["additional-name"], "John"); 159 Assert.equal(address["family-name"], "Berners-Lee"); 160 Assert.equal(address["address-line1"], undefined); 161 Assert.equal(address["address-line2"], undefined); 162 163 // Modifying output shouldn't affect the storage. 164 address.organization = "test"; 165 do_check_record_matches( 166 await profileStorage.addresses.get(guid), 167 TEST_ADDRESS_1 168 ); 169 170 Assert.equal(await profileStorage.addresses.get("INVALID_GUID"), null); 171 }); 172 173 add_task(async function test_add() { 174 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [ 175 TEST_ADDRESS_1, 176 TEST_ADDRESS_2, 177 ]); 178 179 let addresses = await profileStorage.addresses.getAll(); 180 181 Assert.equal(addresses.length, 2); 182 183 do_check_record_matches(addresses[0], TEST_ADDRESS_1); 184 do_check_record_matches(addresses[1], TEST_ADDRESS_2); 185 186 Assert.notEqual(addresses[0].guid, undefined); 187 Assert.equal(addresses[0].version, 1); 188 Assert.notEqual(addresses[0].timeCreated, undefined); 189 Assert.equal(addresses[0].timeLastModified, addresses[0].timeCreated); 190 Assert.equal(addresses[0].timeLastUsed, 0); 191 Assert.equal(addresses[0].timesUsed, 0); 192 193 // Empty string should be deleted before saving. 194 await profileStorage.addresses.add(TEST_ADDRESS_WITH_EMPTY_FIELD); 195 let address = profileStorage.addresses._data[2]; 196 Assert.equal(address.name, TEST_ADDRESS_WITH_EMPTY_FIELD.name); 197 Assert.equal(address["street-address"], undefined); 198 199 // Empty computed fields shouldn't cause any problem. 200 await profileStorage.addresses.add(TEST_ADDRESS_WITH_EMPTY_COMPUTED_FIELD); 201 address = profileStorage.addresses._data[3]; 202 Assert.equal(address.email, TEST_ADDRESS_WITH_EMPTY_COMPUTED_FIELD.email); 203 204 await Assert.rejects( 205 profileStorage.addresses.add(TEST_ADDRESS_WITH_INVALID_FIELD), 206 /"email" contains invalid data type: object/ 207 ); 208 209 await Assert.rejects( 210 profileStorage.addresses.add({}), 211 /Record contains no valid field\./ 212 ); 213 214 await Assert.rejects( 215 profileStorage.addresses.add(TEST_ADDRESS_EMPTY_AFTER_NORMALIZE), 216 /Record contains no valid field\./ 217 ); 218 }); 219 220 add_task(async function test_update() { 221 // Test assumes that when an entry is saved a second time, it's last modified date will 222 // be different from the first. With high values of precision reduction, we execute too 223 // fast for that to be true. 224 let timerPrecision = Preferences.get("privacy.reduceTimerPrecision"); 225 Preferences.set("privacy.reduceTimerPrecision", false); 226 227 registerCleanupFunction(function () { 228 Preferences.set("privacy.reduceTimerPrecision", timerPrecision); 229 }); 230 231 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [ 232 TEST_ADDRESS_1, 233 TEST_ADDRESS_2, 234 ]); 235 236 let addresses = await profileStorage.addresses.getAll(); 237 let guid = addresses[1].guid; 238 // We need to cheat a little due to race conditions of Date.now() when 239 // we're running these tests, so we subtract one and test accordingly 240 // in the times Date.now() returns the same timestamp 241 let timeLastModified = addresses[1].timeLastModified - 1; 242 243 let onChanged = TestUtils.topicObserved( 244 "formautofill-storage-changed", 245 (subject, data) => 246 data == "update" && 247 subject.wrappedJSObject.guid == guid && 248 subject.wrappedJSObject.collectionName == COLLECTION_NAME 249 ); 250 251 Assert.notEqual(addresses[1].country, undefined); 252 253 await profileStorage.addresses.update(guid, TEST_ADDRESS_3); 254 await onChanged; 255 await profileStorage._saveImmediately(); 256 257 profileStorage.addresses.pullSyncChanges(); // force sync metadata, which we check below. 258 259 let address = await profileStorage.addresses.get(guid, { rawData: true }); 260 261 Assert.equal(address.country, "US"); 262 Assert.greater(address.timeLastModified, timeLastModified); 263 do_check_record_matches(address, TEST_ADDRESS_3); 264 Assert.equal(getSyncChangeCounter(profileStorage.addresses, guid), 1); 265 266 // Test preserveOldProperties parameter and field with empty string. 267 await profileStorage.addresses.update( 268 guid, 269 TEST_ADDRESS_WITH_EMPTY_FIELD, 270 true 271 ); 272 await onChanged; 273 await profileStorage._saveImmediately(); 274 275 profileStorage.addresses.pullSyncChanges(); // force sync metadata, which we check below. 276 277 address = await profileStorage.addresses.get(guid, { rawData: true }); 278 279 Assert.equal(address.name, "Tim Berners"); 280 Assert.equal(address["street-address"], undefined); 281 Assert.equal(address["postal-code"], "12345"); 282 Assert.notEqual(address.timeLastModified, timeLastModified); 283 Assert.equal(getSyncChangeCounter(profileStorage.addresses, guid), 2); 284 285 // Empty string should be deleted while updating. 286 await profileStorage.addresses.update( 287 profileStorage.addresses._data[0].guid, 288 TEST_ADDRESS_WITH_EMPTY_FIELD 289 ); 290 address = profileStorage.addresses._data[0]; 291 Assert.equal(address.name, TEST_ADDRESS_WITH_EMPTY_FIELD.name); 292 Assert.equal(address["street-address"], undefined); 293 Assert.equal(address[("unknown-1", "an unknown field from another client")]); 294 295 // Empty computed fields shouldn't cause any problem. 296 await profileStorage.addresses.update( 297 profileStorage.addresses._data[0].guid, 298 TEST_ADDRESS_WITH_EMPTY_COMPUTED_FIELD, 299 false 300 ); 301 address = profileStorage.addresses._data[0]; 302 Assert.equal(address.email, TEST_ADDRESS_WITH_EMPTY_COMPUTED_FIELD.email); 303 await profileStorage.addresses.update( 304 profileStorage.addresses._data[1].guid, 305 TEST_ADDRESS_WITH_EMPTY_COMPUTED_FIELD, 306 true 307 ); 308 address = profileStorage.addresses._data[1]; 309 Assert.equal(address.email, TEST_ADDRESS_WITH_EMPTY_COMPUTED_FIELD.email); 310 311 await Assert.rejects( 312 profileStorage.addresses.update("INVALID_GUID", TEST_ADDRESS_3), 313 /No matching record\./ 314 ); 315 316 await Assert.rejects( 317 profileStorage.addresses.update(guid, TEST_ADDRESS_WITH_INVALID_FIELD), 318 /"email" contains invalid data type: object/ 319 ); 320 321 await Assert.rejects( 322 profileStorage.addresses.update(guid, {}), 323 /Record contains no valid field\./ 324 ); 325 326 await Assert.rejects( 327 profileStorage.addresses.update(guid, TEST_ADDRESS_EMPTY_AFTER_NORMALIZE), 328 /Record contains no valid field\./ 329 ); 330 331 profileStorage.addresses.update(guid, TEST_ADDRESS_2); 332 }); 333 334 add_task(async function test_notifyUsed() { 335 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [ 336 TEST_ADDRESS_1, 337 TEST_ADDRESS_2, 338 ]); 339 340 let addresses = await profileStorage.addresses.getAll(); 341 let guid = addresses[1].guid; 342 let timeLastUsed = addresses[1].timeLastUsed; 343 let timesUsed = addresses[1].timesUsed; 344 345 profileStorage.addresses.pullSyncChanges(); // force sync metadata, which we check below. 346 let changeCounter = getSyncChangeCounter(profileStorage.addresses, guid); 347 348 let onChanged = TestUtils.topicObserved( 349 "formautofill-storage-changed", 350 (subject, data) => 351 data == "notifyUsed" && 352 subject.wrappedJSObject.guid == guid && 353 subject.wrappedJSObject.collectionName == COLLECTION_NAME 354 ); 355 356 profileStorage.addresses.notifyUsed(guid); 357 await onChanged; 358 359 let address = await profileStorage.addresses.get(guid); 360 361 Assert.equal(address.timesUsed, timesUsed + 1); 362 Assert.notEqual(address.timeLastUsed, timeLastUsed); 363 364 // Using a record should not bump its change counter. 365 Assert.equal( 366 getSyncChangeCounter(profileStorage.addresses, guid), 367 changeCounter 368 ); 369 }); 370 371 add_task(async function test_remove() { 372 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [ 373 TEST_ADDRESS_1, 374 TEST_ADDRESS_2, 375 ]); 376 377 let addresses = await profileStorage.addresses.getAll(); 378 let guid = addresses[1].guid; 379 380 let onChanged = TestUtils.topicObserved( 381 "formautofill-storage-changed", 382 (subject, data) => 383 data == "remove" && 384 subject.wrappedJSObject.guid == guid && 385 subject.wrappedJSObject.collectionName == COLLECTION_NAME 386 ); 387 388 Assert.equal(addresses.length, 2); 389 390 profileStorage.addresses.remove(guid); 391 await onChanged; 392 393 addresses = await profileStorage.addresses.getAll(); 394 395 Assert.equal(addresses.length, 1); 396 397 Assert.equal(await profileStorage.addresses.get(guid), null); 398 });