robo_appian 0.0.15__py3-none-any.whl → 0.0.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of robo_appian might be problematic. Click here for more details.
- robo_appian/components/LabelUtils.py +12 -3
- robo_appian/components/SearchDropdownUtils.py +29 -13
- robo_appian/components/TabUtils.py +25 -33
- robo_appian/components/TableUtils.py +36 -11
- robo_appian/controllers/ComponentDriver.py +2 -2
- robo_appian/utils/ComponentUtils.py +7 -3
- {robo_appian-0.0.15.dist-info → robo_appian-0.0.16.dist-info}/METADATA +1 -1
- {robo_appian-0.0.15.dist-info → robo_appian-0.0.16.dist-info}/RECORD +10 -10
- {robo_appian-0.0.15.dist-info → robo_appian-0.0.16.dist-info}/LICENSE +0 -0
- {robo_appian-0.0.15.dist-info → robo_appian-0.0.16.dist-info}/WHEEL +0 -0
|
@@ -14,7 +14,7 @@ class LabelUtils:
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
@staticmethod
|
|
17
|
-
def
|
|
17
|
+
def __findByLabelText(wait: WebDriverWait, label: str):
|
|
18
18
|
"""
|
|
19
19
|
Finds a label element by its text.
|
|
20
20
|
|
|
@@ -26,7 +26,7 @@ class LabelUtils:
|
|
|
26
26
|
"""
|
|
27
27
|
xpath = f".//*[normalize-space(.)='{label}']"
|
|
28
28
|
try:
|
|
29
|
-
component = wait.until(EC.
|
|
29
|
+
component = wait.until(EC.visibility_of_element_located((By.XPATH, xpath)))
|
|
30
30
|
except Exception as e:
|
|
31
31
|
raise RuntimeError(f"Label with text '{label}' not found.") from e
|
|
32
32
|
|
|
@@ -42,5 +42,14 @@ class LabelUtils:
|
|
|
42
42
|
Example:
|
|
43
43
|
LabelUtils.clickByLabelText(wait, "Submit")
|
|
44
44
|
"""
|
|
45
|
-
component = LabelUtils.
|
|
45
|
+
component = LabelUtils.__findByLabelText(wait, label)
|
|
46
|
+
wait.until(EC.element_to_be_clickable(component))
|
|
46
47
|
component.click()
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def checkLabelExists(wait: WebDriverWait, label: str):
|
|
51
|
+
try:
|
|
52
|
+
LabelUtils.__findByLabelText(wait, label)
|
|
53
|
+
except Exception:
|
|
54
|
+
return False
|
|
55
|
+
return True
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
2
2
|
from selenium.webdriver.support import expected_conditions as EC
|
|
3
3
|
from selenium.webdriver.common.by import By
|
|
4
|
+
from selenium.webdriver.remote.webelement import WebElement
|
|
4
5
|
from robo_appian.components.InputUtils import InputUtils
|
|
5
6
|
|
|
6
7
|
|
|
@@ -14,14 +15,20 @@ class SearchDropdownUtils:
|
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
17
|
@staticmethod
|
|
17
|
-
def
|
|
18
|
-
|
|
18
|
+
def __selectSearchDropdownValueByDropdownId(wait, component_id, value):
|
|
19
|
+
if not component_id:
|
|
20
|
+
raise ValueError("Invalid component_id provided.")
|
|
21
|
+
|
|
19
22
|
try:
|
|
23
|
+
input_component_id = str(component_id) + "_searchInput"
|
|
24
|
+
wait.until(EC.presence_of_element_located((By.ID, input_component_id)))
|
|
20
25
|
input_component = wait.until(EC.element_to_be_clickable((By.ID, input_component_id)))
|
|
21
26
|
except Exception as e:
|
|
22
27
|
raise RuntimeError(f"Failed to locate or click input component with ID '{input_component_id}': {e}")
|
|
23
28
|
InputUtils._setValueByComponent(input_component, value)
|
|
24
29
|
|
|
30
|
+
dropdown_option_id = str(component_id) + "_list"
|
|
31
|
+
|
|
25
32
|
xpath = f'.//ul[@id="{dropdown_option_id}"]/li[./div[normalize-space(.)="{value}"]][1]'
|
|
26
33
|
try:
|
|
27
34
|
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
@@ -33,26 +40,35 @@ class SearchDropdownUtils:
|
|
|
33
40
|
def __selectSearchDropdownValueByPartialLabelText(wait: WebDriverWait, label: str, value: str):
|
|
34
41
|
xpath = f'.//div[./div/span[contains(normalize-space(.), "{label}")]]/div/div/div/div[@role="combobox" and not(@aria-disabled="true")]'
|
|
35
42
|
try:
|
|
36
|
-
|
|
43
|
+
combobox = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
37
44
|
except Exception as e:
|
|
38
45
|
raise RuntimeError(f"Failed to locate or click dropdown component with XPath '{xpath}': {e}")
|
|
39
|
-
component_id = component.get_attribute("aria-labelledby")
|
|
40
|
-
dropdown_id = component.get_attribute("aria-controls")
|
|
41
|
-
component.click()
|
|
42
46
|
|
|
43
|
-
SearchDropdownUtils.
|
|
47
|
+
SearchDropdownUtils._selectSearchDropdownValueByComboboxComponent(wait, combobox, value)
|
|
44
48
|
|
|
45
49
|
@staticmethod
|
|
46
50
|
def __selectSearchDropdownValueByLabelText(wait: WebDriverWait, label: str, value: str):
|
|
47
|
-
xpath =
|
|
51
|
+
xpath = (
|
|
52
|
+
f'.//div[./div/span[normalize-space(.)="{label}"]]/div/div/div/div[@role="combobox" and not(@aria-disabled="true")]'
|
|
53
|
+
)
|
|
48
54
|
try:
|
|
49
|
-
|
|
55
|
+
combobox = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
50
56
|
except Exception as e:
|
|
51
57
|
raise RuntimeError(f"Failed to locate or click dropdown component with XPath '{xpath}': {e}")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
SearchDropdownUtils._selectSearchDropdownValueByComboboxComponent(wait, combobox, value)
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _selectSearchDropdownValueByComboboxComponent(wait: WebDriverWait, combobox: WebElement, value: str):
|
|
62
|
+
id = combobox.get_attribute("id")
|
|
63
|
+
if id is not None:
|
|
64
|
+
component_id = id.rsplit("_value", 1)[0]
|
|
65
|
+
else:
|
|
66
|
+
raise RuntimeError("Combobox element does not have an 'id' attribute.")
|
|
67
|
+
|
|
68
|
+
wait.until(EC.element_to_be_clickable(combobox))
|
|
69
|
+
combobox.click()
|
|
70
|
+
|
|
71
|
+
SearchDropdownUtils.__selectSearchDropdownValueByDropdownId(wait, component_id, value)
|
|
56
72
|
|
|
57
73
|
@staticmethod
|
|
58
74
|
def selectSearchDropdownValueByLabelText(wait: WebDriverWait, dropdown_label: str, value: str):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from selenium.webdriver.common.by import By
|
|
2
2
|
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
from robo_appian.utils.ComponentUtils import ComponentUtils
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class TabUtils:
|
|
@@ -17,47 +18,38 @@ class TabUtils:
|
|
|
17
18
|
selected_tab = TabUtils.findSelectedTabByLabelText(wait, "Tab Label")
|
|
18
19
|
|
|
19
20
|
# Select an inactive tab by its label
|
|
20
|
-
TabUtils.
|
|
21
|
+
TabUtils.selectTabByLabelText(wait, "Inactive Tab Label")
|
|
21
22
|
|
|
22
23
|
driver.quit()
|
|
23
24
|
"""
|
|
24
25
|
|
|
25
26
|
@staticmethod
|
|
26
|
-
def
|
|
27
|
-
"""
|
|
28
|
-
Finds the currently selected tab by its label.
|
|
29
|
-
|
|
30
|
-
:param wait: Selenium WebDriverWait instance.
|
|
31
|
-
:param label: The label of the tab to find.
|
|
32
|
-
:return: WebElement representing the selected tab.
|
|
33
|
-
Example:
|
|
34
|
-
component = TabUtils.findSelectedTabByLabelText(wait, "Tab Label")
|
|
35
|
-
"""
|
|
36
|
-
xpath = f".//div[./div[./div/div/div/div/div/p/strong[normalize-space(.)='{label}']]/span[text()='Selected Tab.']]/div[@role='link']"
|
|
27
|
+
def findTabByLabelText(wait, label):
|
|
28
|
+
xpath = f'//div/div[@role="link" ]/div/div/div/div/div/p[normalize-space(.)="{label}"]'
|
|
37
29
|
try:
|
|
38
|
-
component = wait.until(EC.
|
|
39
|
-
except
|
|
40
|
-
raise
|
|
41
|
-
except Exception as e:
|
|
42
|
-
raise RuntimeError(f"Could not find selected tab with label '{label}': {e}")
|
|
30
|
+
component = wait.until(EC.visibility_of_element_located((By.XPATH, xpath)))
|
|
31
|
+
except Exception:
|
|
32
|
+
raise Exception(f"Tab with label '{label}' not found.")
|
|
43
33
|
return component
|
|
44
34
|
|
|
45
35
|
@staticmethod
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
Selects an inactive tab by its label.
|
|
49
|
-
|
|
50
|
-
:param wait: Selenium WebDriverWait instance.
|
|
51
|
-
:param label: The label of the tab to select.
|
|
52
|
-
:return: None
|
|
53
|
-
Example:
|
|
54
|
-
TabUtils.selectInactiveTabByLabelText(wait, "Tab Label")
|
|
55
|
-
"""
|
|
56
|
-
xpath = f".//div[@role='link']/div/div/div/div/div[./p/span[text()='{label}']]"
|
|
36
|
+
def selectTabByLabelText(wait, label):
|
|
37
|
+
component = TabUtils.findTabByLabelText(wait, label)
|
|
57
38
|
try:
|
|
58
|
-
component = wait.until(EC.element_to_be_clickable(
|
|
59
|
-
except
|
|
60
|
-
raise
|
|
61
|
-
except Exception as e:
|
|
62
|
-
raise RuntimeError(f"Could not find tab with label '{label}': {e}")
|
|
39
|
+
component = wait.until(EC.element_to_be_clickable(component))
|
|
40
|
+
except Exception:
|
|
41
|
+
raise Exception(f"Tab with label '{label}' is not clickable.")
|
|
63
42
|
component.click()
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def checkTabSelectedByLabelText(wait, label):
|
|
46
|
+
component = TabUtils.findTabByLabelText(wait, label)
|
|
47
|
+
|
|
48
|
+
select_text = "Selected Tab."
|
|
49
|
+
xpath = f'./span[normalize-space(.)="{select_text}"]'
|
|
50
|
+
try:
|
|
51
|
+
component = ComponentUtils.findChildComponentByXpath(wait, component, xpath)
|
|
52
|
+
except Exception:
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
return True
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from selenium.webdriver.common.by import By
|
|
2
2
|
from selenium.webdriver.support import expected_conditions as EC
|
|
3
3
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
4
|
+
from robo_appian.utils.ComponentUtils import ComponentUtils
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class TableUtils:
|
|
@@ -34,11 +35,14 @@ class TableUtils:
|
|
|
34
35
|
|
|
35
36
|
xpath = f'.//table[./thead/tr/th[@abbr="{columnName}"]]'
|
|
36
37
|
try:
|
|
37
|
-
component = wait.until(EC.
|
|
38
|
-
except
|
|
39
|
-
raise
|
|
38
|
+
component = wait.until(EC.visibility_of_element_located((By.XPATH, xpath)))
|
|
39
|
+
except Exception as e:
|
|
40
|
+
raise Exception(f"Could not find table with column name '{columnName}': {e}")
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
component = wait.until(EC.element_to_be_clickable(component))
|
|
40
44
|
except Exception as e:
|
|
41
|
-
raise
|
|
45
|
+
raise Exception(f"Table found by column name '{columnName}' is not clickable: {e}")
|
|
42
46
|
return component
|
|
43
47
|
|
|
44
48
|
@staticmethod
|
|
@@ -56,7 +60,7 @@ class TableUtils:
|
|
|
56
60
|
try:
|
|
57
61
|
rows = tableObject.find_elements(By.XPATH, xpath)
|
|
58
62
|
except Exception as e:
|
|
59
|
-
raise
|
|
63
|
+
raise Exception(f"Could not count rows in table: {e}")
|
|
60
64
|
return len(rows)
|
|
61
65
|
|
|
62
66
|
@staticmethod
|
|
@@ -92,6 +96,12 @@ class TableUtils:
|
|
|
92
96
|
data = selected_word.split("_")
|
|
93
97
|
return int(data[1])
|
|
94
98
|
|
|
99
|
+
@staticmethod
|
|
100
|
+
def __findRowByColumnNameAndRowNumber(wait, rowNumber, columnName):
|
|
101
|
+
xpath = f'.//table[./thead/tr/th/div[normalize-space(.)="{columnName}"] ]/tbody/tr[@data-dnd-name="row {rowNumber + 1}"]'
|
|
102
|
+
row = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
103
|
+
return row
|
|
104
|
+
|
|
95
105
|
@staticmethod
|
|
96
106
|
def findComponentFromTableCell(wait, rowNumber, columnName):
|
|
97
107
|
"""
|
|
@@ -107,16 +117,31 @@ class TableUtils:
|
|
|
107
117
|
|
|
108
118
|
tableObject = TableUtils.findTableByColumnName(wait, columnName)
|
|
109
119
|
columnNumber = TableUtils.__findColumNumberByColumnName(tableObject, columnName)
|
|
110
|
-
# xpath=f'./tbody/tr[@data-dnd-name="row {rowNumber+1}"]/td[not (@data-empty-grid-message)][{columnNumber}]'
|
|
111
|
-
# component = tableObject.find_elements(By.XPATH, xpath)
|
|
112
120
|
rowNumber = rowNumber + 1
|
|
113
121
|
columnNumber = columnNumber + 1
|
|
114
122
|
xpath = f'.//table[./thead/tr/th[@abbr="{columnName}"]]/tbody/tr[@data-dnd-name="row {rowNumber}"]/td[not (@data-empty-grid-message)][{columnNumber}]/*'
|
|
115
123
|
try:
|
|
116
124
|
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
117
|
-
except TimeoutError as e:
|
|
118
|
-
raise TimeoutError(f"Could not find component in cell at row {rowNumber}, column '{columnName}': {e}")
|
|
119
125
|
except Exception as e:
|
|
120
|
-
raise
|
|
121
|
-
|
|
126
|
+
raise Exception(f"Could not find component in cell at row {rowNumber}, column '{columnName}': {e}")
|
|
127
|
+
return component
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def selectRowFromTableByColumnNameAndRowNumber(wait, rowNumber, columnName):
|
|
131
|
+
row = TableUtils.__findRowByColumnNameAndRowNumber(wait, rowNumber, columnName)
|
|
132
|
+
row = wait.until(EC.element_to_be_clickable(row))
|
|
133
|
+
row.click()
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def findComponentByColumnNameAndRowNumber(wait, rowNumber, columnName):
|
|
137
|
+
xpath = f'.//table/thead/tr/th[./div[normalize-space(.)="{columnName}"]]'
|
|
138
|
+
column = wait.until(EC.visibility_of_element_located((By.XPATH, xpath)))
|
|
139
|
+
id = column.get_attribute("id")
|
|
140
|
+
parts = id.rsplit("_", 1)
|
|
141
|
+
columnNumber = int(parts[-1])
|
|
142
|
+
|
|
143
|
+
tableRow = TableUtils.__findRowByColumnNameAndRowNumber(wait, rowNumber, columnName)
|
|
144
|
+
xpath = f"./td[{columnNumber + 1}]/*"
|
|
145
|
+
component = ComponentUtils.findChildComponentByXpath(wait, tableRow, xpath)
|
|
146
|
+
component = wait.until(EC.element_to_be_clickable(component))
|
|
122
147
|
return component
|
|
@@ -56,7 +56,7 @@ class ComponentDriver:
|
|
|
56
56
|
case "Label":
|
|
57
57
|
match action:
|
|
58
58
|
case "Find":
|
|
59
|
-
LabelUtils.
|
|
59
|
+
LabelUtils.checkLabelExists(wait, label)
|
|
60
60
|
case _:
|
|
61
61
|
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
62
62
|
case "Link":
|
|
@@ -86,7 +86,7 @@ class ComponentDriver:
|
|
|
86
86
|
case "Tab":
|
|
87
87
|
match action:
|
|
88
88
|
case "Find":
|
|
89
|
-
TabUtils.
|
|
89
|
+
TabUtils.selectTabByLabelText(wait, label)
|
|
90
90
|
case _:
|
|
91
91
|
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
92
92
|
case _:
|
|
@@ -34,7 +34,7 @@ class ComponentUtils:
|
|
|
34
34
|
return yesterday_formatted
|
|
35
35
|
|
|
36
36
|
@staticmethod
|
|
37
|
-
def
|
|
37
|
+
def findChildComponentByXpath(wait: WebDriverWait, component: WebElement, xpath: str):
|
|
38
38
|
"""Finds a child component using the given XPath within a parent component.
|
|
39
39
|
|
|
40
40
|
:param wait: WebDriverWait instance to wait for elements
|
|
@@ -49,9 +49,13 @@ class ComponentUtils:
|
|
|
49
49
|
wait = WebDriverWait(driver, 10)
|
|
50
50
|
parent_component = driver.find_element(By.ID, "parent")
|
|
51
51
|
xpath = ".//button[@class='child']"
|
|
52
|
-
child_component = ComponentUtils.
|
|
52
|
+
child_component = ComponentUtils.findChildComponentByXpath(wait, parent_component, xpath)
|
|
53
53
|
"""
|
|
54
|
-
|
|
54
|
+
try:
|
|
55
|
+
component = component.find_element(By.XPATH, xpath)
|
|
56
|
+
except Exception:
|
|
57
|
+
raise Exception(f"Child component with XPath '{xpath}' not found within the given parent component.")
|
|
58
|
+
return component
|
|
55
59
|
|
|
56
60
|
@staticmethod
|
|
57
61
|
def findComponentUsingXpathAndClick(wait: WebDriverWait, xpath: str):
|
|
@@ -3,20 +3,20 @@ robo_appian/components/ButtonUtils.py,sha256=_FUote6eBekTAq7KsFJvE301ZRbmnjYr_-5
|
|
|
3
3
|
robo_appian/components/DateUtils.py,sha256=9scKYCyJavVyHzoHVfe1cdW2rEp4NAXsP5Zs_CV3fwk,3252
|
|
4
4
|
robo_appian/components/DropdownUtils.py,sha256=X_ucR4uCiU3fnsMXGnJC9PTfwbfW3w3ulzyuURh1Vlw,11149
|
|
5
5
|
robo_appian/components/InputUtils.py,sha256=21MsMuGHwUtpTmZdwUxVrpePMr2Kdy7aOZ6kqPV4lQ4,6423
|
|
6
|
-
robo_appian/components/LabelUtils.py,sha256=
|
|
6
|
+
robo_appian/components/LabelUtils.py,sha256=Fue3BDpqvHKGK_vBOtMld3Lai3ZQ2wzZPeQJESrjUF8,1860
|
|
7
7
|
robo_appian/components/LinkUtils.py,sha256=PS3U_o8BpTu5gFLHKPTGcUrs0Qo2H8qHNueKuXTC_y4,1514
|
|
8
|
-
robo_appian/components/SearchDropdownUtils.py,sha256=
|
|
8
|
+
robo_appian/components/SearchDropdownUtils.py,sha256=tCNsggjX-LPcADRizwLFjEE5Xxg5mT5Y8qN4wT9sU1s,4492
|
|
9
9
|
robo_appian/components/SearchInputUtils.py,sha256=bBKXDWT2O4ZKoUz-YbLjnOhy56Af2OR9g707buQ7Y2w,3487
|
|
10
|
-
robo_appian/components/TabUtils.py,sha256=
|
|
11
|
-
robo_appian/components/TableUtils.py,sha256=
|
|
10
|
+
robo_appian/components/TabUtils.py,sha256=uW90mZxO1OaU79NKarC62dVyBsKPjbCkjyh004TH2fo,1923
|
|
11
|
+
robo_appian/components/TableUtils.py,sha256=SmtB4jq0XGZc0smMTe1HRvcCurQTXz3CytswbogfwTY,6059
|
|
12
12
|
robo_appian/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
robo_appian/controllers/ComponentDriver.py,sha256=
|
|
13
|
+
robo_appian/controllers/ComponentDriver.py,sha256=jNaQrbKCFFyahpLKmipsA0v3p3bpjy9dMgW36IdE-Fw,4353
|
|
14
14
|
robo_appian/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
robo_appian/exceptions/MyCustomError.py,sha256=DVAkytXNNQNjqyTyCjk6FFd6fr3AsBe57Y19erDSqVs,222
|
|
16
16
|
robo_appian/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
robo_appian/utils/ComponentUtils.py,sha256=
|
|
17
|
+
robo_appian/utils/ComponentUtils.py,sha256=SuMpMBpHpErA9dpYLavfXQ50bSl24lLnWd1p61KzOeU,7063
|
|
18
18
|
robo_appian/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
-
robo_appian-0.0.
|
|
20
|
-
robo_appian-0.0.
|
|
21
|
-
robo_appian-0.0.
|
|
22
|
-
robo_appian-0.0.
|
|
19
|
+
robo_appian-0.0.16.dist-info/LICENSE,sha256=g-xR4dRa9_4iFkMoJmED-wE-J5hoxbk3105Knhfpjm0,1068
|
|
20
|
+
robo_appian-0.0.16.dist-info/METADATA,sha256=9GwUS8cObRQP2wCxbE0PNuU5f9VTMwr3eJ9w-vS7IZI,2261
|
|
21
|
+
robo_appian-0.0.16.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
22
|
+
robo_appian-0.0.16.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|