Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ resources/.linkedin_cred
# for automation reasons this file needs to be created,
# please provide your own $LINKEDIN_USERNAME and $LINKEDIN_PASSWORD
#----------------------------------

# driver log [ignored geckodriver.log]
*.log
107 changes: 107 additions & 0 deletions geckodriver.log
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,110 @@ JavaScript error: resource://gre/modules/Sqlite.jsm, line 927: Error: Connection

###!!! [Parent][RunMessage] Error: Channel closing: too late to send/recv, messages will be lost

1619477345968 geckodriver INFO Listening on 127.0.0.1:54077
1619477346982 mozrunner::runner INFO Running command: "/usr/bin/firefox" "--marionette" "-headless" "--no-sandbox" "--disable-dev-shm-usage" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofileMP8bev"
*** You are running in headless mode.

(/usr/lib/firefox/firefox:17143): GLib-GObject-CRITICAL **: 00:49:08.072: g_object_set: assertion 'G_IS_OBJECT (object)' failed

(/usr/lib/firefox/firefox:17165): GLib-GObject-CRITICAL **: 00:49:08.286: g_object_set: assertion 'G_IS_OBJECT (object)' failed
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new Error("", "(unknown module)"))
1619477351103 Marionette INFO Listening on port 43151

(/usr/lib/firefox/firefox:17228): GLib-GObject-CRITICAL **: 00:49:11.172: g_object_set: assertion 'G_IS_OBJECT (object)' failed
1619477351251 Marionette WARN TLS certificate errors will be ignored for this session
1619477383665 Marionette INFO Stopped listening on port 43151

###!!! [Child][MessageChannel] Error: (msgtype=0x5D0009,name=PHttpChannel::Msg_DeletingChannel) Channel closing: too late to send/recv, messages will be lost


###!!! [Child][RunMessage] Error: Channel closing: too late to send/recv, messages will be lost


###!!! [Child][RunMessage] Error: Channel closing: too late to send/recv, messages will be lost


###!!! [Child][MessageChannel] Error: (msgtype=0x5D0004,name=PHttpChannel::Msg_Cancel) Channel closing: too late to send/recv, messages will be lost


###!!! [Child][MessageChannel] Error: (msgtype=0x5D0009,name=PHttpChannel::Msg_DeletingChannel) Channel closing: too late to send/recv, messages will be lost

JavaScript error: resource://gre/modules/Sqlite.jsm, line 927: Error: Connection is not open.

###!!! [Parent][RunMessage] Error: Channel closing: too late to send/recv, messages will be lost


###!!! [Parent][RunMessage] Error: Channel closing: too late to send/recv, messages will be lost


###!!! [Parent][RunMessage] Error: Channel closing: too late to send/recv, messages will be lost

1619514021308 geckodriver INFO Listening on 127.0.0.1:48933
1619514022322 mozrunner::runner INFO Running command: "/usr/bin/firefox" "--marionette" "-headless" "--no-sandbox" "--disable-dev-shm-usage" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofileQ8vu07"
*** You are running in headless mode.

(/usr/lib/firefox/firefox:21354): GLib-GObject-CRITICAL **: 11:00:23.426: g_object_set: assertion 'G_IS_OBJECT (object)' failed

(/usr/lib/firefox/firefox:21376): GLib-GObject-CRITICAL **: 11:00:23.611: g_object_set: assertion 'G_IS_OBJECT (object)' failed
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new Error("", "(unknown module)"))

