Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
feder-cr authored Sep 24, 2024
1 parent 7d6c38b commit 88c745d
Show file tree
Hide file tree
Showing 7 changed files with 653 additions and 0 deletions.
Empty file added tests/__init__.py
Empty file.
93 changes: 93 additions & 0 deletions tests/test_aihawk_authenticator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from src.aihawk_authenticator import AIHawkAuthenticator
from selenium.common.exceptions import NoSuchElementException, TimeoutException


@pytest.fixture
def mock_driver(mocker):
"""Fixture to mock the Selenium WebDriver."""
return mocker.Mock()


@pytest.fixture
def authenticator(mock_driver):
"""Fixture to initialize AIHawkAuthenticator with a mocked driver."""
return AIHawkAuthenticator(mock_driver)


def test_handle_login(mocker, authenticator):
"""Test handling the AIHawk login process."""
mocker.patch.object(authenticator.driver, 'get')
mocker.patch.object(authenticator, 'enter_credentials')
mocker.patch.object(authenticator, 'handle_security_check')

# Mock current_url as a regular return value, not PropertyMock
mocker.patch.object(authenticator.driver, 'current_url',
return_value='https://www.linkedin.com/login')

authenticator.handle_login()

authenticator.driver.get.assert_called_with(
'https://www.linkedin.com/login')
authenticator.enter_credentials.assert_called_once()
authenticator.handle_security_check.assert_called_once()


def test_enter_credentials_success(mocker, authenticator):
"""Test entering credentials."""
email_mock = mocker.Mock()
password_mock = mocker.Mock()

mocker.patch.object(WebDriverWait, 'until', return_value=email_mock)
mocker.patch.object(authenticator.driver, 'find_element',
return_value=password_mock)






def test_is_logged_in_true(mocker, authenticator):
"""Test if the user is logged in."""
buttons_mock = mocker.Mock()
buttons_mock.text = "Start a post"
mocker.patch.object(WebDriverWait, 'until')
mocker.patch.object(authenticator.driver, 'find_elements',
return_value=[buttons_mock])

assert authenticator.is_logged_in() is True


def test_is_logged_in_false(mocker, authenticator):
"""Test if the user is not logged in."""
mocker.patch.object(WebDriverWait, 'until')
mocker.patch.object(authenticator.driver, 'find_elements', return_value=[])

assert authenticator.is_logged_in() is False


def test_handle_security_check_success(mocker, authenticator):
"""Test handling security check successfully."""
mocker.patch.object(WebDriverWait, 'until', side_effect=[
mocker.Mock(), # Security checkpoint detection
mocker.Mock() # Security check completion
])

authenticator.handle_security_check()

# Verify WebDriverWait is called with EC.url_contains for both the challenge and feed
WebDriverWait(authenticator.driver, 10).until.assert_any_call(mocker.ANY)
WebDriverWait(authenticator.driver, 300).until.assert_any_call(mocker.ANY)


def test_handle_security_check_timeout(mocker, authenticator):
"""Test handling security check timeout."""
mocker.patch.object(WebDriverWait, 'until', side_effect=TimeoutException)

authenticator.handle_security_check()

# Verify WebDriverWait is called with EC.url_contains for the challenge
WebDriverWait(authenticator.driver, 10).until.assert_any_call(mocker.ANY)
14 changes: 14 additions & 0 deletions tests/test_aihawk_bot_facade.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest
# from src.linkedIn_job_manager import JobManager

@pytest.fixture
def job_manager():
"""Fixture for JobManager."""
return None # Replace with valid instance or mock later

def test_bot_functionality(job_manager):
"""Test AIHawk bot facade."""
# Example: test job manager interacts with the bot facade correctly
job = {"title": "Software Engineer"}
# job_manager.some_method_to_apply(job)
assert job is not None # Placeholder for actual test
97 changes: 97 additions & 0 deletions tests/test_aihawk_easy_applier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import pytest
from unittest import mock
from src.aihawk_easy_applier import AIHawkEasyApplier


@pytest.fixture
def mock_driver():
"""Fixture to mock Selenium WebDriver."""
return mock.Mock()


@pytest.fixture
def mock_gpt_answerer():
"""Fixture to mock GPT Answerer."""
return mock.Mock()


@pytest.fixture
def mock_resume_generator_manager():
"""Fixture to mock Resume Generator Manager."""
return mock.Mock()


