@@ -218,7 +218,12 @@ def _compute_relative_app_path(
218218 self , app_file_path : Path , test_file_path : Path
219219 ) -> str :
220220 """Compute POSIX-style relative path from the test file directory to the app file."""
221- rel = os .path .relpath (str (app_file_path ), start = str (test_file_path .parent ))
221+ # Make sure both paths are absolute
222+ app_file_abs = app_file_path .resolve ()
223+ test_file_abs = test_file_path .resolve ()
224+
225+ # Compute relative path from test file directory to app file
226+ rel = os .path .relpath (str (app_file_abs ), start = str (test_file_abs .parent ))
222227 return Path (rel ).as_posix ()
223228
224229 def _rewrite_fixture_path (self , test_code : str , relative_app_path : str ) -> str :
@@ -229,27 +234,70 @@ def _rewrite_fixture_path(self, test_code: str, relative_app_path: str) -> str:
229234 - create_app_fixture("app.py") -> create_app_fixture("../app.py")
230235 Keeps other arguments intact if present.
231236 """
237+ logging .debug (f"Rewriting fixture path to: { relative_app_path } " )
238+
239+ # First check if create_app_fixture exists in the code
240+ if "create_app_fixture" not in test_code :
241+ logging .warning ("No create_app_fixture found in generated test code" )
242+ return test_code
243+
232244 # Pattern for list form: create_app_fixture(["app.py"]) or with spaces
233245 pattern_list = re .compile (
234- r"(create_app_fixture\(\s*\[\s*)(['\"])([^'\"]+)(\\2)(\\s*)([,\\ ]])" ,
246+ r"(create_app_fixture\(\s*\[\s*)(['\"])([^'\"]+)(\\2)(\\s*)([,\]])" ,
235247 re .DOTALL ,
236248 )
237249
238250 def repl_list (m : re .Match ) -> str :
251+ logging .debug (
252+ f"Replacing list form: '{ m .group (3 )} ' with '{ relative_app_path } '"
253+ )
239254 return f"{ m .group (1 )} { m .group (2 )} { relative_app_path } { m .group (2 )} { m .group (5 )} { m .group (6 )} "
240255
241- new_code , _ = pattern_list .subn (repl_list , test_code )
256+ new_code , list_count = pattern_list .subn (repl_list , test_code )
257+
258+ if list_count > 0 :
259+ logging .debug (f"Replaced { list_count } list-form fixture path(s)" )
242260
243261 # Pattern for direct string form: create_app_fixture("app.py")
244262 pattern_str = re .compile (
245- r"(create_app_fixture\(\s*)(['\"])([^'\"]+)(\\2)(\\s*)([,\\ )])" ,
263+ r"(create_app_fixture\(\s*)(['\"])([^'\"]+)(\\2)(\\s*)([,\)])" ,
246264 re .DOTALL ,
247265 )
248266
249267 def repl_str (m : re .Match ) -> str :
268+ logging .debug (
269+ f"Replacing string form: '{ m .group (3 )} ' with '{ relative_app_path } '"
270+ )
250271 return f"{ m .group (1 )} { m .group (2 )} { relative_app_path } { m .group (2 )} { m .group (5 )} { m .group (6 )} "
251272
252- new_code2 , _ = pattern_str .subn (repl_str , new_code )
273+ new_code2 , str_count = pattern_str .subn (repl_str , new_code )
274+
275+ if str_count > 0 :
276+ logging .debug (f"Replaced { str_count } string-form fixture path(s)" )
277+
278+ # If no replacements were made, there might be a pattern we're not catching
279+ if list_count == 0 and str_count == 0 :
280+ logging .warning (
281+ f"Found create_app_fixture but couldn't replace path. Code snippet: { test_code [:200 ]} ..."
282+ )
283+
284+ # Fallback regex with more generous pattern matching
285+ fallback_pattern = re .compile (
286+ r"(create_app_fixture\([^\)]*?['\"])([^'\"]+)(['\"][^\)]*?\))" ,
287+ re .DOTALL ,
288+ )
289+
290+ def fallback_repl (m : re .Match ) -> str :
291+ logging .debug (
292+ f"Fallback replacement: '{ m .group (2 )} ' with '{ relative_app_path } '"
293+ )
294+ return f"{ m .group (1 )} { relative_app_path } { m .group (3 )} "
295+
296+ new_code2 , fallback_count = fallback_pattern .subn (fallback_repl , new_code )
297+
298+ if fallback_count > 0 :
299+ logging .debug (f"Fallback replaced { fallback_count } fixture path(s)" )
300+
253301 return new_code2
254302
255303 def _create_test_prompt (self , app_text : str , app_file_name : str ) -> str :
@@ -259,19 +307,22 @@ def _create_test_prompt(self, app_text: str, app_file_name: str) -> str:
259307 "Please only add controllers for components that already have an ID in the shiny app.\n "
260308 "Do not add tests for ones that do not have an existing ids since controllers need IDs to locate elements.\n "
261309 "and server functionality of this app. Include appropriate assertions \\ n"
262- "and test cases to verify the app's behavior.\n "
263- "IMPORTANT: In the create_app_fixture call, pass a RELATIVE path from the test file's directory to the app file.\n "
264- "For example, if the test lives under a 'tests/' subfolder next to the app file, use '../"
265- + app_file_name
266- + "'. Do not use absolute paths.\n "
310+ "and test cases to verify the app's behavior.\n \n "
311+ "CRITICAL: In the create_app_fixture call, you MUST pass a RELATIVE path from the test file's directory to the app file.\n "
312+ "For example:\n "
313+ "- If test is in 'tests/test_app.py' and app is in 'app.py', use: '../app.py'\n "
314+ "- If test is in 'tests/subdir/test_app.py' and app is in 'apps/subdir/app.py', use: '../../apps/subdir/app.py'\n "
315+ "- Always compute the correct relative path from the test file to the app file\n "
316+ "- NEVER use absolute paths or paths that aren't relative from the test location\n \n "
267317 "IMPORTANT: Only output the Python test code in a single code block. Do not include any explanation, justification, or extra text."
268318 )
269319
270320 def _infer_app_file_path (
271321 self , app_code : Optional [str ] = None , app_file_path : Optional [str ] = None
272322 ) -> Path :
273323 if app_file_path :
274- return Path (app_file_path )
324+ # Return absolute path to avoid any ambiguity
325+ return Path (app_file_path ).resolve ()
275326
276327 current_dir = Path .cwd ()
277328
@@ -280,10 +331,12 @@ def _infer_app_file_path(
280331 found_files .extend (current_dir .glob (pattern ))
281332
282333 if found_files :
283- return found_files [0 ]
334+ # Return absolute path of found file
335+ return found_files [0 ].resolve ()
284336
285337 if app_code :
286- return Path ("inferred_app.py" )
338+ # For inferred app paths, use absolute path in current directory
339+ return Path ("inferred_app.py" ).resolve ()
287340
288341 raise FileNotFoundError (
289342 "Could not infer app file path. Please provide app_file_path parameter."
@@ -294,7 +347,8 @@ def _generate_test_file_path(
294347 ) -> Path :
295348 output_dir = output_dir or app_file_path .parent
296349 test_file_name = f"test_{ app_file_path .stem } .py"
297- return output_dir / test_file_name
350+ # Return absolute path for test file
351+ return (output_dir / test_file_name ).resolve ()
298352
299353 def generate_test (
300354 self ,
@@ -328,12 +382,41 @@ def generate_test(
328382 )
329383
330384 try :
385+ # Log the paths for debugging
386+ logging .info (f"App file path: { inferred_app_path } " )
387+ logging .info (f"Test file path: { test_file_path } " )
388+
331389 relative_app_path = self ._compute_relative_app_path (
332390 inferred_app_path , test_file_path
333391 )
392+
393+ logging .info (f"Computed relative path: { relative_app_path } " )
394+
395+ # Explicitly check for app.py - this is a common problematic case
396+ if relative_app_path == "app.py" and "../" not in relative_app_path :
397+ logging .warning (
398+ f"Detected possibly incorrect relative path: { relative_app_path } "
399+ )
400+ # Force a proper relative path if needed
401+ if test_file_path .parent != inferred_app_path .parent :
402+ logging .info (
403+ "Test and app are in different directories, adjusting relative path"
404+ )
405+ relative_app_path = f"../{ relative_app_path } "
406+ logging .info (f"Adjusted relative path: { relative_app_path } " )
407+
334408 test_code = self ._rewrite_fixture_path (test_code , relative_app_path )
335- except Exception :
336- pass
409+ except Exception as e :
410+ logging .error (f"Error computing relative path: { e } " )
411+ # Don't silently ignore - use the best path we can
412+ try :
413+ # Fallback: just use the absolute path as string if we can't compute relative
414+ logging .warning ("Falling back to using absolute path in test file" )
415+ test_code = self ._rewrite_fixture_path (
416+ test_code , str (inferred_app_path .resolve ())
417+ )
418+ except Exception as e2 :
419+ logging .error (f"Error in fallback path handling: { e2 } " )
337420
338421 test_file_path .parent .mkdir (parents = True , exist_ok = True )
339422 test_file_path .write_text (test_code , encoding = "utf-8" )
0 commit comments