Context
While trying to click a delete button belonging to a GitHub personal access token (PAT) with a certain description, using Selenium in Python. I am able to find the description and the ID of the PAT. However the button itself does not contain any reference to the id. Only the form that is spawned after clicking the button contains that reference. So to find out how to click the right button, I thought I would be able to find the button within the <div id="access-token-836771760" ..
element. However, most solutions that are able to search elements within elements, require one to know the xpath of this entry. I do not know the xpath of the parent element, because I find this element based on the token description. Apparently it is not practical to get the xpath of an element, once you have the element in Selenium.
HTML Code
<div >
<div id="access-token-836771760" data-id="836771760" data-type="token">
<div >
<div >
<details >
<summary data-view-component="true" role="button"> Delete
</summary>
<details-dialog role="dialog"
aria-modal="true">
<div >
<button type="button"
aria-label="Close dialog" data-close-dialog="">
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16"
data-view-component="true" >
<path fill-rule="evenodd"
d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z">
</path>
</svg>
</button>
<h3 >Are you sure you want to delete this token?</h3>
</div>
<div data-view-component="true" >
Any applications or scripts using this token will no longer be able to access the GitHub
API. You cannot undo this action.
</div>
<div >
</div>
<div >
<!-- '"` -->
<!-- </textarea></xmp> -->
<form data-id="836771760" data-type-name="token"
data-turbo="false" action="/settings/tokens/836771760" accept-charset="UTF-8"
method="post" style=""><input type="hidden" name="_method" value="delete"
autocomplete="off"><input type="hidden" name="authenticity_token"
value="somevalue">
<button type="submit" data-view-component="true" > I
understand, delete this token
</button>
</form>
</div>
</details-dialog>
</details>
</div>
<small >Last used within the last 6 months</small>
<span >
<strong>
<a href="/settings/tokens/836771760" data-pjax="">
Set GitHub commit build status values.</a>
</strong>
<span >
<em>— <span title="Access commit status">repo:status</span></em>
</span>
</span>
<div>
<span >
<a href="/settings/tokens/836771760/regenerate?index_page=1">
Expired <span >on Mon, May 2 2022</span>.
</a> </span>
</div>
</div>
</div>
<div id="access-token-826562783" data-id="826562783" data-type="token">
<div >
<div >
<details >
<summary data-view-component="true" role="button"> Delete
</summary>
<details-dialog role="dialog"
aria-modal="true">
<div >
<button type="button"
aria-label="Close dialog" data-close-dialog="">
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16"
data-view-component="true" >
<path fill-rule="evenodd"
d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z">
</path>
</svg>
</button>
<h3 >Are you sure you want to delete this token?</h3>
</div>
<div data-view-component="true" >
Any applications or scripts using this token will no longer be able to access the GitHub
API. You cannot undo this action.
</div>
<div >
</div>
<div >
<!-- '"` -->
<!-- </textarea></xmp> -->
<form data-id="826562783" data-type-name="token"
data-turbo="false" action="/settings/tokens/826562783" accept-charset="UTF-8"
method="post"><input type="hidden" name="_method" value="delete"
autocomplete="off"><input type="hidden" name="authenticity_token"
value="someothervalue">
<button type="submit" data-view-component="true" > I
understand, delete this token
</button>
</form>
</div>
</details-dialog>
</details>
</div>
<small >Last used within the last 6 months</small>
<span >
<strong>
<a href="/settings/tokens/82653355" data-pjax="">
somedescription</a>
</strong>
<span >
<em>— <span title="something">repo</span></em>
</span>
</span>
<div>
<span >
<a href="/settings/tokens/826562783/regenerate?index_page=1">
Expired <span >on Thu, May 19 2022</span>.
</a> </span>
</div>
</div>
</div>
</div>
Question
How could I click the delete button belonging to the access-token-836771760
class in Python using Selenium?
Approach
I can find the delete buttons with:
danger_button = website_controller.driver.find_elements(By.CSS_SELECTOR,'btn-danger.btn-sm.btn')
print_attributes_of_elements(danger_button,website_controller)
def print_attributes_of_elements(elements,website_controller):
for elem in elements:
attrs = website_controller.driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', elem)
pprint(attrs)
However, within those buttons, I do not know which button is the right one.
CodePudding user response:
If you already have the <div id="access-token-836771760" ..
element it should be as easy as that:
# get div by description (you already have your div)
div = driver.find_element(By.XPATH, "//a[normalize-space(text())='Test']//ancestor::div[@data-type='token']")
# click delete button
button = div.find_element(By.XPATH, ".//summary")
button.click()
You don't need to know the XPATH if you already have the reference to the div.
CodePudding user response:
In the end, I was able to get the xpaths relative to another element of which I knew the xpath, by manually analysing what the xpath change pattern was. Still a general method to find elements within an element, would be appreciated.
Here is the verified script that deletes a GitHub personal access token if it already exists, based on the GitHub personal access token description:
from pprint import pprint
from typing import List
from code.project1.src.Website_controller import Website_controller
from code.project1.src.control_website import click_element_by_xpath, open_url, wait_until_page_is_loaded
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from code.project1.src.helper import scroll_shim
def remove_previous_github_pat(hardcoded,website_controller):
"""Assumes the user is logged in into GitHub. Then lists the already
existing GitHub personal access token (PAT) descriptions. If the new GitHub
PAT description is already existing, it deletes the existing GitHub PAT.
Then it verifies the GitHub PAT is not yet in GitHub/is removed
succesfully."""
# Check if the token exists, and if yes, get a link containing token id.
github_pat_exists,link =github_pat_description_exists(hardcoded,website_controller)
if github_pat_exists:
# Delete the GitHub personal access token.
delete_github_pat(link,hardcoded,website_controller)
# Verify token is deleted.
if github_pat_description_exists(hardcoded,website_controller)[0]:
raise Exception("Error, GitHub pat is not deleted succesfully.")
def github_pat_description_exists(hardcoded,website_controller):
"""Assumes the user is logged in into GitHub. Then lists the already
existing GitHub personal access token (PAT) descriptions. If the new GitHub
PAT description is already existing, it returns True, otherwise returns
False. Also returns the url of the GitHub pat that contains the token id."""
# Go to url containing GitHub pat.
website_controller.driver = open_url(
website_controller.driver,
hardcoded.github_pat_tokens_url,
)
# Wait until url is loaded.
wait_until_page_is_loaded(6,website_controller)
# Get the token descriptions through the href element.
elems = website_controller.driver.find_elements(By.CSS_SELECTOR,f".{hardcoded.github_pat_description_elem_classname} [href]")
for elem in elems:
link=elem.get_attribute('href')
if hardcoded.github_pat_description in elem.text:
return True, link
return False, None
def delete_github_pat(link,hardcoded,website_controller):
"""Gets the GitHub pat id from the link, then clicks the delete button, and
the confirm deletion button, to delete the GitHub pat."""
if link[:len(hardcoded.github_pat_tokens_url)] == hardcoded.github_pat_tokens_url:
github_pat_id=int(link[len(hardcoded.github_pat_tokens_url):])
print(f'github_pat_id={github_pat_id}')
# Get the right table row nr.
valid_indices=list_of_valid_xpath_indices([],f"{hardcoded.github_pat_table_xpath}/div[","]",website_controller)
row_nr= get_desired_token_index(hardcoded,website_controller,valid_indices)
# Click delete button and deletion confirmation button.
click_github_pat_delete_button(hardcoded,website_controller,row_nr)
else:
raise Exception(f'{link[:len(hardcoded.github_pat_tokens_url)]} is not:{hardcoded.github_pat_tokens_url}')
def list_of_valid_xpath_indices(valid_indices,left,right,website_controller):
"""Returns the row numbers of the GitHub personal access tokens table,
starting at index =1. Basically gets how much GitHub pats are stored."""
if valid_indices == []:
latest_index=1
else:
latest_index=valid_indices[-1] 1
try:
row = website_controller.driver.find_element(By.XPATH,
f"{left}{latest_index}{right}"
)
if not row is None:
print(row.text)
valid_indices.append(latest_index)
return list_of_valid_xpath_indices(valid_indices,left,right,website_controller)
else:
return valid_indices
except:
if len(valid_indices) ==0:
raise Exception("Did not find any valid indices.")
return valid_indices
def get_desired_token_index(hardcoded,website_controller,valid_indices:List[int]):
"""Finds the index/row number of the GitHub pat's that corresponds to the
description of the GitHub pat that is to be created, and returns this
index."""
for row_nr in valid_indices:
row_elem = website_controller.driver.find_element(By.XPATH,
f"{hardcoded.github_pat_table_xpath}/div[{row_nr}]"
)
if hardcoded.github_pat_description in row_elem.text:
return row_nr
def click_github_pat_delete_button(hardcoded,website_controller,row_nr:int):
"""Clicks the delete GitHub pat button, and then clicks the confirm
deletion button."""
delete_button = website_controller.driver.find_element(By.XPATH,
f"{hardcoded.github_pat_table_xpath}/div[{row_nr}]/div/div[1]/details/summary"
)
delete_button.click()
confirm_deletion_button = website_controller.driver.find_element(By.XPATH,
f"{hardcoded.github_pat_table_xpath}/div[{row_nr}]/div/div[1]/details/details-dialog/div[4]/form/button"
)
confirm_deletion_button.click()