11from base .base import BasePage
22from pytest_check import check
3+ from config .constants import *
4+ import logging
5+ logger = logging .getLogger (__name__ )
6+ import time
37
48
59class DraftPage (BasePage ):
6- # Principal_Amount_and_Date = "div:nth-child(3) div:nth-child(2) span:nth-child(1) textarea:nth-child(1)"
7- # Borrower_Information = "div:nth-child(3) div:nth-child(2) span:nth-child(1) textarea:nth-child(1)"
8- # Payee_Information = "//div[3]//div[2]//span[1]//textarea[1]"
910 Draft_Sections = "//textarea"
1011 Draft_headings = "//span[@class='fui-Text ___nl2uoq0 fk6fouc f4ybsrx f1i3iumi f16wzh4i fpgzoln f1w7gpdv f6juhto f1gl81tg f2jf649 fepr9ql febqm8h']"
1112 invalid_response = "The requested information is not available in the retrieved data. Please try another query or topic."
1213 invalid_response1 = "There was an issue fetching your data. Please try again."
13- invalid_response2 = " "
14+
1415
1516 def __init__ (self , page ):
1617 self .page = page
1718
18- def check_draft_sections (self , timeout : float = 180.0 , poll_interval : float = 0.5 ):
19- """
20- Waits for all <textarea> draft sections to load valid content using .input_value().
21- Scrolls into view if needed, retries until timeout.
22- Raises clear errors if validation fails.
23- """
24- import time
25- from collections import defaultdict
19+ def validate_draft_sections_loaded (self ):
20+ max_wait_time = 180 # seconds
21+ poll_interval = 2
2622
27- start_time = time . time ( )
23+ self . page . wait_for_timeout ( 25000 )
2824
29- while time . time () - start_time < timeout :
30- section_elements = self .page .locator (self . Draft_Sections )
31- heading_elements = self . page . locator ( self . Draft_headings )
25+ # All draft section containers
26+ section_blocks = self .page .locator ("//div[@class='ms-Stack ___mit7380 f4zyqsv f6m9rw3 fwbpcpn folxr9a f1s274it css-103']" )
27+ total_sections = section_blocks . count ( )
3228
33- section_count = section_elements .count ()
34- heading_count = heading_elements .count ()
29+ logger .info (f"🔍 Total sections found: { total_sections } " )
3530
36- if section_count < 13 or heading_count < 13 :
37- print ("[WAIT] Waiting for all sections to appear..." )
38- time .sleep (poll_interval )
39- continue
31+ for index in range (total_sections ):
32+ section = section_blocks .nth (index )
4033
41- failed_sections = defaultdict (str )
34+ try :
35+ section .scroll_into_view_if_needed ()
36+ self .page .wait_for_timeout (500 )
4237
43- for i in range (section_count ):
44- section = section_elements .nth (i )
38+ title_element = section .locator ("//span[@class='fui-Text ___nl2uoq0 fk6fouc f4ybsrx f1i3iumi f16wzh4i fpgzoln f1w7gpdv f6juhto f1gl81tg f2jf649 fepr9ql febqm8h']" )
39+ title_text = title_element .inner_text (timeout = 5000 ).strip ()
40+ except Exception as e :
41+ logger .error (f"❌ Could not read title for section #{ index + 1 } : { e } " )
42+ continue
4543
44+ logger .info (f"➡️ Validating section [{ index + 1 } /{ total_sections } ]: '{ title_text } '" )
45+
46+ content_locator = section .locator ("//textarea" )
47+ generate_btn = section .locator ("//span[@class='fui-Button__icon rywnvv2 ___963sj20 f1nizpg2']" )
48+ spinner_locator = section .locator ("//div[@id='section-card-spinner']" )
49+
50+ content_loaded = False
51+
52+ # 🚨 If spinner is visible inside this section, click generate immediately
53+ try :
54+ if spinner_locator .is_visible (timeout = 1000 ):
55+ logger .warning (f"⏳ Spinner found in section '{ title_text } '. Clicking Generate immediately." )
56+ generate_btn .click ()
57+ self .page .wait_for_timeout (3000 )
58+ confirm_btn = self .page .locator ("//button[@class='fui-Button r1alrhcs ___zqkcn80 fd1o0ie fjxutwb fwiml72 fj8njcf fzcpov4 f1d2rq10 f1mk8lai ff3glw6']" )
59+ if confirm_btn .is_visible (timeout = 3000 ):
60+ confirm_btn .click ()
61+ logger .info (f"🟢 Clicked Confirm button for section '{ title_text } '" )
62+ else :
63+ logger .warning (f"⚠️ Confirm button not visible for section '{ title_text } '" )
64+ except Exception as e :
65+ logger .error (f"❌ Error while clicking Confirm button for section '{ title_text } ': { e } " )
66+
67+ # ⏳ Retry short wait (15s) for content to load
68+ short_wait = 15
69+ short_start = time .time ()
70+ while time .time () - short_start < short_wait :
4671 try :
47- # Scroll into view and wait a bit for rendering
48- section .scroll_into_view_if_needed (timeout = 2000 )
49- self .page .wait_for_timeout (200 )
50-
51- # Extract content from <textarea>
52- section_text = section .input_value ().strip ()
72+ content = content_locator .text_content (timeout = 2000 ).strip ()
73+ if content :
74+ logger .info (f"✅ Section '{ title_text } ' loaded after Generate + Confirm." )
75+ content_loaded = True
76+ break
77+ except :
78+ pass
79+ time .sleep (1 )
80+
81+ if not content_loaded :
82+ logger .error (f"❌ Section '{ title_text } ' still empty after Generate + Confirm wait ({ short_wait } s). Skipping." )
83+
84+ # Step 1: Wait for content to load normally
85+ start = time .time ()
86+ while time .time () - start < max_wait_time :
87+ try :
88+ content = content_locator .text_content (timeout = 2000 ).strip ()
89+ if content :
90+ logger .info (f"✅ Section '{ title_text } ' loaded successfully." )
91+ content_loaded = True
92+ break
93+ except :
94+ pass
95+ time .sleep (poll_interval )
5396
54- if not section_text :
55- failed_sections [i ] = "Empty"
56- elif section_text in (
57- self .invalid_response ,
58- self .invalid_response1 ,
59- ):
60- failed_sections [i ] = f"Invalid: { repr (section_text [:30 ])} "
97+ # Step 2: If still not loaded, click Generate and retry
98+ if not content_loaded :
99+ logger .warning (f"⚠️ Section '{ title_text } ' is empty. Attempting 'Generate'..." )
61100
101+ try :
102+ generate_btn .click ()
103+ logger .info (f"🔄 Clicked 'Generate' for section '{ title_text } '" )
62104 except Exception as e :
63- failed_sections [i ] = f"Exception: { str (e )} "
64-
65- if not failed_sections :
66- break # ✅ All good
67- else :
68- print (f"[WAITING] Sections not ready yet: { failed_sections } " )
69- time .sleep (poll_interval )
70-
71- else :
72- raise TimeoutError (
73- f"❌ Timeout: These sections did not load valid content: { failed_sections } "
74- )
75-
76- # ✅ Final validations after loading
77- for i in range (section_count ):
78- section = section_elements .nth (i )
79- heading = heading_elements .nth (i )
80-
81- section .scroll_into_view_if_needed (timeout = 2000 )
82- self .page .wait_for_timeout (200 )
83-
84- heading_text = heading .inner_text (timeout = 3000 ).strip ()
85- content = section .input_value ().strip ()
86-
87- print (
88- f"[VALIDATING] Section { i } : '{ heading_text } ' → { repr (content [:60 ])} ..."
89- )
90-
91- with check :
92- check .is_not_none (content , f"❌ Section '{ heading_text } ' is None" )
93- check .not_equal (content , "" , f"❌ Section '{ heading_text } ' is empty" )
94- check .not_equal (
95- content ,
96- self .invalid_response ,
97- f"❌ '{ heading_text } ' has invalid response" ,
98- )
99- check .not_equal (
100- content ,
101- self .invalid_response1 ,
102- f"❌ '{ heading_text } ' has invalid response" ,
103- )
105+ logger .error (f"❌ Failed to click 'Generate' for section '{ title_text } ': { e } " )
106+ continue
107+
108+ # Retry wait
109+ start = time .time ()
110+ while time .time () - start < max_wait_time :
111+ try :
112+ content = content_locator .text_content (timeout = 2000 ).strip ()
113+ if content :
114+ logger .info (f"✅ Section '{ title_text } ' loaded after clicking Generate." )
115+ content_loaded = True
116+ break
117+ except :
118+ pass
119+ time .sleep (poll_interval )
120+
121+ if not content_loaded :
122+ logger .error (f"❌ Section '{ title_text } ' still empty after retrying." )
123+
124+ # Optional: take screenshot
125+ screenshot_dir = "screenshots"
126+ os .makedirs (screenshot_dir , exist_ok = True )
127+ screenshot_path = os .path .join (screenshot_dir , f"section_{ index + 1 } _{ title_text .replace (' ' , '_' )} .png" )
128+ try :
129+ section .screenshot (path = screenshot_path )
130+ logger .error (f"📸 Screenshot saved: { screenshot_path } " )
131+ except Exception as e :
132+ logger .error (f"❌ Generate click failed in section '{ title_text } ': { e } " )
133+ continue
134+
135+ try :
136+ content = content_locator .text_content (timeout = 2000 ).strip ()
137+ with check :
138+ if content == self .invalid_response or content == self .invalid_response1 :
139+ logger .warning (f"❌ Invalid response found in '{ title_text } '. Retrying Generate + Confirm..." )
140+
141+ try :
142+ generate_btn .click ()
143+ self .page .wait_for_timeout (3000 )
144+
145+ confirm_btn = self .page .locator ("//button[@class='fui-Button r1alrhcs ___zqkcn80 fd1o0ie fjxutwb fwiml72 fj8njcf fzcpov4 f1d2rq10 f1mk8lai ff3glw6']" )
146+ if confirm_btn .is_visible (timeout = 3000 ):
147+ confirm_btn .click ()
148+ logger .info (f"🟢 Retried Confirm for section '{ title_text } '" )
149+ else :
150+ logger .warning (f"⚠️ Confirm button not visible during retry for '{ title_text } '" )
151+ except Exception as e :
152+ logger .error (f"❌ Retry Generate/Confirm failed: { e } " )
153+
154+ retry_start = time .time ()
155+ while time .time () - retry_start < short_wait :
156+ try :
157+ content = content_locator .text_content (timeout = 2000 ).strip ()
158+ if content and content not in [self .invalid_response , self .invalid_response1 ]:
159+ logger .info (f"✅ Section '{ title_text } ' fixed after retry." )
160+ break
161+ except :
162+ pass
163+ time .sleep (1 )
164+
165+ with check :
166+ check .not_equal (content , self .invalid_response , f"❌ '{ title_text } ' still has invalid response after retry" )
167+ check .not_equal (content , self .invalid_response1 , f"❌ '{ title_text } ' still has invalid response after retry" )
168+
169+ else :
170+ logger .info (f"🎯 Section '{ title_text } ' has valid content." )
171+ except Exception as e :
172+ logger .error (f"❌ Could not validate content for '{ title_text } ': { e } " )
173+
174+ logger .info (f"✔️ Completed section: '{ title_text } '\n " )
0 commit comments