@@ -44,6 +44,7 @@ import { TotpService } from "@bitwarden/common/vault/services/totp.service";
4444import { BrowserApi } from "../../platform/browser/browser-api" ;
4545import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service" ;
4646import { AutofillMessageCommand , AutofillMessageSender } from "../enums/autofill-message.enums" ;
47+ import { InlineMenuFillTypes } from "../enums/autofill-overlay.enum" ;
4748import { AutofillPort } from "../enums/autofill-port.enum" ;
4849import AutofillField from "../models/autofill-field" ;
4950import AutofillPageDetails from "../models/autofill-page-details" ;
@@ -103,6 +104,15 @@ describe("AutofillService", () => {
103104 beforeEach ( ( ) => {
104105 configService = mock < ConfigService > ( ) ;
105106 configService . getFeatureFlag$ . mockImplementation ( ( ) => of ( false ) ) ;
107+
108+ // Initialize domainSettingsService BEFORE it's used
109+ domainSettingsService = new DefaultDomainSettingsService (
110+ fakeStateProvider ,
111+ policyService ,
112+ accountService ,
113+ ) ;
114+ domainSettingsService . equivalentDomains$ = of ( mockEquivalentDomains ) ;
115+
106116 scriptInjectorService = new BrowserScriptInjectorService (
107117 domainSettingsService ,
108118 platformUtilsService ,
@@ -141,12 +151,6 @@ describe("AutofillService", () => {
141151 userNotificationsSettings ,
142152 messageListener ,
143153 ) ;
144- domainSettingsService = new DefaultDomainSettingsService (
145- fakeStateProvider ,
146- policyService ,
147- accountService ,
148- ) ;
149- domainSettingsService . equivalentDomains$ = of ( mockEquivalentDomains ) ;
150154 jest . spyOn ( BrowserApi , "tabSendMessage" ) ;
151155 } ) ;
152156
@@ -2077,6 +2081,193 @@ describe("AutofillService", () => {
20772081 } ) ;
20782082 } ) ;
20792083
2084+ describe ( "given password generation with inlineMenuFillType" , ( ) => {
2085+ beforeEach ( ( ) => {
2086+ pageDetails . forms = undefined ;
2087+ pageDetails . fields = [ ] ; // Clear fields to start fresh
2088+ options . inlineMenuFillType = InlineMenuFillTypes . PasswordGeneration ;
2089+ options . cipher . login . totp = null ; // Disable TOTP for these tests
2090+ } ) ;
2091+
2092+ it ( "includes all password fields from the same form when filling with password generation" , async ( ) => {
2093+ const newPasswordField = createAutofillFieldMock ( {
2094+ opid : "new-password" ,
2095+ type : "password" ,
2096+ form : "validFormId" ,
2097+ elementNumber : 2 ,
2098+ } ) ;
2099+ const confirmPasswordField = createAutofillFieldMock ( {
2100+ opid : "confirm-password" ,
2101+ type : "password" ,
2102+ form : "validFormId" ,
2103+ elementNumber : 3 ,
2104+ } ) ;
2105+ pageDetails . fields . push ( newPasswordField , confirmPasswordField ) ;
2106+ options . focusedFieldOpid = newPasswordField . opid ;
2107+
2108+ await autofillService [ "generateLoginFillScript" ] (
2109+ fillScript ,
2110+ pageDetails ,
2111+ filledFields ,
2112+ options ,
2113+ ) ;
2114+
2115+ expect ( filledFields [ newPasswordField . opid ] ) . toBeDefined ( ) ;
2116+ expect ( filledFields [ confirmPasswordField . opid ] ) . toBeDefined ( ) ;
2117+ } ) ;
2118+
2119+ it ( "finds username field for the first password field when generating passwords" , async ( ) => {
2120+ const newPasswordField = createAutofillFieldMock ( {
2121+ opid : "new-password" ,
2122+ type : "password" ,
2123+ form : "validFormId" ,
2124+ elementNumber : 2 ,
2125+ } ) ;
2126+ pageDetails . fields . push ( newPasswordField ) ;
2127+ options . focusedFieldOpid = newPasswordField . opid ;
2128+ jest . spyOn ( autofillService as any , "findUsernameField" ) ;
2129+
2130+ await autofillService [ "generateLoginFillScript" ] (
2131+ fillScript ,
2132+ pageDetails ,
2133+ filledFields ,
2134+ options ,
2135+ ) ;
2136+
2137+ expect ( autofillService [ "findUsernameField" ] ) . toHaveBeenCalledWith (
2138+ pageDetails ,
2139+ expect . objectContaining ( { opid : newPasswordField . opid } ) ,
2140+ false ,
2141+ false ,
2142+ true ,
2143+ ) ;
2144+ } ) ;
2145+
2146+ it ( "does not include password fields from different forms" , async ( ) => {
2147+ const formAPasswordField = createAutofillFieldMock ( {
2148+ opid : "form-a-password" ,
2149+ type : "password" ,
2150+ form : "formA" ,
2151+ elementNumber : 1 ,
2152+ } ) ;
2153+ const formBPasswordField = createAutofillFieldMock ( {
2154+ opid : "form-b-password" ,
2155+ type : "password" ,
2156+ form : "formB" ,
2157+ elementNumber : 2 ,
2158+ } ) ;
2159+ pageDetails . fields = [ formAPasswordField , formBPasswordField ] ;
2160+ options . focusedFieldOpid = formAPasswordField . opid ;
2161+
2162+ await autofillService [ "generateLoginFillScript" ] (
2163+ fillScript ,
2164+ pageDetails ,
2165+ filledFields ,
2166+ options ,
2167+ ) ;
2168+
2169+ expect ( filledFields [ formAPasswordField . opid ] ) . toBeDefined ( ) ;
2170+ expect ( filledFields [ formBPasswordField . opid ] ) . toBeUndefined ( ) ;
2171+ } ) ;
2172+ } ) ;
2173+
2174+ describe ( "given current password update with inlineMenuFillType" , ( ) => {
2175+ beforeEach ( ( ) => {
2176+ pageDetails . forms = undefined ;
2177+ pageDetails . fields = [ ] ; // Clear fields to start fresh
2178+ options . inlineMenuFillType = InlineMenuFillTypes . CurrentPasswordUpdate ;
2179+ options . cipher . login . totp = null ; // Disable TOTP for these tests
2180+ } ) ;
2181+
2182+ it ( "includes all password fields from the same form when updating current password" , async ( ) => {
2183+ const currentPasswordField = createAutofillFieldMock ( {
2184+ opid : "current-password" ,
2185+ type : "password" ,
2186+ form : "validFormId" ,
2187+ elementNumber : 1 ,
2188+ } ) ;
2189+ const newPasswordField = createAutofillFieldMock ( {
2190+ opid : "new-password" ,
2191+ type : "password" ,
2192+ form : "validFormId" ,
2193+ elementNumber : 2 ,
2194+ } ) ;
2195+ const confirmPasswordField = createAutofillFieldMock ( {
2196+ opid : "confirm-password" ,
2197+ type : "password" ,
2198+ form : "validFormId" ,
2199+ elementNumber : 3 ,
2200+ } ) ;
2201+ pageDetails . fields . push ( currentPasswordField , newPasswordField , confirmPasswordField ) ;
2202+ options . focusedFieldOpid = currentPasswordField . opid ;
2203+
2204+ await autofillService [ "generateLoginFillScript" ] (
2205+ fillScript ,
2206+ pageDetails ,
2207+ filledFields ,
2208+ options ,
2209+ ) ;
2210+
2211+ expect ( filledFields [ currentPasswordField . opid ] ) . toBeDefined ( ) ;
2212+ expect ( filledFields [ newPasswordField . opid ] ) . toBeDefined ( ) ;
2213+ expect ( filledFields [ confirmPasswordField . opid ] ) . toBeDefined ( ) ;
2214+ } ) ;
2215+
2216+ it ( "includes all password fields from the same form without TOTP" , async ( ) => {
2217+ const currentPasswordField = createAutofillFieldMock ( {
2218+ opid : "current-password" ,
2219+ type : "password" ,
2220+ form : "validFormId" ,
2221+ elementNumber : 1 ,
2222+ } ) ;
2223+ const newPasswordField = createAutofillFieldMock ( {
2224+ opid : "new-password" ,
2225+ type : "password" ,
2226+ form : "validFormId" ,
2227+ elementNumber : 2 ,
2228+ } ) ;
2229+ pageDetails . fields . push ( currentPasswordField , newPasswordField ) ;
2230+ options . focusedFieldOpid = currentPasswordField . opid ;
2231+
2232+ await autofillService [ "generateLoginFillScript" ] (
2233+ fillScript ,
2234+ pageDetails ,
2235+ filledFields ,
2236+ options ,
2237+ ) ;
2238+
2239+ expect ( filledFields [ currentPasswordField . opid ] ) . toBeDefined ( ) ;
2240+ expect ( filledFields [ newPasswordField . opid ] ) . toBeDefined ( ) ;
2241+ } ) ;
2242+
2243+ it ( "does not include password fields from different forms during password update" , async ( ) => {
2244+ const formAPasswordField = createAutofillFieldMock ( {
2245+ opid : "form-a-password" ,
2246+ type : "password" ,
2247+ form : "formA" ,
2248+ elementNumber : 1 ,
2249+ } ) ;
2250+ const formBPasswordField = createAutofillFieldMock ( {
2251+ opid : "form-b-password" ,
2252+ type : "password" ,
2253+ form : "formB" ,
2254+ elementNumber : 2 ,
2255+ } ) ;
2256+ pageDetails . fields = [ formAPasswordField , formBPasswordField ] ;
2257+ options . focusedFieldOpid = formAPasswordField . opid ;
2258+
2259+ await autofillService [ "generateLoginFillScript" ] (
2260+ fillScript ,
2261+ pageDetails ,
2262+ filledFields ,
2263+ options ,
2264+ ) ;
2265+
2266+ expect ( filledFields [ formAPasswordField . opid ] ) . toBeDefined ( ) ;
2267+ expect ( filledFields [ formBPasswordField . opid ] ) . toBeUndefined ( ) ;
2268+ } ) ;
2269+ } ) ;
2270+
20802271 describe ( "given a set of page details that does not contain a password field" , ( ) => {
20812272 let emailField : AutofillField ;
20822273 let emailFieldView : FieldView ;
@@ -3140,12 +3331,16 @@ describe("AutofillService", () => {
31403331 "example.com" ,
31413332 "exampleapp.com" ,
31423333 ] ) ;
3143- domainSettingsService . equivalentDomains$ = of ( [ [ "not-example.com" ] ] ) ;
31443334 const pageUrl = "https://subdomain.example.com" ;
31453335 const tabUrl = "https://www.not-example.com" ;
31463336 const generateFillScriptOptions = createGenerateFillScriptOptionsMock ( { tabUrl } ) ;
31473337 generateFillScriptOptions . cipher . login . matchesUri = jest . fn ( ) . mockReturnValueOnce ( false ) ;
31483338
3339+ // Mock getUrlEquivalentDomains to return the expected domains
3340+ jest
3341+ . spyOn ( domainSettingsService , "getUrlEquivalentDomains" )
3342+ . mockReturnValue ( of ( equivalentDomains ) ) ;
3343+
31493344 const result = await autofillService [ "inUntrustedIframe" ] ( pageUrl , generateFillScriptOptions ) ;
31503345
31513346 expect ( generateFillScriptOptions . cipher . login . matchesUri ) . toHaveBeenCalledWith (
0 commit comments