@@ -31,34 +31,47 @@ def _make_browser(*, export_cookies: bool) -> MagicMock:
3131 return browser
3232
3333
34- @pytest .mark .asyncio
35- async def test_interactive_login_writes_source_state_when_cookie_export_succeeds (
36- monkeypatch , tmp_path , capsys
37- ):
38- browser = _make_browser (export_cookies = True )
39- write_source_state = MagicMock (
40- return_value = SimpleNamespace (login_generation = "gen-123" )
41- )
42-
43- monkeypatch .setattr ("linkedin_mcp_server.setup.get_config" , lambda : AppConfig ())
34+ def _patch_login_deps (
35+ monkeypatch ,
36+ * ,
37+ browser_factory ,
38+ config : AppConfig | None = None ,
39+ write_source_state : MagicMock | None = None ,
40+ ) -> None :
41+ """Patch all interactive_login dependencies in one place."""
4442 monkeypatch .setattr (
45- "linkedin_mcp_server.setup.BrowserManager" ,
46- lambda ** kwargs : _BrowserContextManager (browser ),
43+ "linkedin_mcp_server.setup.get_config" , lambda : config or AppConfig ()
4744 )
45+ monkeypatch .setattr ("linkedin_mcp_server.setup.BrowserManager" , browser_factory )
4846 monkeypatch .setattr ("linkedin_mcp_server.setup.warm_up_browser" , AsyncMock ())
4947 monkeypatch .setattr (
5048 "linkedin_mcp_server.setup.resolve_remember_me_prompt" ,
5149 AsyncMock (return_value = False ),
5250 )
51+ monkeypatch .setattr ("linkedin_mcp_server.setup.wait_for_manual_login" , AsyncMock ())
5352 monkeypatch .setattr (
54- "linkedin_mcp_server.setup.wait_for_manual_login" ,
55- AsyncMock (),
56- )
57- monkeypatch .setattr (
58- "linkedin_mcp_server.setup.write_source_state" , write_source_state
53+ "linkedin_mcp_server.setup.write_source_state" ,
54+ write_source_state
55+ or MagicMock (return_value = SimpleNamespace (login_generation = "gen-1" )),
5956 )
6057 monkeypatch .setattr ("linkedin_mcp_server.setup.asyncio.sleep" , AsyncMock ())
6158
59+
60+ @pytest .mark .asyncio
61+ async def test_interactive_login_writes_source_state_when_cookie_export_succeeds (
62+ monkeypatch , tmp_path , capsys
63+ ):
64+ browser = _make_browser (export_cookies = True )
65+ write_source_state = MagicMock (
66+ return_value = SimpleNamespace (login_generation = "gen-123" )
67+ )
68+
69+ _patch_login_deps (
70+ monkeypatch ,
71+ browser_factory = lambda ** kwargs : _BrowserContextManager (browser ),
72+ write_source_state = write_source_state ,
73+ )
74+
6275 assert await interactive_login (tmp_path / "profile" ) is True
6376
6477 browser .export_cookies .assert_awaited_once_with (
@@ -77,24 +90,11 @@ async def test_interactive_login_returns_false_when_cookie_export_fails(
7790 browser = _make_browser (export_cookies = False )
7891 write_source_state = MagicMock ()
7992
80- monkeypatch . setattr ( "linkedin_mcp_server.setup.get_config" , lambda : AppConfig ())
81- monkeypatch . setattr (
82- "linkedin_mcp_server.setup.BrowserManager" ,
83- lambda ** kwargs : _BrowserContextManager ( browser ) ,
93+ _patch_login_deps (
94+ monkeypatch ,
95+ browser_factory = lambda ** kwargs : _BrowserContextManager ( browser ) ,
96+ write_source_state = write_source_state ,
8497 )
85- monkeypatch .setattr ("linkedin_mcp_server.setup.warm_up_browser" , AsyncMock ())
86- monkeypatch .setattr (
87- "linkedin_mcp_server.setup.resolve_remember_me_prompt" ,
88- AsyncMock (return_value = False ),
89- )
90- monkeypatch .setattr (
91- "linkedin_mcp_server.setup.wait_for_manual_login" ,
92- AsyncMock (),
93- )
94- monkeypatch .setattr (
95- "linkedin_mcp_server.setup.write_source_state" , write_source_state
96- )
97- monkeypatch .setattr ("linkedin_mcp_server.setup.asyncio.sleep" , AsyncMock ())
9898
9999 assert await interactive_login (tmp_path / "profile" ) is False
100100
@@ -122,22 +122,105 @@ def fake_browser_manager(**kwargs):
122122 config = AppConfig ()
123123 config .browser .chrome_path = "/custom/chrome"
124124
125- monkeypatch .setattr ("linkedin_mcp_server.setup.get_config" , lambda : config )
126- monkeypatch .setattr (
127- "linkedin_mcp_server.setup.BrowserManager" , fake_browser_manager
128- )
129- monkeypatch .setattr ("linkedin_mcp_server.setup.warm_up_browser" , AsyncMock ())
130- monkeypatch .setattr (
131- "linkedin_mcp_server.setup.resolve_remember_me_prompt" ,
132- AsyncMock (return_value = False ),
133- )
134- monkeypatch .setattr ("linkedin_mcp_server.setup.wait_for_manual_login" , AsyncMock ())
135- monkeypatch .setattr (
136- "linkedin_mcp_server.setup.write_source_state" ,
137- MagicMock (return_value = SimpleNamespace (login_generation = "gen-1" )),
138- )
139- monkeypatch .setattr ("linkedin_mcp_server.setup.asyncio.sleep" , AsyncMock ())
125+ _patch_login_deps (monkeypatch , browser_factory = fake_browser_manager , config = config )
140126
141127 await interactive_login (tmp_path / "profile" )
142128
143129 assert captured_kwargs .get ("executable_path" ) == "/custom/chrome"
130+
131+
132+ @pytest .mark .asyncio
133+ async def test_interactive_login_forwards_all_browser_params (monkeypatch , tmp_path ):
134+ """All browser config params must reach BrowserManager during --login."""
135+ browser = _make_browser (export_cookies = True )
136+ captured_kwargs : dict = {}
137+
138+ def fake_browser_manager (** kwargs ):
139+ captured_kwargs .update (kwargs )
140+ return _BrowserContextManager (browser )
141+
142+ config = AppConfig ()
143+ config .browser .chrome_path = "/custom/chrome"
144+ config .browser .slow_mo = 250
145+ config .browser .user_agent = "CustomAgent/1.0"
146+ config .browser .viewport_width = 1920
147+ config .browser .viewport_height = 1080
148+
149+ _patch_login_deps (monkeypatch , browser_factory = fake_browser_manager , config = config )
150+
151+ profile = tmp_path / "profile"
152+ await interactive_login (profile )
153+
154+ assert captured_kwargs ["user_data_dir" ] == profile
155+ assert captured_kwargs ["headless" ] is False
156+ assert captured_kwargs ["slow_mo" ] == 250
157+ assert captured_kwargs ["user_agent" ] == "CustomAgent/1.0"
158+ assert captured_kwargs ["viewport" ] == {"width" : 1920 , "height" : 1080 }
159+ assert captured_kwargs ["executable_path" ] == "/custom/chrome"
160+
161+
162+ @pytest .mark .asyncio
163+ async def test_interactive_login_passes_slow_mo_to_browser_manager (
164+ monkeypatch , tmp_path
165+ ):
166+ """When config.browser.slow_mo is set, it must reach BrowserManager."""
167+ browser = _make_browser (export_cookies = True )
168+ captured_kwargs : dict = {}
169+
170+ def fake_browser_manager (** kwargs ):
171+ captured_kwargs .update (kwargs )
172+ return _BrowserContextManager (browser )
173+
174+ config = AppConfig ()
175+ config .browser .slow_mo = 250
176+
177+ _patch_login_deps (monkeypatch , browser_factory = fake_browser_manager , config = config )
178+
179+ await interactive_login (tmp_path / "profile" )
180+
181+ assert captured_kwargs .get ("slow_mo" ) == 250
182+
183+
184+ @pytest .mark .asyncio
185+ async def test_interactive_login_passes_user_agent_to_browser_manager (
186+ monkeypatch , tmp_path
187+ ):
188+ """When config.browser.user_agent is set, it must reach BrowserManager."""
189+ browser = _make_browser (export_cookies = True )
190+ captured_kwargs : dict = {}
191+
192+ def fake_browser_manager (** kwargs ):
193+ captured_kwargs .update (kwargs )
194+ return _BrowserContextManager (browser )
195+
196+ config = AppConfig ()
197+ config .browser .user_agent = "CustomAgent/1.0"
198+
199+ _patch_login_deps (monkeypatch , browser_factory = fake_browser_manager , config = config )
200+
201+ await interactive_login (tmp_path / "profile" )
202+
203+ assert captured_kwargs .get ("user_agent" ) == "CustomAgent/1.0"
204+
205+
206+ @pytest .mark .asyncio
207+ async def test_interactive_login_passes_viewport_to_browser_manager (
208+ monkeypatch , tmp_path
209+ ):
210+ """Non-default viewport_width/viewport_height must reach BrowserManager as viewport."""
211+ browser = _make_browser (export_cookies = True )
212+ captured_kwargs : dict = {}
213+
214+ def fake_browser_manager (** kwargs ):
215+ captured_kwargs .update (kwargs )
216+ return _BrowserContextManager (browser )
217+
218+ config = AppConfig ()
219+ config .browser .viewport_width = 1920
220+ config .browser .viewport_height = 1080
221+
222+ _patch_login_deps (monkeypatch , browser_factory = fake_browser_manager , config = config )
223+
224+ await interactive_login (tmp_path / "profile" )
225+
226+ assert captured_kwargs .get ("viewport" ) == {"width" : 1920 , "height" : 1080 }
0 commit comments