diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts index 6a9707143c8..92f6d007c57 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts @@ -1,6 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import AutofillField from "../models/autofill-field"; +import AutofillForm from "../models/autofill-form"; import AutofillPageDetails from "../models/autofill-page-details"; import { AutoFillConstants } from "./autofill-constants"; @@ -19,7 +20,7 @@ describe("InlineMenuFieldQualificationService", () => { }); describe("isFieldForLoginForm", () => { - describe("validating a password field for a login form", () => { + describe("qualifying a password field for a login form", () => { describe("an invalid password field", () => { it("has a `new-password` autoCompleteType", () => { const field = mock({ @@ -97,6 +98,108 @@ describe("InlineMenuFieldQualificationService", () => { false, ); }); + + describe("does not have a parent form element", () => { + beforeEach(() => { + pageDetails.forms = {}; + }); + + it("on a page that has more than one password field", () => { + const field = mock({ + type: "password", + htmlID: "user-password", + htmlName: "user-password", + placeholder: "user-password", + form: "", + }); + const secondField = mock({ + type: "password", + htmlID: "some-other-password", + htmlName: "some-other-password", + placeholder: "some-other-password", + }); + pageDetails.fields = [field, secondField]; + + expect( + inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails), + ).toBe(false); + }); + + it("on a page that has more than one visible username field", () => { + const field = mock({ + type: "password", + htmlID: "user-password", + htmlName: "user-password", + placeholder: "user-password", + form: "", + }); + const usernameField = mock({ + type: "text", + htmlID: "user-username", + htmlName: "user-username", + placeholder: "user-username", + }); + const secondUsernameField = mock({ + type: "text", + htmlID: "some-other-user-username", + htmlName: "some-other-user-username", + placeholder: "some-other-user-username", + }); + pageDetails.fields = [field, usernameField, secondUsernameField]; + + expect( + inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails), + ).toBe(false); + }); + + it("has a disabled `autocompleteType` value", () => { + const field = mock({ + type: "password", + htmlID: "user-password", + htmlName: "user-password", + placeholder: "user-password", + form: "", + autoCompleteType: "off", + }); + + expect( + inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails), + ).toBe(false); + }); + }); + + describe("has a parent form element", () => { + let form: MockProxy; + + beforeEach(() => { + form = mock({ opid: "validFormId" }); + pageDetails.forms = { + validFormId: form, + }; + }); + + it("is structured with other password fields in the same form", () => { + const field = mock({ + type: "password", + htmlID: "user-password", + htmlName: "user-password", + placeholder: "user-password", + form: "validFormId", + }); + const secondField = mock({ + type: "password", + htmlID: "some-other-password", + htmlName: "some-other-password", + placeholder: "some-other-password", + form: "validFormId", + }); + pageDetails.fields = [field, secondField]; + + expect( + inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails), + ).toBe(false); + }); + }); }); describe("a valid password field", () => { @@ -126,6 +229,94 @@ describe("InlineMenuFieldQualificationService", () => { true, ); }); + + describe("does not have a parent form element", () => { + it("is the only password field on the page, has one username field on the page, and has a non-disabled `autocompleteType` value", () => { + pageDetails.forms = {}; + const field = mock({ + type: "password", + htmlID: "user-password", + htmlName: "user-password", + placeholder: "user-password", + form: "", + autoCompleteType: "current-password", + }); + const usernameField = mock({ + type: "text", + htmlID: "user-username", + htmlName: "user-username", + placeholder: "user-username", + }); + pageDetails.fields = [field, usernameField]; + + expect( + inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails), + ).toBe(true); + }); + }); + + describe("has a parent form element", () => { + let form: MockProxy; + + beforeEach(() => { + form = mock({ opid: "validFormId" }); + pageDetails.forms = { + validFormId: form, + }; + }); + + it("is the only password field within the form and has a visible username field", () => { + const field = mock({ + type: "password", + htmlID: "user-password", + htmlName: "user-password", + placeholder: "user-password", + form: "validFormId", + }); + const secondPasswordField = mock({ + type: "password", + htmlID: "some-other-password", + htmlName: "some-other-password", + placeholder: "some-other-password", + form: "anotherFormId", + }); + const usernameField = mock({ + type: "text", + htmlID: "user-username", + htmlName: "user-username", + placeholder: "user-username", + form: "validFormId", + }); + pageDetails.fields = [field, secondPasswordField, usernameField]; + + expect( + inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails), + ).toBe(true); + }); + + it("is the only password field within the form and has a non-disabled `autocompleteType` value", () => { + const field = mock({ + type: "password", + htmlID: "user-password", + htmlName: "user-password", + placeholder: "user-password", + form: "validFormId", + autoCompleteType: "", + }); + const secondPasswordField = mock({ + type: "password", + htmlID: "some-other-password", + htmlName: "some-other-password", + placeholder: "some-other-password", + form: "anotherFormId", + }); + pageDetails.fields = [field, secondPasswordField]; + + expect( + inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails), + ).toBe(true); + }); + }); }); }); diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts index 11f23fad9bb..62963bdaeaf 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts @@ -102,7 +102,8 @@ export class InlineMenuFieldQualificationService return true; } - // If the field has a form parent a no username field exists and the field has an autocomplete attribute set to "off" or "false", this is not a password field + // If the field has a form parent a no username field exists and the field has an + // autocomplete attribute set to "off" or "false", this is not a password field return !this.autocompleteDisabledValues.has(field.autoCompleteType); }