@pytest.fixture
def easy_applier(mock_driver, mock_gpt_answerer, mock_resume_generator_manager):
"""Fixture to initialize AIHawkEasyApplier with mocks."""
return AIHawkEasyApplier(
driver=mock_driver,
resume_dir="/path/to/resume",
set_old_answers=[('Question 1', 'Answer 1', 'Type 1')],
gpt_answerer=mock_gpt_answerer,
resume_generator_manager=mock_resume_generator_manager
)


def test_initialization(mocker, easy_applier):
"""Test that AIHawkEasyApplier is initialized correctly."""
# Mock os.path.exists to return True
mocker.patch('os.path.exists', return_value=True)

easy_applier = AIHawkEasyApplier(
driver=mocker.Mock(),
resume_dir="/path/to/resume",
set_old_answers=[('Question 1', 'Answer 1', 'Type 1')],
gpt_answerer=mocker.Mock(),
resume_generator_manager=mocker.Mock()
)

assert easy_applier.resume_path == "/path/to/resume"
assert len(easy_applier.set_old_answers) == 1
assert easy_applier.gpt_answerer is not None
assert easy_applier.resume_generator_manager is not None


def test_apply_to_job_success(mocker, easy_applier):
"""Test successfully applying to a job."""
mock_job = mock.Mock()

# Mock job_apply so we don't actually try to apply
mocker.patch.object(easy_applier, 'job_apply')

easy_applier.apply_to_job(mock_job)
easy_applier.job_apply.assert_called_once_with(mock_job)


def test_apply_to_job_failure(mocker, easy_applier):
"""Test failure while applying to a job."""
mock_job = mock.Mock()
mocker.patch.object(easy_applier, 'job_apply',
side_effect=Exception("Test error"))

with pytest.raises(Exception, match="Test error"):
easy_applier.apply_to_job(mock_job)

easy_applier.job_apply.assert_called_once_with(mock_job)


def test_check_for_premium_redirect_no_redirect(mocker, easy_applier):
"""Test that check_for_premium_redirect works when there's no redirect."""
mock_job = mock.Mock()
easy_applier.driver.current_url = "https://www.linkedin.com/jobs/view/1234"

easy_applier.check_for_premium_redirect(mock_job)
easy_applier.driver.get.assert_not_called()


def test_check_for_premium_redirect_with_redirect(mocker, easy_applier):
"""Test that check_for_premium_redirect handles AIHawk Premium redirects."""
mock_job = mock.Mock()
easy_applier.driver.current_url = "https://www.linkedin.com/premium"
mock_job.link = "https://www.linkedin.com/jobs/view/1234"

with pytest.raises(Exception, match="Redirected to AIHawk Premium page and failed to return"):
easy_applier.check_for_premium_redirect(mock_job)

# Verify that it attempted to return to the job page 3 times
assert easy_applier.driver.get.call_count == 3
168 changes: 168 additions & 0 deletions tests/test_aihawk_job_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
from src.job import Job
from unittest import mock
from pathlib import Path
import os
import pytest
from src.aihawk_job_manager import AIHawkJobManager
from selenium.common.exceptions import NoSuchElementException
from loguru import logger


@pytest.fixture
def job_manager(mocker):
"""Fixture to create a AIHawkJobManager instance with mocked driver."""
mock_driver = mocker.Mock()
return AIHawkJobManager(mock_driver)


def test_initialization(job_manager):
"""Test AIHawkJobManager initialization."""
assert job_manager.driver is not None
assert job_manager.set_old_answers == set()
assert job_manager.easy_applier_component is None


def test_set_parameters(mocker, job_manager):
"""Test setting parameters for the AIHawkJobManager."""
# Mocking os.path.exists to return True for the resume path
mocker.patch('pathlib.Path.exists', return_value=True)

params = {
'company_blacklist': ['Company A', 'Company B'],
'title_blacklist': ['Intern', 'Junior'],
'positions': ['Software Engineer', 'Data Scientist'],
'locations': ['New York', 'San Francisco'],
'apply_once_at_company': True,
'uploads': {'resume': '/path/to/resume'}, # Resume path provided here
'outputFileDirectory': '/path/to/output',
'job_applicants_threshold': {
'min_applicants': 5,
'max_applicants': 50
},
'remote': False,
'distance': 50,
'date': {'all time': True}
}

job_manager.set_parameters(params)

# Normalize paths to handle platform differences (e.g., Windows vs Unix-like systems)
assert str(job_manager.resume_path) == os.path.normpath('/path/to/resume')
assert str(job_manager.output_file_directory) == os.path.normpath(
'/path/to/output')