(/usr/lib/firefox/firefox:21423): GLib-GObject-CRITICAL **: 11:00:24.770: g_object_set: assertion 'G_IS_OBJECT (object)' failed
1619514026605 Marionette INFO Listening on port 40047
1619514026666 Marionette WARN TLS certificate errors will be ignored for this session
1619514026804 Marionette INFO Stopped listening on port 40047
console.error: Region.jsm: "Error fetching region" (new TypeError("NetworkError when attempting to fetch resource.", ""))
console.error: Region.jsm: "Failed to fetch region" (new Error("NO_RESULT", "resource://gre/modules/Region.jsm", 419))
console.log: "RemoteSettingsWorker error: Error: Can't import when we've started shutting down."
console.log: "RemoteSettingsWorker error: Error: Can't import when we've started shutting down."
console.log: "RemoteSettingsWorker error: Error: Can't import when we've started shutting down."
console.log: "RemoteSettingsWorker error: Error: Can't import when we've started shutting down."
console.log: "RemoteSettingsWorker error: Error: Can't import when we've started shutting down."
console.error: "Could not load engine google@search.mozilla.org: [Exception... \"AddonManager is not initialized\" nsresult: \"0xc1f30001 (NS_ERROR_NOT_INITIALIZED)\" location: \"JS frame :: resource://gre/modules/AddonManager.jsm :: installBuiltinAddon :: line 2550\" data: no]"
console.error: "Could not load engine amazondotcom@search.mozilla.org: [Exception... \"AddonManager is not initialized\" nsresult: \"0xc1f30001 (NS_ERROR_NOT_INITIALIZED)\" location: \"JS frame :: resource://gre/modules/AddonManager.jsm :: installBuiltinAddon :: line 2550\" data: no]"
console.error: "Could not load engine wikipedia@search.mozilla.org: [Exception... \"AddonManager is not initialized\" nsresult: \"0xc1f30001 (NS_ERROR_NOT_INITIALIZED)\" location: \"JS frame :: resource://gre/modules/AddonManager.jsm :: installBuiltinAddon :: line 2550\" data: no]"
console.error: "Could not load engine bing@search.mozilla.org: [Exception... \"AddonManager is not initialized\" nsresult: \"0xc1f30001 (NS_ERROR_NOT_INITIALIZED)\" location: \"JS frame :: resource://gre/modules/AddonManager.jsm :: installBuiltinAddon :: line 2550\" data: no]"
console.error: "Could not load engine ddg@search.mozilla.org: [Exception... \"AddonManager is not initialized\" nsresult: \"0xc1f30001 (NS_ERROR_NOT_INITIALIZED)\" location: \"JS frame :: resource://gre/modules/AddonManager.jsm :: installBuiltinAddon :: line 2550\" data: no]"
console.warn: SearchService: "_init: abandoning init due to shutting down"
JavaScript error: resource://gre/modules/AsyncShutdown.jsm, line 575: uncaught exception: 2147500036
JavaScript error: resource://gre/modules/AsyncShutdown.jsm, line 575: uncaught exception: 2147500036
JavaScript error: resource://gre/modules/AsyncShutdown.jsm, line 575: uncaught exception: 2147500036
JavaScript error: resource://gre/modules/AsyncShutdown.jsm, line 575: uncaught exception: 2147500036
JavaScript error: resource://gre/modules/AsyncShutdown.jsm, line 575: uncaught exception: 2147500036

###!!! [Parent][RunMessage] Error: Channel closing: too late to send/recv, messages will be lost

1619514095729 geckodriver INFO Listening on 127.0.0.1:47423
1619514096740 mozrunner::runner INFO Running command: "/usr/bin/firefox" "--marionette" "-headless" "--no-sandbox" "--disable-dev-shm-usage" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofileTzBmGQ"
*** You are running in headless mode.

(/usr/lib/firefox/firefox:21572): GLib-GObject-CRITICAL **: 11:01:37.784: g_object_set: assertion 'G_IS_OBJECT (object)' failed

(/usr/lib/firefox/firefox:21594): GLib-GObject-CRITICAL **: 11:01:38.004: g_object_set: assertion 'G_IS_OBJECT (object)' failed
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new Error("", "(unknown module)"))

