Selenium Focus Issues And How To Solve Them
himanshuseth004
Posted on December 21, 2020
Do you know that every website or web application comprises different web pages containing different web elements like buttons, text boxes, progress bars, etc.? However, the web is asynchronous in nature. Hence, all the web elements may not be loaded simultaneously, i.e., the web elements might be loaded dynamically using AJAX (Asynchronous JavaScript And XML).
While performing automation testing in Selenium, your test code or test script could result in erroneous results if it is interacting with a web element that is not yet loaded in the Document Object Model (DOM). Or if it is on another iFrame, tab, or window which is not in focus, or any such scenarios. These types of unexpected erroneous outcomes are known as Selenium focus issues.
Selenium can only control a browser when its window is in focus. How do you ensure that the Selenium test code interacts with web elements when they are in focus? You can use workarounds like the addition of minimal delay (in seconds) to ensure that the element on which the testing is performed has loaded. Still, that solution is not foolproof as any change in web page design could make the test inefficient.
This article will cover what the common Selenium focus issues are and how to solve these issues, with examples.
Before we get started, let’s look at some of the prerequisites required for the development. You need to have Python & Selenium installed on your machine. You can download Python installation files for Windows from here. To install Selenium, you should execute pip install selenium on your terminal. PyCharm IDE (Community Edition) is used for implementation, and you can download it from here. Next, you should download the WebDriver of the browser on which testing is performed; mentioned below are the locations from where you can download WebDriver for browsers like Firefox, Chrome, Internet Explorer, etc.
For in-depth information about Python’s usage with the Selenium framework for automated cross browser testing, please refer to this blog on our website. Now that the setup is ready let’s look at some of the ways to fix Selenium focus issues.
Selenium Focus Issues With Multiple iFrames
You would have come across many websites that have advertisements or follow a design where an HTML document is embedded inside another HTML document. That particular web element is called an iFrame. iFrame is defined by the < iframe >< /iframe >
tag in a web page. To switch between different iFrames, you can make use of switch_to(self, frame-reference) API.
For demonstration purposes, we use the webpage, which contains three iFrames. Below is the HTML code of the page, which only details information about the iFrames
iFrame code of the web page https://seleniumhq.github.io/selenium/docs/api/java/index.html
<frameset cols="20%,80%" title="Documentation frame" onload="top.loadFrames()">
<frameset rows="30%,70%" title="Left frames" onload="top.loadFrames()">
<frame src="overview-frame.html" name="packageListFrame" title="All Packages">
<frame src="allclasses-frame.html" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
</frameset>
<frame src="overview-summary.html" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
<noframes>
As shown in the HTML code above, the three frames’ names are packageListFrame, packageFrame, and classFrame. To understand the Selenium focus issue better, let us see an example. In the below code, we will be using the iFrames of this webpage. We will simply try to click on “com.thoughtworks.selenium” present on “classFrame.”
# Example - Show Selenium lose focus issue with iFrames
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from time import sleep
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://seleniumhq.github.io/selenium/docs/api/java/index.html")
# iFrame code of the web page https://seleniumhq.github.io/selenium/docs/api/java/index.html
#<frameset cols="20%,80%" title="Documentation frame" onload="top.loadFrames()">
#<frameset rows="30%,70%" title="Left frames" onload="top.loadFrames()">
#<frame src="overview-frame.html" name="packageListFrame" title="All Packages">
#<frame src="allclasses-frame.html" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
#</frameset>
#<frame src="overview-summary.html" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
#<noframes>
driver.maximize_window()
print("Window is maximized")
# Test 1
print("[1] - Test on classFrame")
driver.find_element_by_link_text("com.thoughtworks.selenium").click()
driver.implicitly_wait(20)
print("Completion of tests")
# Free up the resources
driver.close()
driver.quit()
Upon execution, the above test gives the following error:
This error occurs because the WebDriver instance is unable to focus on iFrame “classFrame” upon which the element com.thoughtworks.selenium is present. Similarly, in many such scenarios, when the instance’s focus is on some other iFrame, and the required element is on other, the Selenium focus issues happen.
How To Solve Selenium Lose Focus Issue With Multiple iFrames
There are several ways through which you can navigate between iFrames on a webpage. One of the most simple and popular methods is by using switch_to().frame(). The switch_to().frame() method is used to conveniently switch between iFrames. Let us see how to use the switch_to().frame() method to solve the Selenium lose focus issue:
Name of the iFrame – Each iFrame has a name associated with it. You can find the name using the Inspect Tool in the web browser. The name of the iFrame to which you want to switch to can be passed to driver.switch_to.frame(‘frame_name’) API. In the above case, if you plan to navigate to classFrame iFrame, you simply have to use driver.switch_to.frame(“classFrame”).
If your focus is already there on another iFrame, you need to switch back to the parent frame using the following APIs
driver.switch_to.default_content()
driver.switch_to.parent_frame()
ID of the iFrame – You can also switch between iFrames by using the id associated with the iFrame. driver.find_element_by_id(‘frame-id’) where frame-id is used as a locator to switch to a specific iFrame.
Tag name & Index combination – You can make use of the API driver.find_elements_by_tag_name() along with the index of the destination iFrame.
Iframe-name = driver.find_elements_by_tag_name('tag-name')[index]
driver.switch_to.frame(iframe-name)
Now that we have looked into different ways to switch between iFrames let’s look at an example of switching between three iFrames on the webpage.
# Example 1 - Switching between iFrames
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from time import sleep
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://seleniumhq.github.io/selenium/docs/api/java/index.html")
# iFrame code of the web page https://seleniumhq.github.io/selenium/docs/api/java/index.html
#<frameset cols="20%,80%" title="Documentation frame" onload="top.loadFrames()">
#<frameset rows="30%,70%" title="Left frames" onload="top.loadFrames()">
#<frame src="overview-frame.html" name="packageListFrame" title="All Packages">
#<frame src="allclasses-frame.html" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
#</frameset>
#<frame src="overview-summary.html" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
#<noframes>
driver.maximize_window()
print("Window is maximized")
# Test 1
print("[1] - Test on classFrame")
driver.switch_to.frame("classFrame")
driver.find_element_by_link_text("com.thoughtworks.selenium").click()
driver.implicitly_wait(20)
# Switch to the parent frame
driver.switch_to.default_content()
driver.switch_to.parent_frame()
print("[2] - Test on packageFrame")
driver.switch_to.frame("packageFrame")
driver.find_element_by_link_text("WebDriver").click()
driver.implicitly_wait(20)
# Switch to the parent frame
driver.switch_to.default_content()
driver.switch_to.parent_frame()
print("[3] - Switching to the PackageListFrame")
driver.switch_to.frame("packageListFrame")
driver.find_element_by_link_text("org.openqa.selenium.support.pagefactory").click()
driver.implicitly_wait(20)
driver.switch_to.default_content()
driver.switch_to.parent_frame()
time.sleep(20)
print("Completion of tests")
# Free up the resources
driver.close()
driver.quit()
driver.switch_to.frame() API is used with the frame-name as the parameter to switch between different iFrames. You can use the HTML source of the webpage to get the iFrame related information or hover over the designated iFrame & inspect element using the Inspect option in the web browser.
Once you are on the required iFrame, search for a particular link using find_element_by_link_text(), and click operation is performed. An implicit wait of 20 seconds (driver.implicitly_wait(20)) is added between different operations performed on two iFrames so that there is enough time to switch to the parent frame. You need to do a trial & error before zeroing on the wait time; however, care has to be taken that the wait time is not too high else it would delay the execution of the other tests.
Selenium Focus Issues With Multiple Tabs
There would be multiple test scenarios when using automation testing with Selenium, where tests need to be performed on different browser windows. In such cases, multiple browser windows or multiple tabs have to be opened, and test operations on one browser have to be performed. This is also applicable for automation tests that are done in parallel, which is where Selenium’s focus issues are more common.
Let us see the Selenium lose focus issue with multiple tabs with the help of an example. In the below example, we will try to open multiple tabs (2) and try to run some tests on them:
# Example - Selenium lose focus issue with multiple tabs
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from time import sleep
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
primary_url = 'https://www.duckduckgo.com'
secondary_url = 'https://www.lambdatest.com'
driver = webdriver.Chrome()
driver.get(primary_url)
print("[1] - Current Page Title is : %s" %driver.title)
# Add sleep for checking the results
time.sleep(10)
driver.execute_script("window.open('');")
driver.get(secondary_url)
print("[2] - Current Page Title is : %s" %driver.title)
# Let us perform some test on the DuckDuckGo tab, i.e. Tab 1
print("Performing operations on the first tab now")
# Clicking on "Add DuckDuckGo to Chrome" button
self.driver.find_element_by_xpath(self,'//*[@id="content_homepage"]/div[2]/div/div/div/span').click()
print("DuckDuckGo added to Chrome")
# Free the resources
driver.close()
driver.quit()
Upon executing the above test, we will get an error:
This error occurred because the WebDriver instance did not focus on the first tab when we tried to perform tests on it. Instead, the focus was on the second tab because we recently created a new tab. Similarly, when dealing with multiple tabs or windows, this Selenium lose focus issue is very common to occur.
How To Solve Selenium Focus Issue With Multiple Tabs
Each window has a window handle associated with it, and that handle is a unique number. For example, if there are two browser windows/browser tabs opened simultaneously, one window’s window handle will be ‘0’, and the other one will be ‘1’. The window handle will be freed once the browser window/tab is closed. To switch tabs, we make use of the driver.window_handles[handle-number] API. So, for instance, to switch to the window with window handle ‘1’, you can use driver.switch_to.window(driver.window_handles[1]).
To open a new tab i.e. child window from the parent browser window, we use the JavaScript executor driver.execute_script(“window.open(”);”). To open a new browser window, you should use driver.execute_script(“window.open(‘secondary-url’, ‘new window’)”)
# Switching between Tabs using Python and Selenium
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from time import sleep
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
primary_url = 'https://www.duckduckgo.com'
secondary_url = 'https://www.lambdatest.com'
driver = webdriver.Chrome()
driver.get(primary_url)
print("[1] - Current Page Title is : %s" %driver.title)
print("Perform the operations for the first window here")
# Add sleep for checking the results
time.sleep(10)
driver.execute_script("window.open('');")
driver.switch_to.window(driver.window_handles[1])
driver.get(secondary_url)
print("[2] - Current Page Title is : %s" %driver.title)
print("Perform the operations for the second tab here")
time.sleep(10)
driver.close()
driver.switch_to.window(driver.window_handles[0])
print("[3] - Current Page Title is : %s" %driver.title)
# Free the resources
driver.close()
driver.quit()
As seen in the execution snapshot below, the parent window with the test URL (https://www.duckduckgo.com) is first opened. The window handle of this browser window is ‘0’. After a sleep of 10 seconds, a new tab is opened with the test URL (https://www.lambdatest.com), and the window handle associated with this tab is ‘1’. driver.switch_to.window(driver.window_handles[0]) is used to switch back to the parent window (i.e. window handle – 0).
Selenium Focus Issues With onfocus() And onblur() Callbacks
There are many websites where you would have encountered a form validation code. For example, suppose you came across a website asking you to create a desired username as per the set criteria. But as soon as you enter the username and move out of the text box, a warning appears stating the entered username is incorrect along with the rules of setting username. These pop-ups or warnings stating messages like the entered password does not meet the requirements, or the password field is left empty, etc. are created with the help of onfocus() and onblur() methods. onfocus() and onblur() events are used in the majority of websites/web applications.
The onfocus() event is fired when a particular web element gets focus. The web element could be a hyperlink (< a >
), input element (< input >
), select element (< select >
), etc. For example, in the below code, the input window’s color would change when it gets focus.
<!DOCTYPE html>
<html>
<body>
Email: <input type="text" onfocus="lambdaTestFunction(this)">
<p>The above text box gets colored as soon as its gets focus, with help of onfocus() event.</p>
<script>
function lambdaTestFunction(x) {
x.style.background = "aqua";
}
</script>
</body>
</html>
Output:
Before triggering onfocus event
After triggering onfocus event
On the other hand, the onblur() event is fired when the control is shifted from a web element without any change in the web element’s value. For example, the user keeps the user-name field empty, and the focus is shifted to the next field. In the below example, the onblur event will convert all input box characters to lowercase as soon as it loses focus.
<!DOCTYPE html>
<html>
<body>
Email: <input type="text" onblur="LTFunction(this)">
<p>The entered input gets converted to lowercase as soon as the input box loses focus, with help of onblur() event.</p>
<script>
function LTFunction(x) {
x.value = x.value.toLowerCase();
}
</script>
</body>
</html>
Output:
Before triggering onblur event
After triggering onblur event
Now when these events are triggered simultaneously during automation testing in Selenium, there can be situations when one or the other callback event doesn’t happen or gets triggered. One such scenario is with a dropdown. Suppose there is a dropdown whose value you want to select using Selenium, such that the value of the dropdown changes when the onblur() event is triggered. In such a scenario, the Selenium WebDriver instance might not be able to trigger the onblur() event, thereby resulting in Selenium lose focus issue.
How To Handle Selenium Lose Focus Issues With onfocus() And onblur() Callbacks
Now let us see an example to see how to handle Selenium focus issues using onfocus and onblur events. The example below shows the demonstration of onfocus() and onblur() events on the URL https://output.jsbin.com/remozek/1. execute_script() method through the WebDriver instance is invoked to trigger the required events. driver.execute_script(“arguments[0].blur();”, textfield_path) has arguments[xx] as parameter which is passed from Python code to the JavaScript code that has to be executed.
# Example 3 - Demonstration of focus(), blur(), and send_keys()
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from time import sleep
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.maximize_window()
driver.get('https://output.jsbin.com/remozek/1')
textfield_path = driver.find_element_by_xpath("//*[@id='textField']")
print("Focus Event Triggered")
driver.execute_script("arguments[0].focus();", textfield_path)
time.sleep(5)
print("Blur event triggered")
driver.execute_script("arguments[0].blur();", textfield_path)
time.sleep(5)
driver.find_element_by_id("textField").clear()
time.sleep(5)
driver.find_element_by_id("textField").send_keys("Lambdatest")
time.sleep(5)
driver.find_element_by_id("plainButton").click()
time.sleep(5)
print("Button is clicked")
time.sleep(5)
# Clear the resources
driver.close()
driver.quit()
As shown in the implementation above, the textbox element is located using the XPath expression, where the id of the web element is passed as the input argument.
textfield_path = driver.find_element_by_xpath("//*[@id='textField']")
Inspect Element is used to get information about the id of the web element, i.e., text box and JavaScript code that needs to be executed on the execution of the onfocus() and onblur() events.
The code is executed using python < file-name.py >
command, as shown in the output snapshot; the onFocus() and onblur() events are triggered using JavaScript code triggered via Selenium Webdriver.
How To Handle Selenium Focus Issues With Incomplete Page Loading
As mentioned in the blog’s starting, the majority of the Selenium focus issues occur when the operation is performed on a web element that is not yet loaded, or the loading of the web page is not yet complete. In this article, we will see two ways to handle such Selenium lose focus issues:
- Wait until the required Web Element’s loading is found, using presence_of_element_located()
- Find the Web Element by inspecting HTML Source, using elem.get_attribute()
In the cases mentioned above of Selenium focus issues, WebDriverWait() with a specified time duration can be used to check the web element’s presence on the page. If the required web element is not present, TimeoutException is invoked, the next set of instructions is executed.
The unittest framework is used to demonstrate automation testing with Selenium. To fetch details about the web locator, i.e., XPath, CSS Selector, Name, Link Text, ID, etc., we again use the Inspect Element option in the web browser. In the example below, we have used the NAME and XPath locators to perform actions on those web elements.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.edge.options import Options
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.lambdatest.com")
try:
myElem_1 = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'home-btn')))
print("Home Button click under progress")
myElem_1.click()
myElem_2 = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "/html/body/div[1]/section/div/div[2]/div/form/div[3]/p/a")))
print("Login button click under progress")
myElem_2.click()
sleep(10)
except TimeoutException:
print("No element found")
sleep(10)
driver.close()
As seen in the implementation, a WebDriverWait() of 10 seconds is used for the two web elements. Once the web elements are loaded, the required operations are performed on them. However, if the time duration of 10 seconds elapses, TimeOutException() occurs, and the error message No element found is printed. Below is the snapshot of the execution window:
Apart from these mechanisms, there are other ways to tackle Selenium focus issues. One of them is inspecting the HTML source code when doing automation testing with Selenium and checking the presence of an element on the page.
How To Handle Selenium Focus Issue By Inspecting HTML Source
Apart from using presence_of_element_located() to check the presence of web elements to handle Selenium focus issues, another approach is to inspect the HTML source for the presence of the required web element. To get the HTML source of a webpage, elem.get_attribute(“innerHTML”) is used, and a search for the required web element is performed to check the presence of the element on the web page.
In the example shown below, the HTML source of the URL under test https://www.lambdatest.com is written into an external file (optional step), and search for the button home-btn is performed. It matches the string in the HTML source; hence the search operation may take time if the source code is large in size.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from time import sleep
import io
driver = webdriver.Chrome()
driver.get("https://www.lambdatest.com")
elem = driver.find_element_by_xpath("//*")
source_code = elem.get_attribute("innerHTML")
# Optional - Write the code in an external file on the disk
filename = open('lambdatest_page_source.html', 'w')
filename.write(source_code)
filename.close()
# Search for a particular element in the source code
if "home-btn" in source_code:
print("Home Button is present")
else:
print("Home button is not present")
sleep(5)
driver.close()
These are some mechanisms that can be used in automated cross browser testing for fixing Selenium focus problems. Selenium focus issues are more prevalent when doing parallel testing since the control might shift from one window to another. This is a common issue when automation testing with Selenium is done using the local Selenium WebDriver. Even if Remote Selenium WebDriver is used for automated cross browser testing, the approach might not be scalable since you cannot have an in-house setup with different combinations of web browsers, operating systems, and devices.
Parallel testing has to be used when using automation testing with Selenium as it improves the test process’s overall efficiency. Rather than local automated cross browser testing, a better approach is to use remote automated cross browser testing on the cloud. Using a platform like LambdaTest, you can perform automated and live interactive automated cross browser testing on 2000+ real browsers and operating systems online. Since tests can be executed in parallel, there is a huge reduction in the overall turn-around time of the testing phase. Also, tests being executed can be triggered on different machines, thereby minimizing the chances of Selenium focus problems that you encounter with automation testing with Selenium on a local machine.
Solving Selenium Focus Issues On Cloud Selenium Grid
To get started with automated cross browser testing on the cloud, you need to create an account on LambdaTest. Once the account is created, you should note the username and access-key are located at https://accounts.lambdatest.com/profile. This combination is used to access the Selenium Grid on LambdaTest. Though we have used the Python language for implementation, you can use other development languages like C#, Perl, Ruby on Rails, etc. to perform automation testing with Selenium on LambdaTest’s Remote Selenium Grid.
Every test that is performed on the LambdaTest grid has a unique test-id associated with it. The Automation Tab contains the information of all the automated tests that have been executed so far. To set the browser capabilities, you should use the LambdaTest Capability Generator. Select the development language as Python and choose the correct settings of the browser, operating system, resolution, etc.
For demonstration, we use Chrome 67.0 on macOS (OS X Mavericks) operating system. Now that the setup is ready, we port the implementation that demonstrated the usage of onfocus() and onblur() events in automation testing with Selenium.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from time import sleep
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.keys import Keys
import urllib3
############# Changes for porting local Selenium WebDriver implementation to LambdaTest #############
#Set capabilities for Chrome on macOS
capabilities = {
"build": "Build to resolve Selenium Focus issues",
"name": "Selenium Focus [Chrome + macOS]",
"platform": "OS X Mavericks",
"browserName": "Chrome",
"version": "67.0",
"visual": True
}
# Obtain the credentials from https://accounts.lambdatest.com/profile
username = os.environ.get("LT_USERNAME")
access_key = os.environ.get("LT_ACCESS_KEY")
# driver = webdriver.Chrome()
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
remote_url = "http://" + username + ":" + access_key + "@hub.lambdatest.com/wd/hub"
driver = webdriver.Remote(command_executor=remote_url, desired_capabilities=capabilities)
driver.maximize_window()
driver.get('https://output.jsbin.com/remozek/1')
textfield_path = driver.find_element_by_xpath("//*[@id='textField']")
print("Focus Event Triggered")
driver.execute_script("arguments[0].focus();", textfield_path)
time.sleep(5)
print("Blur event triggered")
driver.execute_script("arguments[0].blur();", textfield_path)
time.sleep(5)
driver.find_element_by_id("textField").clear()
time.sleep(5)
driver.find_element_by_id("textField").send_keys("Lambdatest")
time.sleep(5)
driver.find_element_by_id("plainButton").click()
time.sleep(5)
print("Button is clicked")
time.sleep(5)
# Clear the resources
driver.close()
driver.quit()
As seen in the source code, there are minimal changes to port from local Selenium Grid to Selenium Grid on LambdaTest. The combination of username & pass-key is passed to the remote URL where the execution is performed.
remote_url = "http://" + username + ":" + access_key + "@hub.lambdatest.com/wd/hub"
The remote URL and browser capabilities that were generated using the LambdaTest capabilities generator are passed to the Selenium WebDriver API. Apart from these changes, the rest of the implementation is the same. Using parallel testing, you can perform multiple tests that do automation testing with Selenium.
The code is executed in a similar manner using the local terminal window. You should visit the Automation Tab to check the status of the automation test.
In case you want to perform automated cross browser testing on locally hosted pages, you can use the Lambda Tunnel app. Using the Lambda Tunnel app, you can test your locally hosted pages on the LambdaTest Selenium Automation Platform. You can connect to the LambdaTest servers from your local machine using SSH based integration. Please refer to this article for more information on leveraging the Lambda Tunnel for automated cross browser testing.
Conclusion
Though automation testing with Selenium is a very popular approach for automated cross browser testing, you might encounter Selenium focus issues, which may impact the tests’ efficiency. Selenium focus issues can be more frequent when tests are executed in parallel. You can use Implicit wait or Explicit wait to ensure that the required web element is present in the DOM, but a better approach would be to use a WebDriverWait() with a wait duration of a few seconds. You can also execute JavaScript code in the Selenium WebDriver, where JavaScript runs in the context of the current frame/window. The automated cross browser testing activity can be shifted to a cross browser testing platform like LambdaTest for improved efficiency and better scalability.
Thank you for reading. If you have any questions, don’t hesitate to reach out via the comment section below.
Posted on December 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.