def next_job_page(self, position, location, job_page):
logger.debug(f"Navigating to next job page: {position} in {location}, page {job_page}")
self.driver.get(
f"https://www.linkedin.com/jobs/search/{self.base_search_url}&keywords={position}&location={location}&start={job_page * 25}")


def test_get_jobs_from_page_no_jobs(mocker, job_manager):
"""Test get_jobs_from_page when no jobs are found."""
mocker.patch.object(job_manager.driver, 'find_element',
side_effect=NoSuchElementException)

jobs = job_manager.get_jobs_from_page()
assert jobs == []


def test_get_jobs_from_page_with_jobs(mocker, job_manager):
"""Test get_jobs_from_page when job elements are found."""
# Mock the no_jobs_element to behave correctly
mock_no_jobs_element = mocker.Mock()
mock_no_jobs_element.text = "No matching jobs found"

# Mocking the find_element to return the mock no_jobs_element
mocker.patch.object(job_manager.driver, 'find_element',
return_value=mock_no_jobs_element)

# Mock the page_source
mocker.patch.object(job_manager.driver, 'page_source',
return_value="some page content")

# Ensure jobs are returned as empty list due to "No matching jobs found"
jobs = job_manager.get_jobs_from_page()
assert jobs == [] # No jobs expected due to "No matching jobs found"


def test_apply_jobs_with_no_jobs(mocker, job_manager):
"""Test apply_jobs when no jobs are found."""
# Mocking find_element to return a mock element that simulates no jobs
mock_element = mocker.Mock()
mock_element.text = "No matching jobs found"

# Mock the driver to simulate the page source
mocker.patch.object(job_manager.driver, 'page_source', return_value="")

# Mock the driver to return the mock element when find_element is called
mocker.patch.object(job_manager.driver, 'find_element',
return_value=mock_element)

# Call apply_jobs and ensure no exceptions are raised
job_manager.apply_jobs()

# Ensure it attempted to find the job results list
assert job_manager.driver.find_element.call_count == 1


def test_apply_jobs_with_jobs(mocker, job_manager):
"""Test apply_jobs when jobs are present."""

# Mock no_jobs_element to simulate the absence of "No matching jobs found" banner
no_jobs_element = mocker.Mock()
no_jobs_element.text = "" # Empty text means "No matching jobs found" is not present
mocker.patch.object(job_manager.driver, 'find_element',
return_value=no_jobs_element)

# Mock the page_source to simulate what the page looks like when jobs are present
mocker.patch.object(job_manager.driver, 'page_source',
return_value="some job content")

# Mock the outer find_elements (scaffold-layout__list-container)
container_mock = mocker.Mock()

# Mock the inner find_elements to return job list items
job_element_mock = mocker.Mock()
# Simulating two job items
job_elements_list = [job_element_mock, job_element_mock]

# Return the container mock, which itself returns the job elements list
container_mock.find_elements.return_value = job_elements_list
mocker.patch.object(job_manager.driver, 'find_elements',
return_value=[container_mock])

# Mock the extract_job_information_from_tile method to return sample job info
mocker.patch.object(job_manager, 'extract_job_information_from_tile', return_value=(
"Title", "Company", "Location", "Apply", "Link"))

# Mock other methods like is_blacklisted, is_already_applied_to_job, and is_already_applied_to_company
mocker.patch.object(job_manager, 'is_blacklisted', return_value=False)
mocker.patch.object(
job_manager, 'is_already_applied_to_job', return_value=False)
mocker.patch.object(
job_manager, 'is_already_applied_to_company', return_value=False)

# Mock the AIHawkEasyApplier component
job_manager.easy_applier_component = mocker.Mock()

# Mock the output_file_directory as a valid Path object
job_manager.output_file_directory = Path("/mocked/path/to/output")

# Mock Path.exists() to always return True (so no actual file system interaction is needed)
mocker.patch.object(Path, 'exists', return_value=True)

# Mock the open function to prevent actual file writing
mock_open = mocker.mock_open()
mocker.patch('builtins.open', mock_open)

# Run the apply_jobs method
job_manager.apply_jobs()

# Assertions
assert job_manager.driver.find_elements.call_count == 3
# Called for each job element
assert job_manager.extract_job_information_from_tile.call_count == 2
# Called for each job element
assert job_manager.easy_applier_component.job_apply.call_count == 2
mock_open.assert_called() # Ensure that the open function was called
Loading

0 comments on commit 88c745d

Please sign in to comment.