11from base .base import BasePage
22from pytest_check import check
3+ from config .constants import *
4+ import logging
5+ logger = logging .getLogger (__name__ )
36import time
4- from collections import defaultdict
57
68
79class DraftPage (BasePage ):
8- # Principal_Amount_and_Date = "div:nth-child(3) div:nth-child(2) span:nth-child(1) textarea:nth-child(1)"
9- # Borrower_Information = "div:nth-child(3) div:nth-child(2) span:nth-child(1) textarea:nth-child(1)"
10- # Payee_Information = "//div[3]//div[2]//span[1]//textarea[1]"
1110 Draft_Sections = "//textarea"
1211 Draft_headings = "//span[@class='fui-Text ___nl2uoq0 fk6fouc f4ybsrx f1i3iumi f16wzh4i fpgzoln f1w7gpdv f6juhto f1gl81tg f2jf649 fepr9ql febqm8h']"
1312 invalid_response = "The requested information is not available in the retrieved data. Please try another query or topic."
1413 invalid_response1 = "There was an issue fetching your data. Please try again."
15- invalid_response2 = " "
14+
1615
1716 def __init__ (self , page ):
1817 self .page = page
1918
20- def check_draft_sections (self , timeout : float = 180.0 , poll_interval : float = 0.5 ):
21- """
22- Waits for all <textarea> draft sections to load valid content using .input_value().
23- Scrolls into view if needed, retries until timeout.
24- Raises clear errors if validation fails.
25- """
26- from collections import defaultdict
27- import time
28- start_time = time .time ()
19+ def validate_draft_sections_loaded (self ):
20+ max_wait_time = 180 # seconds
21+ poll_interval = 2
2922
23+ self .page .wait_for_timeout (25000 )
3024
31- while time . time () - start_time < timeout :
32- section_elements = self .page .locator (self . Draft_Sections )
33- 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 ( )
3428
35- section_count = section_elements .count ()
36- heading_count = heading_elements .count ()
29+ logger .info (f"🔍 Total sections found: { total_sections } " )
3730
38- if section_count < 13 or heading_count < 13 :
39- print ("[WAIT] Waiting for all sections to appear..." )
40- time .sleep (poll_interval )
41- continue
31+ for index in range (total_sections ):
32+ section = section_blocks .nth (index )
4233
43- failed_sections = defaultdict (str )
34+ try :
35+ section .scroll_into_view_if_needed ()
36+ self .page .wait_for_timeout (500 )
4437
45- for i in range (section_count ):
46- 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
4743
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 :
4871 try :
49- # Scroll into view and wait a bit for rendering
50- section .scroll_into_view_if_needed (timeout = 2000 )
51- self .page .wait_for_timeout (200 )
52-
53- # Extract content from <textarea>
54- section_text = section .input_value ().strip ()
55-
56- if not section_text :
57- failed_sections [i ] = "Empty"
58- elif section_text in (self .invalid_response , self .invalid_response1 ):
59- failed_sections [i ] = f"Invalid: { repr (section_text [:30 ])} "
60-
61- except Exception as e :
62- failed_sections [i ] = f"Exception: { str (e )} "
63-
64- if not failed_sections :
65- break # ✅ All good
66- else :
67- print (f"[WAITING] Sections not ready yet: { failed_sections } " )
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
6895 time .sleep (poll_interval )
6996
70- else :
71- raise TimeoutError (f"❌ Timeout: These sections did not load valid content: { failed_sections } " )
72-
73- # ✅ Final validations after loading
74- for i in range (section_count ):
75- section = section_elements .nth (i )
76- heading = heading_elements .nth (i )
77-
78- section .scroll_into_view_if_needed (timeout = 2000 )
79- self .page .wait_for_timeout (200 )
80-
81- heading_text = heading .inner_text (timeout = 3000 ).strip ()
82- content = section .input_value ().strip ()
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'..." )
83100
84- print (f"[VALIDATING] Section { i } : '{ heading_text } ' → { repr (content [:60 ])} ..." )
85-
86- with check :
87- check .is_not_none (content , f"❌ Section '{ heading_text } ' is None" )
88- check .not_equal (content , "" , f"❌ Section '{ heading_text } ' is empty" )
89- check .not_equal (content , self .invalid_response , f"❌ '{ heading_text } ' has invalid response" )
90- check .not_equal (content , self .invalid_response1 , f"❌ '{ heading_text } ' has invalid response" )
101+ try :
102+ generate_btn .click ()
103+ logger .info (f"🔄 Clicked 'Generate' for section '{ title_text } '" )
104+ except Exception as e :
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