@@ -210,6 +210,131 @@ def _check_for_classic(self):
210210 except AssertionError :
211211 return False
212212
213+ @capture_screenshot_on_error
214+ def open_app_launcher (self , timeout = "15 seconds" ):
215+ """Opens the Salesforce App Launcher by clicking the waffle button.
216+
217+ Waits for the App Launcher modal dialog to appear.
218+ """
219+ self .browser .click ("button.slds-icon-waffle_container" )
220+ self .wait_until_modal_is_open (timeout = timeout )
221+
222+ @capture_screenshot_on_error
223+ def select_app_launcher_app (self , app_name , timeout = "15 seconds" ):
224+ """Searches for and selects an app in the App Launcher.
225+
226+ Requires the App Launcher dialog to already be open.
227+ """
228+ search_input = "input[placeholder='Search apps and items...']"
229+ self .browser .fill_text (search_input , app_name )
230+ time .sleep (1 )
231+ self .browser .click (f"a.slds-app-launcher__tile--small:has-text('{ app_name } ')" )
232+ self .wait_until_loading_is_complete ()
233+
234+ @capture_screenshot_on_error
235+ def select_app_launcher_tab (self , tab_name , timeout = "15 seconds" ):
236+ """Searches for and selects a tab/item in the App Launcher.
237+
238+ Requires the App Launcher dialog to already be open.
239+ """
240+ search_input = "input[placeholder='Search apps and items...']"
241+ self .browser .fill_text (search_input , tab_name )
242+ time .sleep (1 )
243+ self .browser .click (f"one-app-launcher-menu-item a:has-text('{ tab_name } ')" )
244+ self .wait_until_loading_is_complete ()
245+
246+ @capture_screenshot_on_error
247+ def populate_field (self , name , value ):
248+ """Finds a form field by its label and fills it with the given value.
249+
250+ ``name`` is the label text of the field.
251+ ``value`` is the text to enter.
252+ """
253+ input_el = f"lightning-input label:has-text('{ name } ')"
254+ try :
255+ self .browser .get_element (input_el )
256+ self .browser .fill_text (
257+ f"lightning-input:has(label:has-text('{ name } ')) input" , value
258+ )
259+ return
260+ except (AssertionError , Exception ):
261+ pass
262+
263+ textarea_el = f"lightning-textarea:has(label:has-text('{ name } ')) textarea"
264+ try :
265+ self .browser .get_element (textarea_el )
266+ self .browser .fill_text (textarea_el , value )
267+ return
268+ except (AssertionError , Exception ):
269+ pass
270+
271+ generic = f"label:has-text('{ name } ')"
272+ self .browser .get_element (generic )
273+ self .browser .fill_text (
274+ f":near({ generic } ) input, :near({ generic } ) textarea" , value
275+ )
276+
277+ @capture_screenshot_on_error
278+ def populate_form (self , ** kwargs ):
279+ """Fills in multiple form fields at once.
280+
281+ Each keyword argument maps a field label to its desired value.
282+
283+ Example::
284+
285+ Populate Form First Name=Alice Last Name=Smith
286+ """
287+ for name , value in kwargs .items ():
288+ self .populate_field (name , value )
289+
290+ @capture_screenshot_on_error
291+ def click_modal_button (self , button_text , timeout = "15 seconds" ):
292+ """Clicks a button with the given text inside the currently open modal dialog.
293+
294+ Waits for the modal to be present first.
295+ """
296+ self .wait_until_modal_is_open (timeout = timeout )
297+ self .browser .click (
298+ f"div.slds-modal__container button:has-text('{ button_text } ')"
299+ )
300+
301+ @capture_screenshot_on_error
302+ def wait_until_modal_is_open (self , timeout = "15 seconds" ):
303+ """Waits until a Salesforce modal dialog (``slds-modal``) is visible."""
304+ self .browser .wait_for_elements_state ("section.slds-modal" , "visible" , timeout )
305+
306+ @capture_screenshot_on_error
307+ def wait_until_modal_is_closed (self , timeout = "15 seconds" ):
308+ """Waits until all Salesforce modal dialogs (``slds-modal``) have disappeared."""
309+ self .browser .wait_for_elements_state ("section.slds-modal" , "detached" , timeout )
310+
311+ @capture_screenshot_on_error
312+ def click_related_list_button (self , heading , button_title ):
313+ """Clicks a button within a related list identified by its heading.
314+
315+ ``heading`` is the title text of the related list (e.g. "Contacts").
316+ ``button_title`` is the visible text of the button to click.
317+ """
318+ card = f"article.slds-card:has(span[title='{ heading } '])"
319+ self .browser .click (f"{ card } button:has-text('{ button_title } ')" )
320+
321+ @capture_screenshot_on_error
322+ def get_related_list_count (self , heading ):
323+ """Returns the item count displayed in a related list's heading.
324+
325+ ``heading`` is the title of the related list (e.g. "Contacts").
326+ Returns the integer count parsed from the heading, or 0 if no
327+ count is found.
328+ """
329+ span_text = self .browser .get_text (
330+ f"article.slds-card:has(span[title='{ heading } ']) "
331+ f"span.slds-card__header-title"
332+ )
333+ match = re .search (r"\((\d+)\)" , span_text )
334+ if match :
335+ return int (match .group (1 ))
336+ return 0
337+
213338 def breakpoint (self ):
214339 """Serves as a breakpoint for the robot debugger
215340
0 commit comments