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 @@ + + +