diff --git a/browser-extensions/chrome/src/shared/formDetector/FormDetector.ts b/browser-extensions/chrome/src/shared/formDetector/FormDetector.ts index 061af4f60..0b73a90e4 100644 --- a/browser-extensions/chrome/src/shared/formDetector/FormDetector.ts +++ b/browser-extensions/chrome/src/shared/formDetector/FormDetector.ts @@ -201,7 +201,7 @@ export class FormDetector { // Find primary email field const primaryEmail = this.findInputField( form, - ['email', 'e-mail', 'mail', 'address', '@'], + ['email', 'e-mail', 'mail', '@', 'emailaddress'], ['text', 'email'] ); @@ -401,9 +401,6 @@ export class FormDetector { return patterns.some(pattern => attributes.some(attr => attr.includes(pattern))); } - /** - * Find the password field in a form. - */ /** * Find the password field in a form. */ @@ -423,7 +420,6 @@ export class FormDetector { const attributes = [ input.id, input.name, - input.className, input.placeholder ].map(attr => attr?.toLowerCase() || ''); @@ -434,6 +430,11 @@ export class FormDetector { } } + // If no clear primary password field is found, use the first password field as primary + if (!primaryPassword && candidates.length > 0) { + primaryPassword = candidates[0]; + } + // If we found a primary password, look for a confirmation field if (primaryPassword) { for (const input of Array.from(candidates)) { @@ -454,11 +455,6 @@ export class FormDetector { } } - // If no clear primary password field is found, use the first password field as primary - if (!primaryPassword && candidates.length > 0) { - primaryPassword = candidates[0]; - } - return { primary: primaryPassword, confirm: confirmPassword diff --git a/browser-extensions/chrome/src/shared/formDetector/__tests__/FormDetector.nl.test.ts b/browser-extensions/chrome/src/shared/formDetector/__tests__/FormDetector.nl.test.ts index d17b21daf..00503039c 100644 --- a/browser-extensions/chrome/src/shared/formDetector/__tests__/FormDetector.nl.test.ts +++ b/browser-extensions/chrome/src/shared/formDetector/__tests__/FormDetector.nl.test.ts @@ -64,6 +64,13 @@ describe('FormDetector', () => { testField(FormField.Email, 'field18478', htmlFile); testField(FormField.FirstName, 'field18479', htmlFile); testField(FormField.LastName, 'field18486', htmlFile); + }); + describe('Dutch registration form 7 detection', () => { + const htmlFile = 'nl-registration-form7.html'; + + testField(FormField.Email, 'Form_EmailAddress', htmlFile); + testField(FormField.Password, 'Form_Password', htmlFile); + testField(FormField.PasswordConfirm, 'Form_RepeatPassword', htmlFile); }); }); diff --git a/browser-extensions/chrome/src/shared/formDetector/__tests__/FormDetector.test.ts b/browser-extensions/chrome/src/shared/formDetector/__tests__/FormDetector.test.ts new file mode 100644 index 000000000..cf780bea8 --- /dev/null +++ b/browser-extensions/chrome/src/shared/formDetector/__tests__/FormDetector.test.ts @@ -0,0 +1,202 @@ +import { FormDetector } from '../FormDetector'; +import { readFileSync } from 'fs'; +import { join } from 'path'; +import { describe, it, expect } from 'vitest'; +import { JSDOM } from 'jsdom'; +import { LoginForm } from '../types/LoginForm'; + +/** + * Load a test HTML file. + */ +const loadTestHtml = (filename: string): string => { + return readFileSync(join(__dirname, 'test-forms', filename), 'utf-8'); +}; + +/** + * Setup a form detection test. + */ +const setupFormTest = (htmlFile: string, focusedElementId?: string) : { document: Document, result: LoginForm } => { + const html = loadTestHtml(htmlFile); + const dom = new JSDOM(html, { + url: 'http://localhost', + runScripts: 'dangerously', + resources: 'usable' + }); + const document = dom.window.document; + + // Set focus on specified element if provided + let focusedElement: HTMLElement | null = null; + if (focusedElementId) { + focusedElement = document.getElementById(focusedElementId); + if (!focusedElement) { + throw new Error(`Focus element with id "${focusedElementId}" not found in test HTML`); + } + focusedElement.focus(); + + // Create a new form detector with the focused element. + const formDetector = new FormDetector(document, focusedElement); + const result = formDetector.detectForms()[0]; + return { document, result }; + } + + // No focused element, so just detect the first form. + const formDetector = new FormDetector(document); + const result = formDetector.detectForms()[0]; + return { document, result }; +}; + +enum FormField { + Username = 'username', + FirstName = 'firstName', + LastName = 'lastName', + Email = 'email', + EmailConfirm = 'emailConfirm', + Password = 'password', + PasswordConfirm = 'passwordConfirm', + BirthDate = 'birthdate', + BirthDateFormat = 'birthdateFormat', + BirthDay = 'birthdateDay', + BirthMonth = 'birthdateMonth', + BirthYear = 'birthdateYear', + Gender = 'gender', + GenderMale = 'genderMale', + GenderFemale = 'genderFemale', + GenderOther = 'genderOther' +} + +/** + * Helper function to test field detection + */ +const testField = (fieldName: FormField, elementId: string, htmlFile: string) : void => { + it(`should detect ${fieldName} field`, () => { + const { document, result } = setupFormTest(htmlFile, elementId); + + // First verify the test element exists + const expectedElement = document.getElementById(elementId); + if (!expectedElement) { + throw new Error(`Test setup failed: Element with id "${elementId}" not found in test HTML. Check if the element is present in the test HTML file: ${htmlFile}`); + } + + // Handle birthdate fields differently + if (fieldName === FormField.BirthDate) { + expect(result.birthdateField.single).toBe(expectedElement); + } else if (fieldName === FormField.BirthDay) { + expect(result.birthdateField.day).toBe(expectedElement); + } else if (fieldName === FormField.BirthMonth) { + expect(result.birthdateField.month).toBe(expectedElement); + } else if (fieldName === FormField.BirthYear) { + expect(result.birthdateField.year).toBe(expectedElement); + } + // Handle gender field differently + else if (fieldName === FormField.Gender) { + expect(result.genderField.field).toBe(expectedElement); + } else if (fieldName === FormField.GenderMale) { + expect(result.genderField.radioButtons?.male).toBe(expectedElement); + } else if (fieldName === FormField.GenderFemale) { + expect(result.genderField.radioButtons?.female).toBe(expectedElement); + } else if (fieldName === FormField.GenderOther) { + expect(result.genderField.radioButtons?.other).toBe(expectedElement); + } + // Handle default fields + else { + const fieldKey = `${fieldName}Field` as keyof typeof result; + expect(result[fieldKey]).toBeDefined(); + expect(result[fieldKey]).toBe(expectedElement); + } + }); +}; + +/** + * Test the birthdate format. + */ +const testBirthdateFormat = (expectedFormat: string, htmlFile: string) : void => { + it('should detect correct birthdate format', () => { + const { result } = setupFormTest(htmlFile); + expect(result.birthdateField.format).toBe(expectedFormat); + }); +}; + +describe('FormDetector', () => { + describe('Dutch registration form detection', () => { + const htmlFile = 'nl-registration-form1.html'; + + testField(FormField.LastName, 'cpContent_txtAchternaam', htmlFile); + testField(FormField.Email, 'cpContent_txtEmail', htmlFile); + testField(FormField.Password, 'cpContent_txtWachtwoord', htmlFile); + testField(FormField.PasswordConfirm, 'cpContent_txtWachtwoord2', htmlFile); + }); + + describe('Dutch registration form 2 detection', () => { + const htmlFile = 'nl-registration-form2.html'; + + testField(FormField.Username, 'register-username', htmlFile); + testField(FormField.Email, 'register-email', htmlFile); + testField(FormField.Password, 'register-password', htmlFile); + + testField(FormField.BirthDay, 'register-day', htmlFile); + testField(FormField.BirthMonth, 'register-month', htmlFile); + testField(FormField.BirthYear, 'register-year', htmlFile); + + testField(FormField.GenderMale, 'man', htmlFile); + testField(FormField.GenderFemale, 'vrouw', htmlFile); + testField(FormField.GenderOther, 'iets', htmlFile); + }); + + describe('Dutch registration form 3 detection', () => { + const htmlFile = 'nl-registration-form3.html'; + + testField(FormField.FirstName, 'firstName', htmlFile); + testField(FormField.LastName, 'lastName', htmlFile); + testField(FormField.Password, 'password', htmlFile); + + testField(FormField.BirthDate, 'date', htmlFile); + testBirthdateFormat('dd-mm-yyyy', htmlFile); + testField(FormField.GenderMale, 'gender1', htmlFile); + testField(FormField.GenderFemale, 'gender2', htmlFile); + testField(FormField.GenderOther, 'gender3', htmlFile); + }); + + describe('Dutch registration form 4 detection', () => { + const htmlFile = 'nl-registration-form4.html'; + + testField(FormField.Email, 'EmailAddress', htmlFile); + }); + + describe('Dutch registration form 5 detection', () => { + const htmlFile = 'nl-registration-form5.html'; + + testField(FormField.Email, 'input_25_5', htmlFile); + testField(FormField.Gender, 'input_25_13', htmlFile); + testField(FormField.FirstName, 'input_25_14', htmlFile); + testField(FormField.LastName, 'input_25_15', htmlFile); + testField(FormField.BirthDate, 'input_25_10', htmlFile); + testBirthdateFormat('dd/mm/yyyy', htmlFile); + }); + + describe('English registration form 1 detection', () => { + const htmlFile = 'en-registration-form1.html'; + + testField(FormField.Email, 'login', htmlFile); + testField(FormField.Password, 'password', htmlFile); + }); + + describe('English registration form 2 detection', () => { + const htmlFile = 'en-registration-form2.html'; + + testField(FormField.Email, 'signup-email-input', htmlFile); + testField(FormField.Username, 'signup-name-input', htmlFile); + }); + + describe('English registration form 3 detection', () => { + const htmlFile = 'en-registration-form3.html'; + + testField(FormField.Email, 'email', htmlFile); + testField(FormField.EmailConfirm, 'reenter_email', htmlFile); + }); + + describe('English email form 1 detection', () => { + const htmlFile = 'en-email-form1.html'; + + testField(FormField.Email, 'P0-0', htmlFile); + }); +}); \ No newline at end of file diff --git a/browser-extensions/chrome/src/shared/formDetector/__tests__/test-forms/nl-registration-form7.html b/browser-extensions/chrome/src/shared/formDetector/__tests__/test-forms/nl-registration-form7.html new file mode 100644 index 000000000..88af40f4f --- /dev/null +++ b/browser-extensions/chrome/src/shared/formDetector/__tests__/test-forms/nl-registration-form7.html @@ -0,0 +1,5 @@ + + +

Contactgegevens

Naam
Adresgegevens
Contactgegevens
  • Afghanistan+93
  • Albania+355
  • Algeria+213
  • American Samoa+1
  • Andorra+376
  • Angola+244
  • Anguilla+1
  • Antigua & Barbuda+1
  • Argentina+54
  • Armenia+374
  • Aruba+297
  • Australia+61
  • Austria+43
  • Azerbaijan+994
  • Bahamas+1
  • Bahrain+973
  • Bangladesh+880
  • Barbados+1
  • Belarus+375
  • Belgium+32
  • Belize+501
  • Benin+229
  • Bermuda+1
  • Bhutan+975
  • Bolivia+591
  • Bosnia & Herzegovina+387
  • Botswana+267
  • Brazil+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1
  • Brunei+673
  • Bulgaria+359
  • Burkina Faso+226
  • Burundi+257
  • Cambodia+855
  • Cameroon+237
  • Canada+1
  • Cape Verde+238
  • Cayman Islands+1
  • Central African Republic+236
  • Chad+235
  • Chile+56
  • China+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros+269
  • Congo - Brazzaville+242
  • Congo - Kinshasa+243
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia+385
  • Cuba+53
  • Cyprus+357
  • Czechia+420
  • Denmark+45
  • Djibouti+253
  • Dominica+1
  • Dominican Republic+1
  • Ecuador+593
  • Egypt+20
  • El Salvador+503
  • Equatorial Guinea+240
  • Eritrea+291
  • Estonia+372
  • Eswatini+268
  • Ethiopia+251
  • Falkland Islands+500
  • Faroe Islands+298
  • Fiji+679
  • Finland+358
  • France+33
  • French Guiana+594
  • French Polynesia+689
  • Gabon+241
  • Gambia+220
  • Georgia+995
  • Germany+49
  • Ghana+233
  • Gibraltar+350
  • Greece+30
  • Greenland+299
  • Grenada+1
  • Guadeloupe+590
  • Guam+1
  • Guatemala+502
  • Guinea+224
  • Guinea-Bissau+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong SAR China+852
  • Hungary+36
  • Iceland+354
  • India+91
  • Indonesia+62
  • Iran+98
  • Iraq+964
  • Ireland+353
  • Israel+972
  • Italy+39
  • Jamaica+1
  • Japan+81
  • Jersey+44
  • Jordan+962
  • Kazakhstan+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait+965
  • Kyrgyzstan+996
  • Laos+856
  • Latvia+371
  • Lebanon+961
  • Lesotho+266
  • Liberia+231
  • Libya+218
  • Liechtenstein+423
  • Lithuania+370
  • Luxembourg+352
  • Macao SAR China+853
  • Madagascar+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania+222
  • Mauritius+230
  • Mayotte+262
  • Mexico+52
  • Micronesia+691
  • Moldova+373
  • Monaco+377
  • Mongolia+976
  • Montenegro+382
  • Montserrat+1
  • Morocco+212
  • Mozambique+258
  • Myanmar (Burma)+95
  • Namibia+264
  • Nauru+674
  • Nepal+977
  • Netherlands+31
  • New Caledonia+687
  • New Zealand+64
  • Nicaragua+505
  • Niger+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea+850
  • North Macedonia+389
  • Northern Mariana Islands+1
  • Norway+47
  • Oman+968
  • Pakistan+92
  • Palau+680
  • Palestinian Territories+970
  • Panama+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru+51
  • Philippines+63
  • Poland+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar+974
  • Réunion+262
  • Romania+40
  • Russia+7
  • Rwanda+250
  • Samoa+685
  • San Marino+378
  • São Tomé & Príncipe+239
  • Saudi Arabia+966
  • Senegal+221
  • Serbia+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Slovakia+421
  • Slovenia+386
  • Solomon Islands+677
  • Somalia+252
  • South Africa+27
  • South Korea+82
  • Spain+34
  • Sri Lanka+94
  • St. Helena+290
  • St. Kitts & Nevis+1
  • St. Lucia+1
  • St. Pierre & Miquelon+508
  • St. Vincent & Grenadines+1
  • Sudan+249
  • Suriname+597
  • Svalbard & Jan Mayen+47
  • Sweden+46
  • Switzerland+41
  • Syria+963
  • Taiwan+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad & Tobago+1
  • Tunisia+216
  • Turkey+90
  • Turkmenistan+993
  • Turks & Caicos Islands+1
  • Tuvalu+688
  • U.S. Virgin Islands+1
  • Uganda+256
  • Ukraine+380
  • United Arab Emirates+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan+998
  • Vanuatu+678
  • Vatican City+39
  • Venezuela+58
  • Vietnam+84
  • Wallis & Futuna+681
  • Western Sahara+212
  • Yemen+967
  • Zambia+260
  • Zimbabwe+263
Geboortedatum
\ No newline at end of file