(/usr/lib/firefox/firefox:21643): GLib-GObject-CRITICAL **: 11:01:39.164: g_object_set: assertion 'G_IS_OBJECT (object)' failed
1619514100892 Marionette INFO Listening on port 36233
1619514100978 Marionette WARN TLS certificate errors will be ignored for this session
console.error: Region.jsm: "Error fetching region" (new Error("TIMEOUT", "resource://gre/modules/Region.jsm", 772))
console.error: Region.jsm: "Failed to fetch region" (new Error("TIMEOUT", "resource://gre/modules/Region.jsm", 419))
JavaScript error: https://static-exp1.licdn.com/sc/h/efncoxjdl37tofw662sg013tp, line 1: Error: Could not lazy load JS with src https://static-exp1.licdn.com/sc/h/b8nmakf6h0x06rajxf1vxrb8g
JavaScript error: , line 0: TypeError: NetworkError when attempting to fetch resource.
JavaScript error: https://static-exp1.licdn.com/sc/h/efncoxjdl37tofw662sg013tp, line 1: Error: Could not lazy load JS with src https://static-exp1.licdn.com/sc/h/b08gxllvwy6zylnb52u2u7ovr
1619514108613 Marionette INFO Stopped listening on port 36233
JavaScript error: resource://gre/modules/Sqlite.jsm, line 927: Error: Connection is not open.
1619514145907 geckodriver INFO Listening on 127.0.0.1:34031
1619514145914 mozrunner::runner INFO Running command: "/usr/bin/firefox" "--marionette" "--no-sandbox" "--disable-dev-shm-usage" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofile5IfFtc"
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new Error("", "(unknown module)"))
1619514150419 Marionette INFO Listening on port 42765
1619514150461 Marionette WARN TLS certificate errors will be ignored for this session
console.error: Region.jsm: "Error fetching region" (new Error("TIMEOUT", "resource://gre/modules/Region.jsm", 772))
console.error: Region.jsm: "Failed to fetch region" (new Error("TIMEOUT", "resource://gre/modules/Region.jsm", 419))
JavaScript error: https://static-exp1.licdn.com/sc/h/gou6qda3x8bdh2k8t17uu6y4, line 1: Error: GoogleOneTapError display: unknown_reason
1619514159199 Marionette INFO Stopped listening on port 42765
JavaScript error: resource://gre/modules/Sqlite.jsm, line 927: Error: Connection is not open.
1619514482813 geckodriver INFO Listening on 127.0.0.1:46739
1619514483826 mozrunner::runner INFO Running command: "/usr/bin/firefox" "--marionette" "--no-sandbox" "--disable-dev-shm-usage" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofilemlO84j"
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new Error("", "(unknown module)"))
1619514488008 Marionette INFO Listening on port 44165
1619514488090 Marionette WARN TLS certificate errors will be ignored for this session
1619514522985 Marionette INFO Stopped listening on port 44165
130 changes: 130 additions & 0 deletions sources/linkedin_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
'''
Script : linkedin_login.py
Author : SI Mkhonza
Creation Date : 04-04-2021
Finilised Date : 11-04-2021
'''
#3rd-Party Modules
from selenium.webdriver.common.by import By
#Developer-defined Modules
from sources.browser_helper import get_browser as init_browser, wait_for_element, click_on_element
#developer Defined Exceptions
class BrowserSetupError(Exception):
pass
class InvalidLoginArguments(ValueError):
pass
#Implementation
class Linkedin(object):
'''
Implements the automation of linkedin.com login process.
'''
def __init__(self):
self.browser = None
self.username_element = None
self.password_element = None
self.login_button_element = None

def set_browser(self, show_browser=False, enable_fullscreen=False)->None:
'''
helper function which initiates a browser instance
'''
self.browser = init_browser(
show_browser,
enable_fullscreen
)

def get_browser(self)->object:
'''
helper function which get the browser instance
'''
return self.browser

def __validate_login_argument(self, argument:str, argument_name:str)->str:
'''
helper function which validate login inputs
'''
# gaurd condition
if argument is None:
raise InvalidLoginArguments(
f"{argument_name} is None"
)
argument = argument.strip()
if not argument:
raise InvalidLoginArguments(
f"either, provide an empty, blank; {argument_name}"
)
# end of guard condition
return argument

def __goto_linkedin(self)->bool:
'''
helper function which goes to the linkedin.com site
'''
self.browser.get('https://www.linkedin.com/')
return True

def __get_username_element(self)->object:
'''
helper function which gets the username DOM element
'''
return wait_for_element(
browser=self.get_browser(),
locate_by=By.XPATH,
element_reference='//*[@id="session_key"]',
wait_for_n_seconds=2
)

def __get_password_element(self)->object:
'''
helper function which gets the password DOM element
'''
return wait_for_element(
browser=self.get_browser(),
locate_by=By.XPATH,
element_reference='//*[@id="session_password"]',
wait_for_n_seconds=2
)

def __get_login_button_element(self)->object:
'''
helper function which gets the login button DOM element
'''
return wait_for_element(
browser=self.get_browser(),
locate_by=By.XPATH,
element_reference='/html/body/main/section[1]/div[2]/form/button',
wait_for_n_seconds=2
)

def __locate_login_elements(self)->None:
'''
helper function which binds all login DOM elements
'''
self.username_element = self.__get_username_element()
self.password_element = self.__get_password_element()
self.login_button_element = self.__get_login_button_element()


