test_autofill_ios_library.js (6725B)
1 /* Any copyright is dedicated to the Public Domain. 2 https://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { FormAutofillChild } = ChromeUtils.importESModule( 7 "resource://autofill/FormAutofillChild.ios.sys.mjs" 8 ); 9 10 const { AutofillTelemetry } = ChromeUtils.importESModule( 11 "resource://gre/modules/shared/AutofillTelemetry.sys.mjs" 12 ); 13 14 var { FormAutofillHandler } = ChromeUtils.importESModule( 15 "resource://gre/modules/shared/FormAutofillHandler.sys.mjs" 16 ); 17 18 const TEST_CASES = [ 19 { 20 description: `basic credit card form`, 21 document: `<form> 22 <input id="cc-number" autocomplete="cc-number"> 23 <input id="cc-name" autocomplete="cc-name"> 24 <input id="cc-exp-month" autocomplete="cc-exp-month"> 25 <input id="cc-exp-year" autocomplete="cc-exp-year"> 26 </form>`, 27 fillPayload: { 28 "cc-number": "4111111111111111", 29 "cc-name": "test name", 30 "cc-exp-month": 6, 31 "cc-exp-year": 25, 32 }, 33 34 expectedDetectedFields: { 35 "cc-number": "", 36 "cc-name": "", 37 "cc-exp-month": "", 38 "cc-exp-year": "", 39 "cc-type": "", 40 }, 41 42 expectedFill: { 43 "#cc-number": "4111111111111111", 44 "#cc-name": "test name", 45 "#cc-exp-month": 6, 46 "#cc-exp-year": 25, 47 }, 48 49 expectedSubmit: [ 50 { 51 "cc-number": "4111111111111111", 52 "cc-name": "test name", 53 "cc-exp-month": 6, 54 "cc-exp-year": 2025, 55 "cc-type": "visa", 56 }, 57 ], 58 }, 59 { 60 description: `basic address form`, 61 document: `<form> 62 <input id="given-name" autocomplete="given-name"> 63 <input id="family-name" autocomplete="family-name"> 64 <input id="street-address" autocomplete="street-address"> 65 <input id="address-level2" autocomplete="address-level2"> 66 <select id="country" autocomplete="country"> 67 <option/> 68 <option value="US">United States</option> 69 </select> 70 <input id="email" autocomplete="email"> 71 <input id="tel" autocomplete="tel"> 72 <form>`, 73 fillPayload: { 74 "street-address": "2 Harrison St line2", 75 "address-level2": "San Francisco", 76 country: "US", 77 email: "foo@mozilla.com", 78 tel: "1234567", 79 }, 80 81 expectedFill: { 82 "#street-address": "2 Harrison St line2", 83 "#address-level2": "San Francisco", 84 "#country": "US", 85 "#email": "foo@mozilla.com", 86 "#tel": "1234567", 87 }, 88 89 expectedDetectedFields: { 90 "given-name": "", 91 "family-name": "", 92 "street-address": "", 93 "address-level2": "", 94 country: "", 95 email: "", 96 tel: "", 97 }, 98 99 expectedSubmit: null, 100 }, 101 { 102 description: `Test correct street-address to address-line1`, 103 document: `<form> 104 <input id="email" autocomplete="email"> 105 <input id="tel" autocomplete="tel"> 106 <input id="street-address" > 107 <input id="address-line2"> 108 <form>`, 109 fillPayload: { 110 "street-address": "2 Harrison St\nline2", 111 email: "foo@mozilla.com", 112 tel: "1234567", 113 }, 114 115 expectedFill: { 116 "#street-address": "2 Harrison St", 117 "#address-line2": "line2", 118 "#email": "foo@mozilla.com", 119 "#tel": "1234567", 120 }, 121 122 expectedDetectedFields: { 123 email: "", 124 tel: "", 125 "address-line1": "", 126 "address-line2": "", 127 }, 128 129 expectedSubmit: null, 130 }, 131 { 132 description: `Test invalid address section`, 133 document: `<form> 134 <input id="email"> 135 <form>`, 136 fillPayload: {}, 137 expectedFill: false, 138 expectedDetectedFields: {}, 139 140 expectedSubmit: null, 141 }, 142 ]; 143 144 const recordFormInteractionEventStub = sinon.stub( 145 AutofillTelemetry, 146 "recordFormInteractionEvent" 147 ); 148 149 add_setup(() => { 150 registerCleanupFunction(() => sinon.restore()); 151 }); 152 153 add_task(async function test_ios_api() { 154 for (const TEST of TEST_CASES) { 155 info(`Test ${TEST.description}`); 156 const doc = MockDocument.createTestDocument( 157 "http://localhost:8080/test/", 158 TEST.document 159 ); 160 161 const autofillSpy = sinon.spy(); 162 const submitSpy = sinon.spy(); 163 164 const fac = new FormAutofillChild({ 165 address: { 166 autofill: autofillSpy, 167 submit: submitSpy, 168 }, 169 creditCard: { 170 autofill: autofillSpy, 171 submit: submitSpy, 172 }, 173 }); 174 175 // Test `onFocusIn` API 176 fac.onFocusIn({ target: doc.querySelector("input") }); 177 178 if (TEST.expectedFill) { 179 Assert.ok( 180 autofillSpy.calledOnce, 181 "autofill callback should be called once" 182 ); 183 Assert.ok( 184 autofillSpy.calledWithExactly(TEST.expectedDetectedFields), 185 "autofill callback should be called with correct payload" 186 ); 187 Assert.ok( 188 recordFormInteractionEventStub.calledWithMatch("detected"), 189 "detect telemetry event should be recorded" 190 ); 191 192 // Test `fillFormFields` API 193 fac.fillFormFields(TEST.fillPayload); 194 Object.entries(TEST.expectedFill).forEach(([selector, expectedValue]) => { 195 const element = doc.querySelector(selector); 196 Assert.equal( 197 element.value, 198 expectedValue, 199 `Should fill ${element.id} field correctly` 200 ); 201 }); 202 Assert.ok( 203 recordFormInteractionEventStub.calledWithMatch("filled"), 204 "filled telemetry event should be recorded" 205 ); 206 207 // Test `onFilledModified` API 208 Object.entries(TEST.expectedFill).forEach(([selector, expectedValue]) => { 209 const element = doc.querySelector(selector); 210 // Simulate input change (e.g. adding a char) 211 FormAutofillHandler.fillFieldValue(element, expectedValue + "a"); 212 Assert.ok( 213 recordFormInteractionEventStub.calledWithMatch("filled_modified"), 214 "filled_modified telemetry event should be recorded" 215 ); 216 FormAutofillHandler.fillFieldValue(element, expectedValue); 217 }); 218 } else { 219 Assert.ok( 220 autofillSpy.notCalled, 221 "autofill callback should not be called when section is invalid" 222 ); 223 } 224 225 // Test `onSubmit` API 226 if (TEST.expectedSubmit) { 227 fac.onSubmit(); 228 Assert.ok(submitSpy.calledOnce, "submit callback should be called once"); 229 Assert.ok( 230 submitSpy.calledWithExactly(TEST.expectedSubmit), 231 "submit callback should be called with correct payload" 232 ); 233 Assert.ok( 234 recordFormInteractionEventStub.calledWithMatch("submitted"), 235 "submitted telemetry event should be recorded" 236 ); 237 } 238 } 239 });