1+ import os
2+ import ast
3+ import itertools
4+
5+ files_to_skip = [
6+ # Console Domain Disabled
7+ 'test_console.py' ,
8+
9+ # query_selector is deprecated
10+ 'test_element_handle.py' ,
11+ 'test_element_handle_wait_for_element_state.py' ,
12+
13+ # https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/issues/31
14+ 'test_route_web_socket.py'
15+ ]
16+
17+ tests_to_skip = [
18+ # Disabled Console Domain
19+ "test_block_blocks_service_worker_registration" ,
20+ "test_console_event_should_work" ,
21+ "test_console_event_should_work_in_popup" ,
22+ "test_console_event_should_work_in_popup_2" ,
23+ "test_console_event_should_work_in_immediately_closed_popup" ,
24+ "test_dialog_event_should_work_in_immdiately_closed_popup" ,
25+ "test_console_event_should_work_with_context_manager" ,
26+ "test_weberror_event_should_work" ,
27+ "test_console_repr" ,
28+ "test_console_should_work" ,
29+ "test_should_collect_trace_with_resources_but_no_js" ,
30+ "test_should_work_with_playwright_context_managers" ,
31+ "test_should_respect_traces_dir_and_name" ,
32+ "test_should_show_tracing_group_in_action_list" ,
33+ "test_page_error_event_should_work" ,
34+ "test_click_offscreen_buttons" ,
35+ "test_watch_position_should_be_notified" ,
36+ "test_page_error_should_fire" ,
37+ "test_page_error_should_handle_odd_values" ,
38+ "test_page_error_should_handle_object" ,
39+ "test_page_error_should_handle_window" ,
40+ "test_page_error_should_pass_error_name_property" ,
41+ "test_workers_should_report_console_logs" ,
42+ "test_workers_should_have_JSHandles_for_console_logs" ,
43+ "test_workers_should_report_errors" ,
44+
45+ # InitScript Timing
46+ "test_expose_function_should_be_callable_from_inside_add_init_script" ,
47+ "test_expose_bindinghandle_should_work" ,
48+ "test_browser_context_add_init_script_should_apply_to_an_in_process_popup" ,
49+ "test_should_expose_function_from_browser_context" ,
50+
51+ # Disable Popup Blocking
52+ "test_page_event_should_have_an_opener" ,
53+
54+ # query_selector is deprecated
55+ "test_should_work_with_layout_selectors" ,
56+ "test_should_dispatch_click_event_element_handle" ,
57+ "test_should_dispatch_drag_and_drop_events_element_handle" ,
58+
59+ # Minor Differences in Call Log. Deemed Unimportant
60+ "test_should_be_attached_fail_with_not" ,
61+ "test_add_script_tag_should_include_source_url_when_path_is_provided" ,
62+
63+ # Server/Client Header Mismatch
64+ "test_should_report_request_headers_array" ,
65+ "test_request_headers_should_get_the_same_headers_as_the_server_cors" ,
66+ "test_request_headers_should_get_the_same_headers_as_the_server" ,
67+ ]
68+
69+ dont_isolate_evaluation_tests = [
70+ "test_timeout_waiting_for_stable_position" ,
71+ "test_jshandle_evaluate_accept_object_handle_as_argument" ,
72+ "test_jshandle_evaluate_accept_nested_handle" ,
73+ "test_jshandle_evaluate_accept_nested_window_handle" ,
74+ "test_jshandle_evaluate_accept_multiple_nested_handles" ,
75+ "test_should_dispatch_drag_drop_events" ,
76+ "test_should_dispatch_drag_and_drop_events_element_handle" ,
77+ "track_events" ,
78+ "captureLastKeydown" ,
79+ "test_expose_function_should_work_on_frames_before_navigation" ,
80+ ]
81+
82+ # Directory containing the test files
83+ async_test_dir = 'tests/async'
84+ sync_test_dir = 'tests/sync'
85+
86+ # Reason for skipping tests_backup
87+ skip_reason = "Skipped as per documentation (https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/issues/31)"
88+
89+
90+ class ParentAnnotator (ast .NodeVisitor ):
91+ def __init__ (self ):
92+ self .parents = {}
93+
94+ def visit (self , node ):
95+ for child in ast .iter_child_nodes (node ):
96+ self .parents [child ] = node
97+ self .visit (child )
98+
99+ def process_file (file_path ):
100+ with open (file_path , 'r' , encoding = 'utf-8' ) as f :
101+ source = f .read ()
102+
103+ file_tree = ast .parse (source )
104+ annotator = ParentAnnotator ()
105+ annotator .visit (file_tree )
106+
107+ for node in ast .walk (file_tree ):
108+ # Rename Playwright Imports to Patchright
109+ if isinstance (node , ast .Import ):
110+ for alias in node .names :
111+ if alias .name .startswith ("playwright" ):
112+ alias .name = alias .name .replace ("playwright" , "patchright" , 1 )
113+ if isinstance (node , ast .ImportFrom ) and node .module .startswith ("playwright" ):
114+ node .module = node .module .replace ("playwright" , "patchright" , 1 )
115+
116+ # Skip Tests Documented: https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/issues/31
117+ if isinstance (node , ast .FunctionDef ) or isinstance (node , ast .AsyncFunctionDef ):
118+ # Add Server Arg to test_add_init_script Tests
119+ if "test_add_init_script" in file_path :
120+ node .args .args .append (ast .arg (arg = 'server' , annotation = None ))
121+ # Skip Tests to skip
122+ if node .name in tests_to_skip :
123+ skip_decorator = ast .parse (f"@pytest.mark.skip(reason='{ skip_reason } ')\n def placeholder(): return" ).body [0 ].decorator_list [0 ]
124+ node .decorator_list .insert (0 , skip_decorator )
125+
126+ # Add isolated_context=False to every necessary evaluate/evaluate_handle Call
127+ if isinstance (node , ast .Call ) and isinstance (node .func , ast .Attribute ):
128+ # Very Bad Hack to get the parent node
129+ test_name = ""
130+ current_node = node
131+ while annotator .parents .get (current_node ):
132+ current_node = annotator .parents [current_node ]
133+ if isinstance (current_node , ast .FunctionDef ) or isinstance (current_node , ast .AsyncFunctionDef ):
134+ test_name = current_node .name
135+
136+ if test_name in dont_isolate_evaluation_tests :
137+ # Don't add isolated_context=False to these tests
138+ continue
139+
140+ if node .func .attr in ("evaluate" , "evaluate_handle" , "evaluate_all" ) and isinstance (node .func .value , ast .Name ) and node .func .value .id in ("page" , "popup" , "button" , "new_page" , "page1" , "page2" , "target" , "page_1" , "page_2" , "frame" ):
141+ node .keywords .append (ast .keyword (arg = 'isolated_context' , value = ast .Constant (value = False )))
142+
143+ modified_source = ast .unparse (ast .fix_missing_locations (file_tree ))
144+
145+ with open (file_path , 'w' , encoding = 'utf-8' ) as f :
146+ f .write (modified_source )
147+
148+ def main ():
149+ with open ("./tests/assets/inject.html" , "w" ) as f :
150+ f .write ("<script>window.result = window.injected;</script>" )
151+
152+ for root , _ , files in itertools .chain (os .walk (sync_test_dir ), os .walk (async_test_dir )):
153+ for file in files :
154+ file_path = os .path .join (root , file )
155+
156+ # Init Script Behaviour https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/issues/30
157+ if file == "test_add_init_script.py" :
158+ with open (file_path , 'r' , encoding = 'utf-8' ) as f :
159+ content = f .read ()
160+
161+ # Replace the full quoted strings with valid Python expressions (not strings)
162+ content = content .replace (
163+ '"data:text/html,<script>window.result = window.injected</script>"' ,
164+ 'server.PREFIX + "/inject.html"'
165+ ).replace (
166+ '"data:text/html,<html></html>"' ,
167+ 'server.PREFIX + "/empty.html"'
168+ )
169+
170+ with open (file_path , 'w' , encoding = 'utf-8' ) as f :
171+ f .write (content )
172+
173+ # Init Script Behaviour https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/issues/30
174+ if file == "test_page_clock.py" :
175+ with open (file_path , 'r' , encoding = 'utf-8' ) as f :
176+ content = f .read ()
177+
178+ # Replace the full quoted strings with valid Python expressions (not strings)
179+ content = content .replace (
180+ "about:blank" ,
181+ "https://www.google.com/blank.html"
182+ ).replace (
183+ "data:text/html," ,
184+ "https://www.google.com/blank.html"
185+ )
186+
187+ with open (file_path , 'w' , encoding = 'utf-8' ) as f :
188+ f .write (content )
189+
190+ # Import Pytest
191+ if file in ("test_browsercontext_service_worker_policy.py" , "test_tracing.py" , "test_popup.py" , "test_dispatch_event.py" ):
192+ # Append "import pytest" to the top of the file
193+ with open (file_path , 'r+' , encoding = 'utf-8' ) as f :
194+ content = f .read ()
195+ if "import pytest" not in content :
196+ f .seek (0 , 0 )
197+ f .write ("import pytest\n " + content )
198+
199+ # Skipping Files
200+ if file in files_to_skip :
201+ # Delete File
202+ os .remove (file_path )
203+ continue
204+
205+ if file .endswith ('.py' ):
206+ process_file (file_path )
207+
208+ if __name__ == '__main__' :
209+ main ()
0 commit comments