def perform_login(self, username:str, password:str)->bool:
'''
helper function which performs the actual login process
'''
# let's validate
username = self.__validate_login_argument(username, 'username')
password = self.__validate_login_argument(password, 'password')
if self.get_browser() is None:
raise BrowserSetupError(
'self.set_browser() never called!'
)
# let's goto linkedin.com
self.__goto_linkedin()
# let's locate login elements
self.__locate_login_elements()
# let's perform actions on DOM elements
self.username_element.send_keys(username)
self.password_element.send_keys(password)
return click_on_element(
browser=self.get_browser(),
element=self.login_button_element,
wait_for_n_seconds=2
)
139 changes: 36 additions & 103 deletions tests/test_selenium_linkedin_login.py
Original file line number Diff line number Diff line change
@@ -1,105 +1,38 @@
#-------------------------------------------------------
# Script : test_selenium_linkedin_login.py
# Author : SI Mkhonza
# Creation Date : 26-04-2021
# finilised Date : 27-04-2021
#-------------------------------------------------------

# Built-in Modules
from time import sleep
'''
Script : test_selenium_linkedin_login.py
Author : SI Mkhonza
Creation Date : 26-04-2021
Finilised Date : 27-04-2021
'''
# built-in Modules
import os

# 3rd Party Modules
from selenium.webdriver.common.by import By

# developer-defined Modules
from sources.browser_helper import get_browser, wait_for_element, click_on_element

# developer Defined Exceptions
class InitiationError(Exception):
pass
class InvalidLoginArguments(ValueError):
pass
# -------------------------------------------------------------------------
class Linkedin(object):
def __validate_login_argument(self, argument:str, argument_name:str)->str:
# gaurd condition
if argument is None:
raise InvalidLoginArguments(f"{argument_name} is None")
argument = argument.strip()
if not argument:
raise InvalidLoginArguments(f"either, provide an empty, blank; {argument_name}")
# end of guard condition
return argument

def __goto_linkedin(self)->bool:
self.browser.get('https://www.linkedin.com/')
return True

def __login(self, username:str, password:str)->bool:
# then let's validate username and password
username = self.__validate_login_argument(username, 'username')
password = self.__validate_login_argument(password, 'password')
self.__goto_linkedin()
# let's locate DOM field 'elements'
username_field = wait_for_element(
browser=self.browser,
locate_by=By.XPATH,
element_reference='//*[@id="session_key"]',
wait_for_n_seconds=2
)
password_field = wait_for_element(
browser=self.browser,
locate_by=By.XPATH,
element_reference='//*[@id="session_password"]',
wait_for_n_seconds=2
)
login_button = wait_for_element(
browser=self.browser,
locate_by=By.XPATH,
element_reference='/html/body/main/section[1]/div[2]/form/button',
wait_for_n_seconds=2
)
# let's perform actions on DOM elements
username_field.send_keys(username)
password_field.send_keys(password)
return click_on_element(
browser=self.browser,
element=login_button,
wait_for_n_seconds=30
)

def perform_login(self, username=None, password=None, show_browser=True)->bool:
# let's get username and password from os.getenvirn(), if None is provided
USERNAME = os.getenv('LINKEDIN_USERNAME') if not username else username
PASSWORD = os.getenv('LINKEDIN_PASSWORD') if not password else password
self.browser = None
try:
# get browser driver
self.browser = get_browser(show_browser, enable_fullscreen=False)
except InitiationError:
print('Something want wrong; during Browser initialization!')
return False

# let's try to login:
did_login = False
try:
assert self.__login(
USERNAME,
PASSWORD
), 'Did not login to Linkedin!'
did_login = True
finally:
# clean up ...
if show_browser:
import time
time.sleep(5)
self.browser.close()
return did_login


def test_linkedin_login()->None:
from unittest import TestCase
# 3rd-party Modules
import pytest
# developer-defined Module
from sources.linkedin_login import Linkedin
'''
Test Implementation
'''
@pytest.fixture
def get_linkedin():
# setup
linkedin = Linkedin()
assert linkedin.perform_login(show_browser=False), 'Test failed, Couldnot Login to Linkedin!'


show_browser = False
linkedin.set_browser(
show_browser,
enable_fullscreen=False,
)
yield linkedin
# teardown
if show_browser:
import time
time.sleep(30)
linkedin.get_browser().close()


def test_linkedin_login(get_linkedin)->None:
assert get_linkedin.perform_login(
username=os.getenv('LINKEDIN_USERNAME'),
password=os.getenv('LINKEDIN_PASSWORD')
), 'Test failed, Couldnot Login to Linkedin!'