@@ -94,6 +94,223 @@ public void CandidatesForChannel_Firefox_DoesNotOverlapChrome()
9494
9595 CollectionAssert . AreNotEqual ( chrome . ToList ( ) , firefox . ToList ( ) ) ;
9696 }
97+
98+ [ TestMethod ]
99+ public void CandidatesForChannel_Edge_ReturnsNonEmptyList ( )
100+ {
101+ var candidates = BrowserFinder . CandidatesForChannel ( BrowserChannel . Edge ) ;
102+
103+ Assert . IsTrue ( candidates . Count > 0 , "Should return at least one Edge candidate path." ) ;
104+ }
105+
106+ [ TestMethod ]
107+ public void CandidatesForChannel_Chromium_ReturnsNonEmptyList ( )
108+ {
109+ var candidates = BrowserFinder . CandidatesForChannel ( BrowserChannel . Chromium ) ;
110+
111+ Assert . IsTrue ( candidates . Count > 0 , "Should return at least one Chromium candidate path." ) ;
112+ }
113+
114+ [ TestMethod ]
115+ public void CandidatesForChannel_Edge_IncludesInstalledBinariesPath_WhenSet ( )
116+ {
117+ var originalPath = BrowserFinder . InstalledBinariesPath ;
118+ try
119+ {
120+ BrowserFinder . InstalledBinariesPath = "/opt/motus/browsers" ;
121+ var candidates = BrowserFinder . CandidatesForChannel ( BrowserChannel . Edge ) ;
122+
123+ Assert . IsTrue ( candidates . Any ( c => c . StartsWith ( "/opt/motus/browsers" ) ) ,
124+ "Should include installed binaries path for Edge when set." ) ;
125+ Assert . IsTrue ( candidates . Any ( c => c . Contains ( "msedge" ) ) ,
126+ "Edge candidate should contain 'msedge' in the path." ) ;
127+ }
128+ finally
129+ {
130+ BrowserFinder . InstalledBinariesPath = originalPath ;
131+ }
132+ }
133+
134+ [ TestMethod ]
135+ public void CandidatesForChannel_Chromium_IncludesInstalledBinariesPath_WhenSet ( )
136+ {
137+ var originalPath = BrowserFinder . InstalledBinariesPath ;
138+ try
139+ {
140+ BrowserFinder . InstalledBinariesPath = "/opt/motus/browsers" ;
141+ var candidates = BrowserFinder . CandidatesForChannel ( BrowserChannel . Chromium ) ;
142+
143+ Assert . IsTrue ( candidates . Any ( c => c . StartsWith ( "/opt/motus/browsers" ) ) ,
144+ "Should include installed binaries path for Chromium when set." ) ;
145+ Assert . IsTrue ( candidates . Any ( c => c . Contains ( "chromium" ) ) ,
146+ "Chromium candidate should contain 'chromium' in the path." ) ;
147+ }
148+ finally
149+ {
150+ BrowserFinder . InstalledBinariesPath = originalPath ;
151+ }
152+ }
153+
154+ [ TestMethod ]
155+ public void CandidatesForChannel_AllChannels_DoNotOverlap ( )
156+ {
157+ var chrome = BrowserFinder . CandidatesForChannel ( BrowserChannel . Chrome ) ;
158+ var edge = BrowserFinder . CandidatesForChannel ( BrowserChannel . Edge ) ;
159+ var chromium = BrowserFinder . CandidatesForChannel ( BrowserChannel . Chromium ) ;
160+ var firefox = BrowserFinder . CandidatesForChannel ( BrowserChannel . Firefox ) ;
161+
162+ // With InstalledBinariesPath cleared, platform-specific paths should be distinct
163+ var originalPath = BrowserFinder . InstalledBinariesPath ;
164+ try
165+ {
166+ BrowserFinder . InstalledBinariesPath = null ;
167+ chrome = BrowserFinder . CandidatesForChannel ( BrowserChannel . Chrome ) ;
168+ edge = BrowserFinder . CandidatesForChannel ( BrowserChannel . Edge ) ;
169+ chromium = BrowserFinder . CandidatesForChannel ( BrowserChannel . Chromium ) ;
170+ firefox = BrowserFinder . CandidatesForChannel ( BrowserChannel . Firefox ) ;
171+
172+ var all = chrome . Concat ( edge ) . Concat ( chromium ) . Concat ( firefox ) . ToList ( ) ;
173+ var distinct = all . Distinct ( ) . ToList ( ) ;
174+ Assert . AreEqual ( distinct . Count , all . Count , "Candidate paths across channels should be unique." ) ;
175+ }
176+ finally
177+ {
178+ BrowserFinder . InstalledBinariesPath = originalPath ;
179+ }
180+ }
181+
182+ [ TestMethod ]
183+ public void Resolve_WithChannel_ExercisesChannelPath ( )
184+ {
185+ var originalPath = BrowserFinder . InstalledBinariesPath ;
186+ try
187+ {
188+ BrowserFinder . InstalledBinariesPath = "/nonexistent/motus/browsers" ;
189+ try
190+ {
191+ var result = BrowserFinder . Resolve ( channel : BrowserChannel . Firefox , executablePath : null ) ;
192+ // If we reach here, a real Firefox is installed at a system path
193+ Assert . IsTrue ( File . Exists ( result ) ) ;
194+ }
195+ catch ( FileNotFoundException )
196+ {
197+ // Expected when no Firefox is installed
198+ }
199+ }
200+ finally
201+ {
202+ BrowserFinder . InstalledBinariesPath = originalPath ;
203+ }
204+ }
205+
206+ [ TestMethod ]
207+ public void Resolve_WithChannel_UsesExistingBinary ( )
208+ {
209+ var existingFile = typeof ( BrowserFinderTests ) . Assembly . Location ;
210+ var originalPath = BrowserFinder . InstalledBinariesPath ;
211+ try
212+ {
213+ // Point InstalledBinariesPath to the directory containing the test assembly,
214+ // and rename expectation to match the binary name pattern
215+ var dir = Path . GetDirectoryName ( existingFile ) ! ;
216+
217+ // Create a temp file named "chrome" (or "chrome.exe" on Windows) in a temp dir
218+ var tempDir = Path . Combine ( Path . GetTempPath ( ) , $ "motus-test-{ Guid . NewGuid ( ) : N} ") ;
219+ Directory . CreateDirectory ( tempDir ) ;
220+ var fakeBrowser = Path . Combine ( tempDir ,
221+ OperatingSystem . IsWindows ( ) ? "chrome.exe" : "chrome" ) ;
222+ File . WriteAllText ( fakeBrowser , "fake" ) ;
223+
224+ BrowserFinder . InstalledBinariesPath = tempDir ;
225+ var result = BrowserFinder . Resolve ( channel : BrowserChannel . Chrome , executablePath : null ) ;
226+
227+ Assert . AreEqual ( fakeBrowser , result ) ;
228+
229+ // Cleanup
230+ File . Delete ( fakeBrowser ) ;
231+ Directory . Delete ( tempDir ) ;
232+ }
233+ finally
234+ {
235+ BrowserFinder . InstalledBinariesPath = originalPath ;
236+ }
237+ }
238+
239+ [ TestMethod ]
240+ public void Resolve_AutoDetect_FindsFirstAvailableBrowser ( )
241+ {
242+ var originalPath = BrowserFinder . InstalledBinariesPath ;
243+ try
244+ {
245+ var tempDir = Path . Combine ( Path . GetTempPath ( ) , $ "motus-test-{ Guid . NewGuid ( ) : N} ") ;
246+ Directory . CreateDirectory ( tempDir ) ;
247+ var fakeBrowser = Path . Combine ( tempDir ,
248+ OperatingSystem . IsWindows ( ) ? "chrome.exe" : "chrome" ) ;
249+ File . WriteAllText ( fakeBrowser , "fake" ) ;
250+
251+ BrowserFinder . InstalledBinariesPath = tempDir ;
252+ // channel: null, executablePath: null triggers auto-detect
253+ var result = BrowserFinder . Resolve ( channel : null , executablePath : null ) ;
254+
255+ Assert . AreEqual ( fakeBrowser , result , "Auto-detect should find Chrome first." ) ;
256+
257+ File . Delete ( fakeBrowser ) ;
258+ Directory . Delete ( tempDir ) ;
259+ }
260+ finally
261+ {
262+ BrowserFinder . InstalledBinariesPath = originalPath ;
263+ }
264+ }
265+
266+ [ TestMethod ]
267+ public void Resolve_AutoDetect_ExercisesAutoDetectPath ( )
268+ {
269+ var originalPath = BrowserFinder . InstalledBinariesPath ;
270+ try
271+ {
272+ var tempDir = Path . Combine ( Path . GetTempPath ( ) , $ "motus-test-{ Guid . NewGuid ( ) : N} ") ;
273+ Directory . CreateDirectory ( tempDir ) ;
274+
275+ BrowserFinder . InstalledBinariesPath = tempDir ;
276+ try
277+ {
278+ var result = BrowserFinder . Resolve ( channel : null , executablePath : null ) ;
279+ // If we reach here, a real browser is installed at a system path
280+ Assert . IsTrue ( File . Exists ( result ) ) ;
281+ }
282+ catch ( FileNotFoundException )
283+ {
284+ // Expected when no browser is installed at system paths
285+ }
286+ finally
287+ {
288+ Directory . Delete ( tempDir ) ;
289+ }
290+ }
291+ finally
292+ {
293+ BrowserFinder . InstalledBinariesPath = originalPath ;
294+ }
295+ }
296+
297+ [ TestMethod ]
298+ public void InstalledBinariesPath_Null_DoesNotPrependPath ( )
299+ {
300+ var originalPath = BrowserFinder . InstalledBinariesPath ;
301+ try
302+ {
303+ BrowserFinder . InstalledBinariesPath = null ;
304+ var candidates = BrowserFinder . CandidatesForChannel ( BrowserChannel . Chrome ) ;
305+
306+ // All candidates should be platform-specific system paths, not prepended
307+ Assert . IsTrue ( candidates . All ( c => ! string . IsNullOrWhiteSpace ( c ) ) ) ;
308+ }
309+ finally
310+ {
311+ BrowserFinder . InstalledBinariesPath = originalPath ;
312+ }
313+ }
97314}
98315
99316[ TestClass ]
0 commit comments