diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_csv.py b/tests/test_csv.py
new file mode 100644
index 0000000..dd49314
--- /dev/null
+++ b/tests/test_csv.py
@@ -0,0 +1,72 @@
+import pytest
+from ipars import CsvManager
+
+
+def test_writerow_and_getrows(tmp_path):
+ cm = CsvManager()
+ filepath = str(tmp_path / 'data.csv')
+ row = ['Alice', '30', 'Engineer']
+
+ cm.writerow(filepath, 'w', row)
+ result = cm.getRows(filepath)
+
+ assert result == [row]
+
+
+def test_writerows_and_getrows(tmp_path):
+ cm = CsvManager()
+ filepath = str(tmp_path / 'data.csv')
+ rows = [['Alice', '30'], ['Bob', '25'], ['Carol', '28']]
+
+ cm.writerows(filepath, 'w', rows)
+ result = cm.getRows(filepath)
+
+ assert result == rows
+
+
+def test_append_mode(tmp_path):
+ cm = CsvManager()
+ filepath = str(tmp_path / 'data.csv')
+
+ cm.writerow(filepath, 'w', ['first'])
+ cm.writerow(filepath, 'a', ['second'])
+ result = cm.getRows(filepath)
+
+ assert result == [['first'], ['second']]
+
+
+def test_custom_delimiter(tmp_path):
+ cm = CsvManager(delimiter=',')
+ filepath = str(tmp_path / 'data.csv')
+ row = ['one', 'two', 'three']
+
+ cm.writerow(filepath, 'w', row)
+ result = cm.getRows(filepath)
+
+ assert result == [row]
+
+
+def test_invalid_mode_raises(tmp_path):
+ cm = CsvManager()
+ filepath = str(tmp_path / 'data.csv')
+ with pytest.raises(ValueError):
+ cm.writerow(filepath, 'x', ['data'])
+
+
+def test_invalid_row_type_raises(tmp_path):
+ cm = CsvManager()
+ filepath = str(tmp_path / 'data.csv')
+ with pytest.raises(ValueError):
+ cm.writerow(filepath, 'w', 'not a list')
+
+
+def test_invalid_constructor_args():
+ with pytest.raises(ValueError):
+ CsvManager(delimiter=123)
+
+
+def test_pprint_does_not_raise(capsys):
+ cm = CsvManager()
+ cm.pprint([['a', 'b'], ['c', 'd']])
+ captured = capsys.readouterr()
+ assert 'a' in captured.out
diff --git a/tests/test_json.py b/tests/test_json.py
new file mode 100644
index 0000000..ef51a6e
--- /dev/null
+++ b/tests/test_json.py
@@ -0,0 +1,65 @@
+import pytest
+from ipars import JsonManager
+
+
+def test_dump_and_load(tmp_path):
+ jm = JsonManager()
+ filepath = str(tmp_path / 'data.json')
+ data = {'name': 'ipars', 'version': 3, 'active': True}
+
+ jm.dump(filepath, data)
+ result = jm.load(filepath)
+
+ assert result == data
+
+
+def test_dump_and_load_list(tmp_path):
+ jm = JsonManager()
+ filepath = str(tmp_path / 'list.json')
+ data = [1, 2, 3, 'hello']
+
+ jm.dump(filepath, data)
+ result = jm.load(filepath)
+
+ assert result == data
+
+
+def test_dump_and_load_with_cyrillic(tmp_path):
+ jm = JsonManager()
+ filepath = str(tmp_path / 'cyrillic.json')
+ data = {'текст': 'привет'}
+
+ jm.dump(filepath, data)
+ result = jm.load(filepath)
+
+ assert result == data
+
+
+def test_load_nonexistent_file():
+ jm = JsonManager()
+ with pytest.raises(FileNotFoundError):
+ jm.load('nonexistent_file_xyz.json')
+
+
+def test_invalid_encoding_raises():
+ with pytest.raises(ValueError):
+ JsonManager(encoding=123)
+
+
+def test_load_invalid_path_type():
+ jm = JsonManager()
+ with pytest.raises(ValueError):
+ jm.load(123)
+
+
+def test_dump_invalid_path_type(tmp_path):
+ jm = JsonManager()
+ with pytest.raises(ValueError):
+ jm.dump(123, {'key': 'value'})
+
+
+def test_pprint_does_not_raise(capsys):
+ jm = JsonManager()
+ jm.pprint({'key': 'value'})
+ captured = capsys.readouterr()
+ assert 'key' in captured.out
diff --git a/tests/test_pars.py b/tests/test_pars.py
new file mode 100644
index 0000000..4555d05
--- /dev/null
+++ b/tests/test_pars.py
@@ -0,0 +1,189 @@
+import pytest
+from unittest.mock import patch, MagicMock
+from bs4 import BeautifulSoup
+from ipars import Pars
+
+
+# --- exists ---
+
+def test_exists_true(tmp_path):
+ p = Pars()
+ assert p.exists(str(tmp_path)) is True
+
+
+def test_exists_false():
+ p = Pars()
+ assert p.exists('this_path_does_not_exist_xyz') is False
+
+
+def test_exists_invalid_type():
+ p = Pars()
+ with pytest.raises(ValueError):
+ p.exists(123)
+
+
+# --- listdir ---
+
+def test_listdir_returns_list(tmp_path):
+ p = Pars()
+ (tmp_path / 'a.txt').write_text('a')
+ (tmp_path / 'b.txt').write_text('b')
+ result = p.listdir(str(tmp_path))
+ assert isinstance(result, list)
+ assert 'a.txt' in result
+ assert 'b.txt' in result
+
+
+def test_listdir_invalid_type():
+ p = Pars()
+ with pytest.raises(ValueError):
+ p.listdir(42)
+
+
+# --- mkdir ---
+
+def test_mkdir_creates_directory(tmp_path):
+ p = Pars()
+ new_dir = str(tmp_path / 'new_folder')
+ p.mkdir(new_dir)
+ assert (tmp_path / 'new_folder').exists()
+
+
+def test_mkdir_does_not_raise_if_exists(tmp_path):
+ p = Pars()
+ existing = str(tmp_path)
+ p.mkdir(existing) # не должен бросать ошибку
+
+
+def test_mkdir_invalid_type():
+ p = Pars()
+ with pytest.raises(ValueError):
+ p.mkdir(99)
+
+
+# --- returnBs4Object ---
+
+def test_returnBs4Object(tmp_path):
+ p = Pars()
+ html_file = tmp_path / 'page.html'
+ html_file.write_text('
Hello
', encoding='utf-8')
+
+ soup = p.returnBs4Object(str(html_file))
+
+ assert soup.find('h1').text == 'Hello'
+
+
+def test_returnBs4Object_returns_beautifulsoup(tmp_path):
+ p = Pars()
+ html_file = tmp_path / 'page.html'
+ html_file.write_text('Test
', encoding='utf-8')
+
+ result = p.returnBs4Object(str(html_file))
+
+ assert isinstance(result, BeautifulSoup)
+
+
+# --- getTexts ---
+
+def _make_soup_elements(html: str) -> list:
+ soup = BeautifulSoup(html, 'lxml')
+ return soup.find_all('div')
+
+
+def test_getTexts_basic():
+ p = Pars()
+ elements = _make_soup_elements('Hello
World
')
+ result = p.getTexts(elements)
+ assert result == ['Hello', 'World']
+
+
+def test_getTexts_needfix_strips_whitespace():
+ p = Pars()
+ elements = _make_soup_elements(' Hello \n
\t World\t
')
+ result = p.getTexts(elements, needFix=True)
+ assert result == ['Hello', 'World']
+
+
+def test_getTexts_returns_none_when_empty():
+ p = Pars()
+ elements = _make_soup_elements('')
+ result = p.getTexts(elements)
+ assert result is None
+
+
+def test_getTexts_invalid_type():
+ p = Pars()
+ with pytest.raises(ValueError):
+ p.getTexts('not a list')
+
+
+# --- getAttributes ---
+
+def test_getAttributes_basic():
+ p = Pars()
+ soup = BeautifulSoup('12', 'lxml')
+ elements = soup.find_all('a')
+ result = p.getAttributes(elements, 'href')
+ assert result == ['http://a.com', 'http://b.com']
+
+
+def test_getAttributes_missing_attr():
+ p = Pars()
+ soup = BeautifulSoup('no href
', 'lxml')
+ elements = soup.find_all('div')
+ result = p.getAttributes(elements, 'href')
+ assert result is None
+
+
+def test_getAttributes_invalid_type():
+ p = Pars()
+ with pytest.raises(ValueError):
+ p.getAttributes('not a list', 'href')
+
+
+# --- pprint ---
+
+def test_pprint_does_not_raise(capsys):
+ p = Pars()
+ p.pprint({'key': 'value', 'list': [1, 2, 3]})
+ captured = capsys.readouterr()
+ assert 'key' in captured.out
+
+
+# --- getStaticPage (с моком запроса) ---
+
+def test_getStaticPage_saves_file(tmp_path):
+ p = Pars()
+ filepath = str(tmp_path / 'page.html')
+
+ mock_response = MagicMock()
+ mock_response.status_code = 200
+ mock_response.text = 'Mocked'
+
+ with patch('requests.get', return_value=mock_response):
+ status = p.getStaticPage(filepath, 'http://example.com')
+
+ assert status == 200
+ assert (tmp_path / 'page.html').read_text(encoding='utf-8') == mock_response.text
+
+
+def test_getStaticPage_binary_mode(tmp_path):
+ p = Pars()
+ filepath = str(tmp_path / 'image.png')
+
+ mock_response = MagicMock()
+ mock_response.status_code = 200
+ mock_response.content = b'\x89PNG\r\n'
+
+ with patch('requests.get', return_value=mock_response):
+ status = p.getStaticPage(filepath, 'http://example.com/img.png', writeMethod='wb')
+
+ assert status == 200
+ assert (tmp_path / 'image.png').read_bytes() == mock_response.content
+
+
+def test_getStaticPage_invalid_write_method(tmp_path):
+ p = Pars()
+ filepath = str(tmp_path / 'page.html')
+ with pytest.raises(ValueError):
+ p.getStaticPage(filepath, 'http://example.com', writeMethod='r')
diff --git a/tests/test_progress.py b/tests/test_progress.py
new file mode 100644
index 0000000..3595a74
--- /dev/null
+++ b/tests/test_progress.py
@@ -0,0 +1,29 @@
+from ipars import ProgressBarManager
+
+
+def test_full_cycle_does_not_raise():
+ bar = ProgressBarManager(max=3)
+ bar.next()
+ bar.next()
+ bar.next()
+ bar.finish()
+
+
+def test_single_iteration():
+ bar = ProgressBarManager(max=1)
+ bar.next()
+ bar.finish()
+
+
+def test_custom_message():
+ bar = ProgressBarManager(max=2, message='Загрузка')
+ bar.next()
+ bar.next()
+ bar.finish()
+
+
+def test_custom_fill_and_width():
+ bar = ProgressBarManager(max=2, fill='=', width=20)
+ bar.next()
+ bar.next()
+ bar.finish()
diff --git a/tests/test_timer.py b/tests/test_timer.py
new file mode 100644
index 0000000..ce5a602
--- /dev/null
+++ b/tests/test_timer.py
@@ -0,0 +1,69 @@
+import time
+import pytest
+from ipars import TimerManager
+
+
+def test_getWorkTime_seconds():
+ timer = TimerManager()
+ timer.start()
+ time.sleep(0.1)
+ timer.end()
+ result = timer.getWorkTime('seconds')
+ assert 0.05 < result < 0.5
+
+
+def test_getWorkTime_minutes():
+ timer = TimerManager()
+ timer.start()
+ time.sleep(0.1)
+ timer.end()
+ result = timer.getWorkTime('minutes')
+ assert result < 0.01
+
+
+def test_getWorkTime_hours():
+ timer = TimerManager()
+ timer.start()
+ time.sleep(0.1)
+ timer.end()
+ result = timer.getWorkTime('hours')
+ assert result < 0.001
+
+
+def test_getWorkTime_ndigits():
+ timer = TimerManager()
+ timer.start()
+ time.sleep(0.1)
+ timer.end()
+ result = timer.getWorkTime('seconds', ndigits=2)
+ assert isinstance(result, float)
+ assert len(str(result).split('.')[-1]) <= 2
+
+
+def test_getWorkTime_returns_float():
+ timer = TimerManager()
+ timer.start()
+ timer.end()
+ result = timer.getWorkTime()
+ assert isinstance(result, float)
+
+
+def test_raises_without_start():
+ timer = TimerManager()
+ with pytest.raises(ValueError, match='"start"'):
+ timer.getWorkTime()
+
+
+def test_raises_without_end():
+ timer = TimerManager()
+ timer.start()
+ with pytest.raises(ValueError, match='"end"'):
+ timer.getWorkTime()
+
+
+def test_raises_invalid_format():
+ timer = TimerManager()
+ timer.start()
+ timer.end()
+ with pytest.raises(ValueError, match='milliseconds'):
+ timer.getWorkTime('milliseconds')
diff --git a/tests/test_zip.py b/tests/test_zip.py
new file mode 100644
index 0000000..3433a63
--- /dev/null
+++ b/tests/test_zip.py
@@ -0,0 +1,87 @@
+import zipfile
+import pytest
+from ipars import ZipManager
+
+
+def test_zipfile_creates_archive(tmp_path):
+ zm = ZipManager()
+ source = tmp_path / 'hello.txt'
+ source.write_text('hello world')
+ zip_path = str(tmp_path / 'output.zip')
+
+ zm.zipFile(str(source), zip_path)
+
+ assert (tmp_path / 'output.zip').exists()
+ with zipfile.ZipFile(zip_path, 'r') as zf:
+ assert 'hello.txt' in zf.namelist()
+
+
+def test_zipfile_content_is_correct(tmp_path):
+ zm = ZipManager()
+ source = tmp_path / 'data.txt'
+ source.write_text('test content')
+ zip_path = str(tmp_path / 'output.zip')
+
+ zm.zipFile(str(source), zip_path)
+
+ with zipfile.ZipFile(zip_path, 'r') as zf:
+ content = zf.read('data.txt').decode('utf-8')
+ assert content == 'test content'
+
+
+def test_zipfolder_creates_archive(tmp_path):
+ zm = ZipManager()
+ folder = tmp_path / 'myfolder'
+ folder.mkdir()
+ (folder / 'a.txt').write_text('aaa')
+ (folder / 'b.txt').write_text('bbb')
+ zip_path = str(tmp_path / 'folder.zip')
+
+ zm.zipFolder(str(folder), zip_path)
+
+ assert (tmp_path / 'folder.zip').exists()
+ with zipfile.ZipFile(zip_path, 'r') as zf:
+ names = zf.namelist()
+ assert any('a.txt' in n for n in names)
+ assert any('b.txt' in n for n in names)
+
+
+def test_zipfolder_nested(tmp_path):
+ zm = ZipManager()
+ folder = tmp_path / 'root'
+ folder.mkdir()
+ sub = folder / 'sub'
+ sub.mkdir()
+ (sub / 'deep.txt').write_text('deep')
+ zip_path = str(tmp_path / 'nested.zip')
+
+ zm.zipFolder(str(folder), zip_path)
+
+ with zipfile.ZipFile(zip_path, 'r') as zf:
+ names = zf.namelist()
+ assert any('deep.txt' in n for n in names)
+
+
+def test_set_compression_none():
+ zm = ZipManager('none')
+ assert zm.compression == zipfile.ZIP_STORED
+
+
+def test_set_compression_normal():
+ zm = ZipManager('normal')
+ assert zm.compression == zipfile.ZIP_DEFLATED
+
+
+def test_set_compression_hard():
+ zm = ZipManager('hard')
+ assert zm.compression == zipfile.ZIP_BZIP2
+
+
+def test_set_compression_maximum():
+ zm = ZipManager('maximum')
+ assert zm.compression == zipfile.ZIP_LZMA
+
+
+def test_invalid_compression_raises():
+ with pytest.raises(ValueError):
+ ZipManager('ultra')