Skip to content

Commit f4cde66

Browse files
authored
Merge pull request #268 from devicons/build-integrate
Introducing github action for build automatisation (using python)
2 parents 654144d + a2a1720 commit f4cde66

21 files changed

Lines changed: 4577 additions & 12 deletions
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import argparse
2+
from pathlib import Path
3+
4+
5+
class PathResolverAction(argparse.Action):
6+
def __call__(self, parser, namespace, values, option_string=None):
7+
path = Path(values).resolve()
8+
if not path.exists():
9+
raise ValueError(f"{path} doesn't exist.")
10+
11+
if self.dest == "icons_folder_path":
12+
if not path.is_dir():
13+
raise ValueError("icons_folder_path must be a directory")
14+
15+
elif self.dest == "download_path":
16+
if not path.is_dir():
17+
raise ValueError("download_path must be a directory")
18+
19+
setattr(namespace, self.dest, str(path))
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
from typing import List
2+
from pathlib import Path
3+
import time
4+
5+
from selenium.webdriver.firefox.webdriver import WebDriver
6+
from selenium.webdriver.firefox.options import Options
7+
from selenium.webdriver.common.by import By
8+
from selenium.webdriver.support.ui import WebDriverWait
9+
from selenium.webdriver.support import expected_conditions as ec
10+
from selenium.common.exceptions import TimeoutException as SeleniumTimeoutException
11+
12+
13+
class SeleniumRunner:
14+
"""
15+
A runner that upload and download Icomoon resources using Selenium.
16+
The WebDriver will use Firefox.
17+
"""
18+
19+
"""
20+
The long wait time for the driver in seconds.
21+
"""
22+
LONG_WAIT_IN_SEC = 25
23+
24+
"""
25+
The medium wait time for the driver in seconds.
26+
"""
27+
MED_WAIT_IN_SEC = 6
28+
29+
"""
30+
The short wait time for the driver in seconds.
31+
"""
32+
SHORT_WAIT_IN_SEC = 0.6
33+
34+
"""
35+
The Icomoon Url.
36+
"""
37+
ICOMOON_URL = "https://icomoon.io/app/#/select"
38+
39+
def __init__(self, icomoon_json_path: str, download_path: str,
40+
geckodriver_path: str, headless):
41+
"""
42+
Create a SeleniumRunner object.
43+
:param icomoon_json_path: a path to the iconmoon.json.
44+
:param download_path: the location where you want to download
45+
the icomoon.zip to.
46+
:param geckodriver_path: the path to the firefox executable.
47+
:param headless: whether to run browser in headless (no UI) mode.
48+
"""
49+
self.icomoon_json_path = icomoon_json_path
50+
self.download_path = download_path
51+
self.driver = None
52+
self.set_options(geckodriver_path, headless)
53+
54+
def set_options(self, geckodriver_path: str, headless: bool):
55+
"""
56+
Build the WebDriver with Firefox Options allowing downloads and
57+
set download to download_path.
58+
:param geckodriver_path: the path to the firefox executable.
59+
:param headless: whether to run browser in headless (no UI) mode.
60+
61+
:raises AssertionError: if the page title does not contain
62+
"IcoMoon App".
63+
"""
64+
options = Options()
65+
allowed_mime_types = "application/zip, application/gzip, application/octet-stream"
66+
# disable prompt to download from Firefox
67+
options.set_preference("browser.helperApps.neverAsk.saveToDisk", allowed_mime_types)
68+
options.set_preference("browser.helperApps.neverAsk.openFile", allowed_mime_types)
69+
70+
# set the default download path to downloadPath
71+
options.set_preference("browser.download.folderList", 2)
72+
options.set_preference("browser.download.dir", self.download_path)
73+
options.headless = headless
74+
75+
self.driver = WebDriver(options=options, executable_path=geckodriver_path)
76+
self.driver.get(self.ICOMOON_URL)
77+
assert "IcoMoon App" in self.driver.title
78+
79+
def upload_icomoon(self):
80+
"""
81+
Upload the icomoon.json to icomoon.io.
82+
:raises TimeoutException: happens when elements are not found.
83+
"""
84+
print("Uploading icomoon.json file...")
85+
try:
86+
# find the file input and enter the file path
87+
import_btn = WebDriverWait(self.driver, SeleniumRunner.LONG_WAIT_IN_SEC).until(
88+
ec.presence_of_element_located((By.CSS_SELECTOR, "div#file input"))
89+
)
90+
import_btn.send_keys(self.icomoon_json_path)
91+
except Exception as e:
92+
self.close()
93+
raise e
94+
95+
try:
96+
confirm_btn = WebDriverWait(self.driver, SeleniumRunner.MED_WAIT_IN_SEC).until(
97+
ec.element_to_be_clickable((By.XPATH, "//div[@class='overlay']//button[text()='Yes']"))
98+
)
99+
confirm_btn.click()
100+
except SeleniumTimeoutException as e:
101+
print(e.stacktrace)
102+
print("Cannot find the confirm button when uploading the icomoon.json",
103+
"Ensure that the icomoon.json is in the correct format for Icomoon.io",
104+
sep='\n')
105+
self.close()
106+
107+
print("JSON file uploaded.")
108+
109+
def upload_svgs(self, svgs: List[str]):
110+
"""
111+
Upload the SVGs provided in folder_info
112+
:param svgs: a list of svg Paths that we'll upload to icomoon.
113+
"""
114+
try:
115+
print("Uploading SVGs...")
116+
117+
edit_mode_btn = self.driver.find_element_by_css_selector(
118+
"div.btnBar button i.icon-edit"
119+
)
120+
edit_mode_btn.click()
121+
122+
self.click_hamburger_input()
123+
124+
for svg in svgs:
125+
import_btn = self.driver.find_element_by_css_selector(
126+
"li.file input[type=file]"
127+
)
128+
import_btn.send_keys(svg)
129+
print(f"Uploaded {svg}")
130+
self.test_for_possible_alert(self.SHORT_WAIT_IN_SEC, "Dismiss")
131+
self.remove_color_from_icon()
132+
133+
self.click_hamburger_input()
134+
select_all_button = WebDriverWait(self.driver, self.LONG_WAIT_IN_SEC).until(
135+
ec.element_to_be_clickable((By.XPATH, "//button[text()='Select All']"))
136+
)
137+
select_all_button.click()
138+
except Exception as e:
139+
self.close()
140+
raise e
141+
142+
def click_hamburger_input(self):
143+
"""
144+
Click the hamburger input until the pop up menu appears. This
145+
method is needed because sometimes, we need to click the hamburger
146+
input two times before the menu appears.
147+
:return: None.
148+
"""
149+
try:
150+
hamburger_input = self.driver.find_element_by_css_selector(
151+
"button.btn5.lh-def.transparent i.icon-menu"
152+
)
153+
154+
menu_appear_callback = ec.element_to_be_clickable(
155+
(By.CSS_SELECTOR, "h1#setH2 ul")
156+
)
157+
158+
while not menu_appear_callback(self.driver):
159+
hamburger_input.click()
160+
except Exception as e:
161+
self.close()
162+
raise e
163+
164+
def test_for_possible_alert(self, wait_period: float, btn_text: str):
165+
"""
166+
Test for the possible alert when we upload the svgs.
167+
:param wait_period: the wait period for the possible alert
168+
in seconds.
169+
:param btn_text: the text that the alert's button will have.
170+
:return: None.
171+
"""
172+
try:
173+
dismiss_btn = WebDriverWait(self.driver, wait_period, 0.15).until(
174+
ec.element_to_be_clickable(
175+
(By.XPATH, f"//div[@class='overlay']//button[text()='{btn_text}']"))
176+
)
177+
dismiss_btn.click()
178+
except SeleniumTimeoutException:
179+
pass
180+
181+
def remove_color_from_icon(self):
182+
"""
183+
Remove the color from the most recent uploaded icon.
184+
:return: None.
185+
"""
186+
try:
187+
recently_uploaded_icon = WebDriverWait(self.driver, self.LONG_WAIT_IN_SEC).until(
188+
ec.element_to_be_clickable((By.XPATH, "//div[@id='set0']//mi-box[1]//div"))
189+
)
190+
recently_uploaded_icon.click()
191+
except Exception as e:
192+
self.close()
193+
raise e
194+
195+
try:
196+
color_tab = WebDriverWait(self.driver, self.SHORT_WAIT_IN_SEC).until(
197+
ec.element_to_be_clickable((By.CSS_SELECTOR, "div.overlayWindow i.icon-droplet"))
198+
)
199+
color_tab.click()
200+
201+
remove_color_btn = self.driver \
202+
.find_element_by_css_selector("div.overlayWindow i.icon-droplet-cross")
203+
remove_color_btn.click()
204+
except SeleniumTimeoutException:
205+
pass
206+
except Exception as e:
207+
self.close()
208+
raise e
209+
210+
try:
211+
close_btn = self.driver \
212+
.find_element_by_css_selector("div.overlayWindow i.icon-close")
213+
close_btn.click()
214+
except Exception as e:
215+
self.close()
216+
raise e
217+
218+
def download_icomoon_fonts(self, zip_path: Path):
219+
"""
220+
Download the icomoon.zip from icomoon.io.
221+
:param zip_path: the path to the zip file after it's downloaded.
222+
"""
223+
try:
224+
print("Downloading Font files...")
225+
self.driver.find_element_by_css_selector(
226+
"a[href='#/select/font']"
227+
).click()
228+
229+
self.test_for_possible_alert(self.MED_WAIT_IN_SEC, "Continue")
230+
download_btn = WebDriverWait(self.driver, SeleniumRunner.LONG_WAIT_IN_SEC).until(
231+
ec.presence_of_element_located((By.CSS_SELECTOR, "button.btn4 span"))
232+
)
233+
download_btn.click()
234+
if self.wait_for_zip(zip_path):
235+
print("Font files downloaded.")
236+
else:
237+
raise TimeoutError(f"Couldn't find {zip_path} after download button was clicked.")
238+
except Exception as e:
239+
self.close()
240+
raise e
241+
242+
def wait_for_zip(self, zip_path: Path) -> bool:
243+
"""
244+
Wait for the zip file to be downloaded by checking for its existence
245+
in the download path. Wait time is self.LONG_WAIT_IN_SEC and check time
246+
is 1 sec.
247+
:param zip_path: the path to the zip file after it's
248+
downloaded.
249+
:return: True if the file is found within the allotted time, else
250+
False.
251+
"""
252+
end_time = time.time() + self.LONG_WAIT_IN_SEC
253+
while time.time() <= end_time:
254+
if zip_path.exists():
255+
return True
256+
time.sleep(1)
257+
return False
258+
259+
def close(self):
260+
"""
261+
Close the SeleniumRunner instance.
262+
"""
263+
print("Closing down SeleniumRunner...")
264+
self.driver.quit()

0 commit comments

Comments
 (0)