EGYADMIN commited on
Commit
8c3326a
·
verified ·
1 Parent(s): e05a890

Delete tests

Browse files
tests/README.md DELETED
@@ -1,130 +0,0 @@
1
- # اختبارات نظام تسعير المناقصات
2
-
3
- يحتوي هذا المجلد على اختبارات وحدة واختبارات تكامل لنظام تسعير المناقصات.
4
-
5
- ## هيكل المجلد
6
-
7
- - `unit/`: اختبارات الوحدة للمكونات الفردية
8
- - `integration/`: اختبارات التكامل بين مكونات النظام المختلفة
9
-
10
- ## تشغيل الاختبارات
11
-
12
- ### تشغيل جميع الاختبارات
13
-
14
- لتشغيل جميع الاختبارات، قم بتنفيذ الأمر التالي من المجلد الرئيسي للمشروع:
15
-
16
- ```bash
17
- python -m unittest discover -s tests
18
- ```
19
-
20
- ### تشغيل اختبارات الوحدة فقط
21
-
22
- ```bash
23
- python -m unittest discover -s tests/unit
24
- ```
25
-
26
- ### تشغيل اختبارات التكامل فقط
27
-
28
- ```bash
29
- python -m unittest discover -s tests/integration
30
- ```
31
-
32
- ### تشغيل ملف اختبار محدد
33
-
34
- ```bash
35
- python -m unittest tests/unit/test_reports.py
36
- ```
37
-
38
- ## كتابة اختبارات جديدة
39
-
40
- ### اختبارات الوحدة
41
-
42
- اختبارات الوحدة تركز على اختبار مكون واحد معزول عن باقي النظام. يجب اتباع النمط التالي:
43
-
44
- ```python
45
- import unittest
46
-
47
- class TestComponentName(unittest.TestCase):
48
-
49
- def setUp(self):
50
- # إعداد بيئة الاختبار
51
- pass
52
-
53
- def tearDown(self):
54
- # تنظيف بيئة الاختبار
55
- pass
56
-
57
- def test_functionality_name(self):
58
- # اختبار وظيفة معينة
59
- result = function_to_test(params)
60
- self.assertEqual(result, expected_value)
61
- ```
62
-
63
- ### اختبارات التكامل
64
-
65
- اختبارات التكامل تركز على التفاعل بين مكونين أو أكثر. يجب اتباع النمط التالي:
66
-
67
- ```python
68
- import unittest
69
-
70
- class TestIntegrationName(unittest.TestCase):
71
-
72
- def setUp(self):
73
- # إعداد بيئة الاختبار
74
- # إنشاء المكونات المختلفة المطلوبة
75
- pass
76
-
77
- def tearDown(self):
78
- # تنظيف بيئة الاختبار
79
- pass
80
-
81
- def test_integration_scenario(self):
82
- # اختبار سيناريو تكامل محدد
83
- result = component1.action(params)
84
- component2.process(result)
85
- final_result = component2.get_result()
86
- self.assertEqual(final_result, expected_value)
87
- ```
88
-
89
- ## المحاكاة والاستبدال
90
-
91
- في بعض الحالات، قد نحتاج إلى محاكاة بعض المكونات لعزل المكون الذي نختبره. يمكن استخدام مكتبة `unittest.mock` لهذا الغرض:
92
-
93
- ```python
94
- from unittest.mock import Mock, patch
95
-
96
- class TestWithMocking(unittest.TestCase):
97
-
98
- @patch('module.ClassName')
99
- def test_with_mock(self, MockClass):
100
- # إعداد المحاكاة
101
- instance = MockClass.return_value
102
- instance.method.return_value = 'mocked_result'
103
-
104
- # استدعاء الدالة التي تستخدم الكائن المحاكى
105
- result = function_to_test()
106
-
107
- # التحقق من النتائج ومن استدعاء المحاكاة
108
- self.assertEqual(result, expected_value)
109
- instance.method.assert_called_once_with(expected_params)
110
- ```
111
-
112
- ## التغطية
113
-
114
- يمكن قياس تغطية الاختبارات باستخدام أداة `coverage`:
115
-
116
- ```bash
117
- # تثبيت أداة coverage
118
- pip install coverage
119
-
120
- # تشغيل الاختبارات مع قياس التغطية
121
- coverage run -m unittest discover -s tests
122
-
123
- # عرض تقرير التغطية
124
- coverage report
125
-
126
- # إنشاء تقرير HTML للتغطية
127
- coverage html
128
- ```
129
-
130
- ملف تقرير HTML سيكون في مجلد `htmlcov/index.html`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/integration/test_dashboard.py DELETED
@@ -1,266 +0,0 @@
1
- """
2
- اختبارات تكامل للوحة المعلومات
3
- """
4
-
5
- import unittest
6
- import pandas as pd
7
- import numpy as np
8
- import os
9
- import sys
10
- import json
11
- from datetime import datetime, timedelta
12
-
13
- # إضافة المسار الرئيسي للمشروع لاستيراد الوحدات
14
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
15
-
16
- from modules.reports.reports_app import ReportsApp
17
- import streamlit as st
18
-
19
-
20
- class TestDashboard(unittest.TestCase):
21
- """اختبارات تكامل للوحة المعلومات"""
22
-
23
- def setUp(self):
24
- """إعداد بيئة الاختبار"""
25
- self.reports_app = ReportsApp()
26
-
27
- # إنشاء بيانات محاكاة للمشاريع
28
- self.mock_projects = [
29
- {
30
- 'id': 1,
31
- 'name': 'مشروع 1',
32
- 'number': 'T-2024001',
33
- 'client': 'وزارة الصحة',
34
- 'location': 'الرياض',
35
- 'status': 'جديد',
36
- 'submission_date': '2024-01-15',
37
- 'tender_type': 'عامة',
38
- 'created_at': '2024-01-10',
39
- 'value': 1500000,
40
- 'local_content': 75
41
- },
42
- {
43
- 'id': 2,
44
- 'name': 'مشروع 2',
45
- 'number': 'T-2024002',
46
- 'client': 'وزارة التعليم',
47
- 'location': 'جدة',
48
- 'status': 'قيد التسعير',
49
- 'submission_date': '2024-01-28',
50
- 'tender_type': 'خاصة',
51
- 'created_at': '2024-01-20',
52
- 'value': 4500000,
53
- 'local_content': 80
54
- },
55
- {
56
- 'id': 3,
57
- 'name': 'مشروع 3',
58
- 'number': 'T-2024003',
59
- 'client': 'وزارة الصحة',
60
- 'location': 'الدمام',
61
- 'status': 'تم التقديم',
62
- 'submission_date': '2024-02-10',
63
- 'tender_type': 'عامة',
64
- 'created_at': '2024-02-01',
65
- 'value': 8000000,
66
- 'local_content': 70
67
- },
68
- {
69
- 'id': 4,
70
- 'name': 'مشروع 4',
71
- 'number': 'T-2024004',
72
- 'client': 'شركة أرامكو',
73
- 'location': 'الظهران',
74
- 'status': 'تمت الترسية',
75
- 'submission_date': '2024-02-15',
76
- 'tender_type': 'عامة',
77
- 'created_at': '2024-02-05',
78
- 'value': 15000000,
79
- 'local_content': 85
80
- },
81
- {
82
- 'id': 5,
83
- 'name': 'مشروع 5',
84
- 'number': 'T-2024005',
85
- 'client': 'شركة سابك',
86
- 'location': 'الجبيل',
87
- 'status': 'قيد التنفيذ',
88
- 'submission_date': '2024-02-20',
89
- 'tender_type': 'خاصة',
90
- 'created_at': '2024-02-10',
91
- 'value': 25000000,
92
- 'local_content': 75
93
- },
94
- {
95
- 'id': 6,
96
- 'name': 'مشروع 6',
97
- 'number': 'T-2024006',
98
- 'client': 'وزارة النقل',
99
- 'location': 'الرياض',
100
- 'status': 'منتهي',
101
- 'submission_date': '2024-01-05',
102
- 'tender_type': 'عامة',
103
- 'created_at': '2023-12-20',
104
- 'value': 5000000,
105
- 'local_content': 72
106
- },
107
- {
108
- 'id': 7,
109
- 'name': 'مشروع 7',
110
- 'number': 'T-2024007',
111
- 'client': 'وزارة الإسكان',
112
- 'location': 'مكة',
113
- 'status': 'ملغي',
114
- 'submission_date': '2024-01-10',
115
- 'tender_type': 'خاصة',
116
- 'created_at': '2023-12-25',
117
- 'value': 12000000,
118
- 'local_content': 65
119
- }
120
- ]
121
-
122
- # تعيين المشاريع في حالة الجلسة
123
- st.session_state.projects = self.mock_projects
124
-
125
- def test_dashboard_metrics(self):
126
- """اختبار مؤشرات لوحة المعلومات الرئيسية"""
127
- # الحصول على المؤشرات
128
- total_projects = self.reports_app._get_total_projects()
129
- active_projects = self.reports_app._get_active_projects()
130
- won_projects = self.reports_app._get_won_projects()
131
- avg_local_content = self.reports_app._get_avg_local_content()
132
-
133
- # التحقق من المؤشرات
134
- self.assertEqual(total_projects, 7)
135
- self.assertEqual(active_projects, 4) # قيد التسعير، تم التقديم، تمت الترسية، قيد التنفيذ
136
- self.assertEqual(won_projects, 3) # تمت الترسية، قيد التنفيذ، منتهي
137
-
138
- # التحقق من أن متوسط المحتوى المحلي قريب من القيمة المتوقعة
139
- expected_avg_local_content = sum(p['local_content'] for p in self.mock_projects) / len(self.mock_projects)
140
- self.assertAlmostEqual(avg_local_content, expected_avg_local_content, delta=1.0)
141
-
142
- def test_project_status_distribution(self):
143
- """اختبار توزيع المشاريع حسب الحالة"""
144
- # الحصول على بيانات التوزيع
145
- status_data = self.reports_app._get_project_status_data()
146
-
147
- # التحقق من أن البيانات إطار بيانات
148
- self.assertIsInstance(status_data, pd.DataFrame)
149
-
150
- # التحقق من الأعمدة
151
- self.assertIn('status', status_data.columns)
152
- self.assertIn('count', status_data.columns)
153
-
154
- # التحقق من أن عدد الحالات الفريدة يساوي 7
155
- self.assertEqual(len(status_data), 7)
156
-
157
- # التحقق من أن مجموع المشاريع صحيح
158
- self.assertEqual(status_data['count'].sum(), 7)
159
-
160
- # التحقق من عدد المشاريع في كل حالة
161
- status_counts = status_data.set_index('status')['count'].to_dict()
162
- self.assertEqual(status_counts['جديد'], 1)
163
- self.assertEqual(status_counts['قيد التسعير'], 1)
164
- self.assertEqual(status_counts['تم التقديم'], 1)
165
- self.assertEqual(status_counts['تمت الترسية'], 1)
166
- self.assertEqual(status_counts['قيد التنفيذ'], 1)
167
- self.assertEqual(status_counts['منتهي'], 1)
168
- self.assertEqual(status_counts['ملغي'], 1)
169
-
170
- def test_monthly_trend_data(self):
171
- """اختبار بيانات الاتجاه الشهري"""
172
- # الحصول على بيانات الاتجاه
173
- monthly_data = self.reports_app._get_monthly_project_data()
174
-
175
- # التحقق من أن البيانات إطار بيانات
176
- self.assertIsInstance(monthly_data, pd.DataFrame)
177
-
178
- # التحقق من الأعمدة
179
- self.assertIn('month', monthly_data.columns)
180
- self.assertIn('new', monthly_data.columns)
181
- self.assertIn('submitted', monthly_data.columns)
182
- self.assertIn('won', monthly_data.columns)
183
-
184
- # التحقق من أن عدد الأشهر يساوي 6
185
- self.assertEqual(len(monthly_data), 6)
186
-
187
- def test_project_value_distribution(self):
188
- """اختبار توزيع المشاريع حسب القيمة"""
189
- # الحصول على بيانات التوزيع
190
- value_data = self.reports_app._get_project_value_data()
191
-
192
- # التحقق من أن البيانات إطار بيانات
193
- self.assertIsInstance(value_data, pd.DataFrame)
194
-
195
- # التحقق من الأعمدة
196
- self.assertIn('range', value_data.columns)
197
- self.assertIn('count', value_data.columns)
198
-
199
- # التحقق من أن عدد النطاقات يساوي 7
200
- self.assertEqual(len(value_data), 7)
201
-
202
- # التحقق من أن عدد المشاريع صحيح
203
- self.assertEqual(value_data['count'].sum(), 7)
204
-
205
- # التحقق من نطاقات القيمة
206
- value_ranges = value_data['range'].tolist()
207
- self.assertIn('أقل من 1 مليون', value_ranges)
208
- self.assertIn('1-5 مليون', value_ranges)
209
- self.assertIn('5-10 مليون', value_ranges)
210
- self.assertIn('10-20 مليون', value_ranges)
211
- self.assertIn('20-50 مليون', value_ranges)
212
-
213
- def test_custom_report_generation(self):
214
- """اختبار إنشاء التقارير المخصصة"""
215
- # اختبار إنشاء تقرير جدول
216
- table_data = self.reports_app._generate_sample_data(
217
- data_source="المشاريع",
218
- fields=["اسم المشروع", "العميل", "الحالة", "تاريخ التقديم"],
219
- rows=10
220
- )
221
-
222
- # التحقق من النتائج
223
- self.assertIsInstance(table_data, pd.DataFrame)
224
- self.assertEqual(len(table_data), 10)
225
- self.assertEqual(len(table_data.columns), 4)
226
-
227
- # اختبار فلترة البيانات
228
- filters = [
229
- {
230
- 'field': 'العميل',
231
- 'operator': 'يساوي',
232
- 'value': 'وزارة الصحة'
233
- }
234
- ]
235
-
236
- filtered_data = table_data.copy()
237
- for filter_info in filters:
238
- field = filter_info['field']
239
- operator = filter_info['operator']
240
- value = filter_info['value']
241
-
242
- if field in filtered_data.columns:
243
- if operator == "يساوي":
244
- filtered_data = filtered_data[filtered_data[field] == value]
245
-
246
- # التحقق من أن الفلترة تعمل بشكل صحيح
247
- if not filtered_data.empty:
248
- self.assertTrue(all(client == 'وزارة الصحة' for client in filtered_data['العميل']))
249
-
250
- def test_report_export_functionality(self):
251
- """اختبار وظيفة تصدير التقارير"""
252
- # في هذا الاختبار نفترض وجود وظيفة تصدير تعمل بشكل صحيح
253
- # ونتحقق فقط من أن البيانات جاهزة للتصدير
254
-
255
- # الحصول على بيانات المشاريع
256
- project_data = pd.DataFrame(self.mock_projects)
257
-
258
- # التحقق من أن البيانات جاهزة للتصدير
259
- self.assertTrue(len(project_data) > 0)
260
- self.assertIn('name', project_data.columns)
261
- self.assertIn('client', project_data.columns)
262
- self.assertIn('status', project_data.columns)
263
-
264
-
265
- if __name__ == '__main__':
266
- unittest.main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/integration/test_report_export.py DELETED
@@ -1,246 +0,0 @@
1
- """
2
- اختبارات تكامل لتصدير التقارير
3
- """
4
-
5
- import unittest
6
- import pandas as pd
7
- import os
8
- import sys
9
- import tempfile
10
- import shutil
11
- from datetime import datetime
12
-
13
- # إضافة المسار الرئيسي للمشروع لاستيراد الوحدات
14
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
15
-
16
- from modules.reports.reports_app import ReportsApp
17
- from utils.excel_handler import export_to_excel
18
-
19
-
20
- class TestReportExport(unittest.TestCase):
21
- """اختبارات تكامل لتصدير التقارير"""
22
-
23
- def setUp(self):
24
- """إعداد بيئة الاختبار"""
25
- self.reports_app = ReportsApp()
26
-
27
- # إنشاء مجلد مؤقت للملفات المصدرة
28
- self.test_dir = tempfile.mkdtemp()
29
-
30
- # إنشاء بيانات محاكاة للمشاريع
31
- self.projects_data = pd.DataFrame({
32
- 'اسم المشروع': [f'مشروع {i}' for i in range(1, 6)],
33
- 'رقم المناقصة': [f'T-2024{i:03d}' for i in range(1, 6)],
34
- 'العميل': ['وزارة الصحة', 'وزارة التعليم', 'وزارة الصحة', 'شركة أرامكو', 'شركة سابك'],
35
- 'الموقع': ['الرياض', 'جدة', 'الدمام', 'الظهران', 'الجبيل'],
36
- 'الحالة': ['جديد', 'قيد التسعير', 'تم التقديم', 'تمت الترسية', 'قيد التنفيذ'],
37
- 'تاريخ التقديم': ['2024-01-15', '2024-01-28', '2024-02-10', '2024-02-15', '2024-02-20'],
38
- 'نوع المناقصة': ['عامة', 'خاصة', 'عامة', 'عامة', 'خاصة']
39
- })
40
-
41
- # إنشاء بيانات محاكاة للتسعير
42
- self.pricing_data = pd.DataFrame({
43
- 'اسم المشروع': ['مشروع 1', 'مشروع 1', 'مشروع 1', 'مشروع 2', 'مشروع 2'],
44
- 'رقم البند': ['A001', 'A002', 'A003', 'B001', 'B002'],
45
- 'وصف البند': [
46
- 'أعمال الحفر والردم',
47
- 'أعمال الخرسانة المسلحة',
48
- 'أعمال حديد التسليح',
49
- 'أعمال البلوك',
50
- 'أعمال اللياسة'
51
- ],
52
- 'الوحدة': ['م3', 'م3', 'طن', 'م2', 'م2'],
53
- 'الكمية': [1000, 500, 50, 800, 1200],
54
- 'سعر الوحدة': [50, 1200, 5000, 120, 80],
55
- 'الإجمالي': [50000, 600000, 250000, 96000, 96000]
56
- })
57
-
58
- # إنشاء بيانات محاكاة للمخاطر
59
- self.risks_data = pd.DataFrame({
60
- 'رمز المخاطرة': [f'R{i:03d}' for i in range(1, 6)],
61
- 'اسم المشروع': ['مشروع 1', 'مشروع 1', 'مشروع 2', 'مشروع 3', 'مشروع 4'],
62
- 'وصف المخاطرة': [
63
- 'غرامة تأخير مرتفعة',
64
- 'مدة تنفيذ قصيرة',
65
- 'متطلبات ضمان بنكي مرتفعة',
66
- 'شروط دفع متأخرة',
67
- 'متطلبات تأمين شاملة ومكلفة'
68
- ],
69
- 'الفئة': [
70
- 'مخاطر مالية',
71
- 'مخاطر زمنية',
72
- 'مخاطر مالية',
73
- 'مخاطر مالية',
74
- 'مخاطر مالية'
75
- ],
76
- 'التأثير': ['عالي', 'متوسط', 'منخفض', 'عالي', 'متوسط'],
77
- 'الاحتمالية': ['محتمل', 'مؤكد', 'غير محتمل', 'محتمل', 'غير محتمل'],
78
- 'درجة المخاطرة': [6, 6, 2, 8, 4]
79
- })
80
-
81
- def tearDown(self):
82
- """تنظيف بيئة الاختبار"""
83
- # حذف المجلد المؤقت
84
- shutil.rmtree(self.test_dir)
85
-
86
- def test_export_projects_report_to_excel(self):
87
- """اختبار تصدير تقرير المشاريع إلى Excel"""
88
- # إنشاء مسار الملف المصدر
89
- output_file = os.path.join(self.test_dir, 'projects_report.xlsx')
90
-
91
- # تصدير البيانات
92
- result = export_to_excel(self.projects_data, output_file, sheet_name='المشاريع', title='تقرير المشاريع')
93
-
94
- # التحقق من نجاح التصدير
95
- self.assertTrue(result)
96
- self.assertTrue(os.path.exists(output_file))
97
-
98
- # التحقق من محتوى الملف المصدر
99
- imported_data = pd.read_excel(output_file, sheet_name='المشاريع', skiprows=2) # تخطي الصفوف الأولى (العنوان)
100
-
101
- # التحقق من عدد الصفوف والأعمدة
102
- self.assertEqual(len(imported_data), len(self.projects_data))
103
- self.assertEqual(len(imported_data.columns), len(self.projects_data.columns))
104
-
105
- def test_export_pricing_report_to_excel(self):
106
- """اختبار تصدير تقرير التسعير إلى Excel"""
107
- # إنشاء مسار الملف المصدر
108
- output_file = os.path.join(self.test_dir, 'pricing_report.xlsx')
109
-
110
- # تصدير البيانات
111
- result = export_to_excel(self.pricing_data, output_file, sheet_name='التسعير', title='تقرير التسعير')
112
-
113
- # التحقق من نجاح التصدير
114
- self.assertTrue(result)
115
- self.assertTrue(os.path.exists(output_file))
116
-
117
- # التحقق من محتوى الملف المصدر
118
- imported_data = pd.read_excel(output_file, sheet_name='التسعير', skiprows=2) # تخطي الصفوف الأولى (العنوان)
119
-
120
- # التحقق من عدد الصفوف والأعمدة
121
- self.assertEqual(len(imported_data), len(self.pricing_data))
122
- self.assertEqual(len(imported_data.columns), len(self.pricing_data.columns))
123
-
124
- def test_export_multiple_sheets(self):
125
- """اختبار تصدير تقرير متعدد الصفحات"""
126
- # إنشاء مسار الملف المصدر
127
- output_file = os.path.join(self.test_dir, 'combined_report.xlsx')
128
-
129
- # تصدير البيانات متعددة الصفحات
130
- result1 = export_to_excel(self.projects_data, output_file, sheet_name='المشاريع', title='تقرير المشاريع')
131
- result2 = export_to_excel(self.pricing_data, output_file, sheet_name='التسعير', title='تقرير التسعير', append=True)
132
- result3 = export_to_excel(self.risks_data, output_file, sheet_name='المخاطر', title='تقرير المخاطر', append=True)
133
-
134
- # التحقق من نجاح التصدير
135
- self.assertTrue(result1)
136
- self.assertTrue(result2)
137
- self.assertTrue(result3)
138
- self.assertTrue(os.path.exists(output_file))
139
-
140
- # التحقق من وجود جميع الصفحات
141
- excel_file = pd.ExcelFile(output_file)
142
- self.assertIn('المشاريع', excel_file.sheet_names)
143
- self.assertIn('التسعير', excel_file.sheet_names)
144
- self.assertIn('المخاطر', excel_file.sheet_names)
145
-
146
- # التحقق من محتوى كل صفحة
147
- projects_data = pd.read_excel(output_file, sheet_name='المشاريع', skiprows=2)
148
- pricing_data = pd.read_excel(output_file, sheet_name='التسعير', skiprows=2)
149
- risks_data = pd.read_excel(output_file, sheet_name='المخاطر', skiprows=2)
150
-
151
- self.assertEqual(len(projects_data), len(self.projects_data))
152
- self.assertEqual(len(pricing_data), len(self.pricing_data))
153
- self.assertEqual(len(risks_data), len(self.risks_data))
154
-
155
- def test_export_with_formatting(self):
156
- """اختبار تصدير تقرير مع التنسيق"""
157
- # إنشاء مسار الملف المصدر
158
- output_file = os.path.join(self.test_dir, 'formatted_report.xlsx')
159
-
160
- # تصدير البيانات مع تنسيق
161
- formatting = {
162
- 'header_format': {
163
- 'bold': True,
164
- 'bg_color': '#4F81BD',
165
- 'font_color': 'white',
166
- 'border': 1,
167
- 'align': 'center'
168
- },
169
- 'row_formats': [
170
- {
171
- 'columns': ['الإجمالي'],
172
- 'num_format': '#,##0.00 ريال'
173
- },
174
- {
175
- 'columns': ['الكمية'],
176
- 'num_format': '#,##0.00'
177
- }
178
- ],
179
- 'conditional_formats': [
180
- {
181
- 'column': 'درجة المخاطرة',
182
- 'criteria': '>',
183
- 'value': 6,
184
- 'format': {'bg_color': '#FF9999'}
185
- }
186
- ]
187
- }
188
-
189
- result = export_to_excel(self.risks_data, output_file, sheet_name='المخاطر',
190
- title='تقرير المخاطر', formatting=formatting)
191
-
192
- # التحقق من نجاح التصدير
193
- self.assertTrue(result)
194
- self.assertTrue(os.path.exists(output_file))
195
-
196
- def test_export_with_filters(self):
197
- """اختبار تصدير تقرير مع تصفية"""
198
- # إنشاء مسار الملف المصدر
199
- output_file = os.path.join(self.test_dir, 'filtered_report.xlsx')
200
-
201
- # تصدير البيانات المشاريع المفلترة (فقط مشاريع وزارة الصحة)
202
- filtered_data = self.projects_data[self.projects_data['العميل'] == 'وزارة الصحة']
203
- result = export_to_excel(filtered_data, output_file, sheet_name='مشاريع الصحة',
204
- title='تقرير مشاريع وزارة الصحة')
205
-
206
- # التحقق من نجاح التصدير
207
- self.assertTrue(result)
208
- self.assertTrue(os.path.exists(output_file))
209
-
210
- # التحقق من محتوى الملف المصدر
211
- imported_data = pd.read_excel(output_file, sheet_name='مشاريع الصحة', skiprows=2)
212
-
213
- # التحقق من أن جميع المشاريع تنتمي لوزارة الصحة
214
- self.assertEqual(len(imported_data), 2)
215
- self.assertTrue(all(client == 'وزارة الصحة' for client in imported_data['العميل']))
216
-
217
- def test_export_with_summary(self):
218
- """اختبار تصدير تقرير مع ملخص"""
219
- # إنشاء مسار الملف المصدر
220
- output_file = os.path.join(self.test_dir, 'summary_report.xlsx')
221
-
222
- # إنشاء بيانات الملخص
223
- summary_data = pd.DataFrame({
224
- 'المؤشر': ['إجمالي المشاريع', 'المشاريع النشطة', 'المشاريع المرساة', 'نسبة النجاح'],
225
- 'القيمة': [5, 4, 2, '40%']
226
- })
227
-
228
- # تصدير البيانات مع ملخص
229
- result1 = export_to_excel(summary_data, output_file, sheet_name='الملخص',
230
- title='ملخص تقرير المشاريع')
231
- result2 = export_to_excel(self.projects_data, output_file, sheet_name='البيانات',
232
- title='تفاصيل المشاريع', append=True)
233
-
234
- # التحقق من نجاح التصدير
235
- self.assertTrue(result1)
236
- self.assertTrue(result2)
237
- self.assertTrue(os.path.exists(output_file))
238
-
239
- # التحقق من وجود صفحات الملخص والبيانات
240
- excel_file = pd.ExcelFile(output_file)
241
- self.assertIn('الملخص', excel_file.sheet_names)
242
- self.assertIn('البيانات', excel_file.sheet_names)
243
-
244
-
245
- if __name__ == '__main__':
246
- unittest.main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_app.py DELETED
@@ -1,601 +0,0 @@
1
- """
2
- وحدة اختبار التطبيق لنظام إدارة المناقصات - Hybrid Face
3
- """
4
-
5
- import os
6
- import sys
7
- import logging
8
- import unittest
9
- import tkinter as tk
10
- from pathlib import Path
11
-
12
- # تهيئة السجل
13
- logging.basicConfig(
14
- level=logging.INFO,
15
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
16
- )
17
- logger = logging.getLogger('test_app')
18
-
19
- # إضافة المسار الرئيسي للتطبيق إلى مسار البحث
20
- sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
21
-
22
- # استيراد الوحدات المطلوبة للاختبار
23
- from database.db_connector import DatabaseConnector
24
- from database.models import User, Project, Document, ProjectItem, Resource, Risk, Report, SystemLog
25
- from modules.document_analysis.analyzer import DocumentAnalyzer
26
- from modules.pricing.pricing_engine import PricingEngine
27
- from modules.risk_analysis.risk_analyzer import RiskAnalyzer
28
- from modules.ai_assistant.assistant import AIAssistant
29
- from styling.theme import AppTheme
30
- from styling.icons import IconGenerator
31
- from styling.charts import ChartGenerator
32
- from config import AppConfig
33
-
34
- class TestDatabaseConnector(unittest.TestCase):
35
- """اختبار وحدة اتصال قاعدة البيانات"""
36
-
37
- def setUp(self):
38
- """إعداد بيئة الاختبار"""
39
- # استخدام قاعدة بيانات اختبار مؤقتة
40
- self.config = AppConfig()
41
- self.config.DB_NAME = "test_tender_system.db"
42
- self.db = DatabaseConnector(self.config)
43
-
44
- def tearDown(self):
45
- """تنظيف بيئة الاختبار"""
46
- self.db.disconnect()
47
- # حذف قاعدة البيانات المؤقتة
48
- if os.path.exists(self.config.DB_NAME):
49
- os.remove(self.config.DB_NAME)
50
-
51
- def test_connection(self):
52
- """اختبار الاتصال بقاعدة البيانات"""
53
- self.assertTrue(self.db.is_connected)
54
-
55
- def test_execute_query(self):
56
- """اختبار تنفيذ استعلام"""
57
- cursor = self.db.execute("SELECT 1")
58
- self.assertIsNotNone(cursor)
59
- result = cursor.fetchone()
60
- self.assertEqual(result[0], 1)
61
-
62
- def test_insert_and_fetch(self):
63
- """اختبار إدراج واسترجاع البيانات"""
64
- # إدراج بيانات
65
- user_data = {
66
- "username": "test_user",
67
- "password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", # admin
68
- "full_name": "مستخدم اختبار",
69
- "email": "[email protected]",
70
- "role": "user"
71
- }
72
- user_id = self.db.insert("users", user_data)
73
- self.assertIsNotNone(user_id)
74
-
75
- # استرجاع البيانات
76
- user = self.db.fetch_one("SELECT * FROM users WHERE id = ?", (user_id,))
77
- self.assertIsNotNone(user)
78
- self.assertEqual(user["username"], "test_user")
79
- self.assertEqual(user["full_name"], "مستخدم اختبار")
80
-
81
- def test_update(self):
82
- """اختبار تحديث البيانات"""
83
- # إدراج بيانات
84
- user_data = {
85
- "username": "update_user",
86
- "password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", # admin
87
- "full_name": "مستخدم للتحديث",
88
- "email": "[email protected]",
89
- "role": "user"
90
- }
91
- user_id = self.db.insert("users", user_data)
92
-
93
- # تحديث البيانات
94
- updated_data = {
95
- "full_name": "مستخدم تم تحديثه",
96
- "role": "admin"
97
- }
98
- rows_affected = self.db.update("users", updated_data, "id = ?", (user_id,))
99
- self.assertEqual(rows_affected, 1)
100
-
101
- # التحقق من التحديث
102
- user = self.db.fetch_one("SELECT * FROM users WHERE id = ?", (user_id,))
103
- self.assertEqual(user["full_name"], "مستخدم تم تحديثه")
104
- self.assertEqual(user["role"], "admin")
105
-
106
- def test_delete(self):
107
- """اختبار حذف البيانات"""
108
- # إدراج بيانات
109
- user_data = {
110
- "username": "delete_user",
111
- "password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", # admin
112
- "full_name": "مستخدم للحذف",
113
- "email": "[email protected]",
114
- "role": "user"
115
- }
116
- user_id = self.db.insert("users", user_data)
117
-
118
- # حذف البيانات
119
- rows_affected = self.db.delete("users", "id = ?", (user_id,))
120
- self.assertEqual(rows_affected, 1)
121
-
122
- # التحقق من الحذف
123
- user = self.db.fetch_one("SELECT * FROM users WHERE id = ?", (user_id,))
124
- self.assertIsNone(user)
125
-
126
-
127
- class TestModels(unittest.TestCase):
128
- """اختبار نماذج البيانات"""
129
-
130
- def setUp(self):
131
- """��عداد بيئة الاختبار"""
132
- # استخدام قاعدة بيانات اختبار مؤقتة
133
- self.config = AppConfig()
134
- self.config.DB_NAME = "test_models.db"
135
- self.db = DatabaseConnector(self.config)
136
-
137
- def tearDown(self):
138
- """تنظيف بيئة الاختبار"""
139
- self.db.disconnect()
140
- # حذف قاعدة البيانات المؤقتة
141
- if os.path.exists(self.config.DB_NAME):
142
- os.remove(self.config.DB_NAME)
143
-
144
- def test_user_model(self):
145
- """اختبار نموذج المستخدم"""
146
- # إنشاء مستخدم جديد
147
- user = User(self.db)
148
- user.data = {
149
- "username": "model_user",
150
- "password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", # admin
151
- "full_name": "مستخدم نموذج",
152
- "email": "[email protected]",
153
- "role": "user",
154
- "is_active": 1
155
- }
156
-
157
- # حفظ المستخدم
158
- self.assertTrue(user.save())
159
- self.assertIsNotNone(user.data.get("id"))
160
-
161
- # استرجاع المستخدم
162
- retrieved_user = User.get_by_id(user.data["id"], self.db)
163
- self.assertIsNotNone(retrieved_user)
164
- self.assertEqual(retrieved_user.data["username"], "model_user")
165
-
166
- # مصادقة المستخدم
167
- authenticated_user = User.authenticate("model_user", "admin", self.db)
168
- self.assertIsNotNone(authenticated_user)
169
- self.assertEqual(authenticated_user.data["username"], "model_user")
170
-
171
- # تعيين كلمة مرور جديدة
172
- user.set_password("newpassword")
173
- user.save()
174
-
175
- # مصادقة المستخدم بكلمة المرور الجديدة
176
- authenticated_user = User.authenticate("model_user", "newpassword", self.db)
177
- self.assertIsNotNone(authenticated_user)
178
-
179
- # حذف المستخدم
180
- self.assertTrue(user.delete())
181
-
182
- def test_project_model(self):
183
- """اختبار نموذج المشروع"""
184
- # إنشاء مستخدم للمشروع
185
- user = User(self.db)
186
- user.data = {
187
- "username": "project_user",
188
- "password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", # admin
189
- "full_name": "مستخدم المشروع",
190
- "email": "[email protected]",
191
- "role": "user",
192
- "is_active": 1
193
- }
194
- user.save()
195
-
196
- # إنشاء مشروع جديد
197
- project = Project(self.db)
198
- project.data = {
199
- "name": "مشروع اختبار",
200
- "client": "عميل اختبار",
201
- "description": "وصف مشروع الاختبار",
202
- "start_date": "2025-01-01",
203
- "end_date": "2025-12-31",
204
- "status": "تخطيط",
205
- "created_by": user.data["id"]
206
- }
207
-
208
- # حفظ المشروع
209
- self.assertTrue(project.save())
210
- self.assertIsNotNone(project.data.get("id"))
211
-
212
- # استرجاع المشروع
213
- retrieved_project = Project.get_by_id(project.data["id"], self.db)
214
- self.assertIsNotNone(retrieved_project)
215
- self.assertEqual(retrieved_project.data["name"], "مشروع اختبار")
216
-
217
- # إضافة بند للمشروع
218
- item = ProjectItem(self.db)
219
- item.data = {
220
- "project_id": project.data["id"],
221
- "name": "بند اختبار",
222
- "description": "وصف بند الاختبار",
223
- "unit": "م²",
224
- "quantity": 100,
225
- "unit_price": 500,
226
- "total_price": 50000
227
- }
228
- item.save()
229
-
230
- # حساب التكلفة الإجمالية للمشروع
231
- total_cost = project.calculate_total_cost()
232
- self.assertEqual(total_cost, 50000)
233
-
234
- # حذف المشروع
235
- self.assertTrue(project.delete())
236
-
237
- # حذف المستخدم
238
- self.assertTrue(user.delete())
239
-
240
-
241
- class TestDocumentAnalyzer(unittest.TestCase):
242
- """اختبار محلل المستندات"""
243
-
244
- def setUp(self):
245
- """إعداد بيئة الاختبار"""
246
- self.config = AppConfig()
247
- self.analyzer = DocumentAnalyzer(self.config)
248
-
249
- # إنشاء مجلد المستندات للاختبار
250
- self.test_docs_dir = Path("test_documents")
251
- self.test_docs_dir.mkdir(exist_ok=True)
252
-
253
- # إنشاء ملف مستند اختبار
254
- self.test_doc_path = self.test_docs_dir / "test_document.txt"
255
- with open(self.test_doc_path, "w", encoding="utf-8") as f:
256
- f.write("هذا مستند اختبار لمحلل المستندات")
257
-
258
- def tearDown(self):
259
- """تنظيف بيئة الاختبار"""
260
- # حذف ملف المستند
261
- if self.test_doc_path.exists():
262
- self.test_doc_path.unlink()
263
-
264
- # حذف مجلد المستندات
265
- if self.test_docs_dir.exists():
266
- self.test_docs_dir.rmdir()
267
-
268
- def test_analyze_document(self):
269
- """اختبار تحليل المستند"""
270
- # تحليل المستند
271
- result = self.analyzer.analyze_document(str(self.test_doc_path), "tender")
272
- self.assertTrue(result)
273
-
274
- # انتظار اكتمال التحليل
275
- import time
276
- max_wait = 5 # ثوانٍ
277
- waited = 0
278
- while self.analyzer.analysis_in_progress and waited < max_wait:
279
- time.sleep(0.5)
280
- waited += 0.5
281
-
282
- # التحقق من نتائج التحليل
283
- self.assertFalse(self.analyzer.analysis_in_progress)
284
- results = self.analyzer.get_analysis_results()
285
- self.assertEqual(results["status"], "اكتمل التحليل")
286
- self.assertEqual(results["document_path"], str(self.test_doc_path))
287
-
288
- def test_export_analysis_results(self):
289
- """اختبار تصدير نتائج التحليل"""
290
- # تحليل المستند
291
- self.analyzer.analyze_document(str(self.test_doc_path), "tender")
292
-
293
- # انتظار اكتمال التحليل
294
- import time
295
- max_wait = 5 # ثوانٍ
296
- waited = 0
297
- while self.analyzer.analysis_in_progress and waited < max_wait:
298
- time.sleep(0.5)
299
- waited += 0.5
300
-
301
- # تصدير النتائج
302
- export_path = self.test_docs_dir / "analysis_results.json"
303
- result_path = self.analyzer.export_analysis_results(str(export_path))
304
- self.assertIsNotNone(result_path)
305
-
306
- # التحقق من وجود ملف التصدير
307
- self.assertTrue(export_path.exists())
308
-
309
- # حذف ملف التصدير
310
- if export_path.exists():
311
- export_path.unlink()
312
-
313
-
314
- class TestPricingEngine(unittest.TestCase):
315
- """اختبار محرك التسعير"""
316
-
317
- def setUp(self):
318
- """إعداد بيئة الاختبار"""
319
- self.config = AppConfig()
320
- self.pricing_engine = PricingEngine(self.config)
321
-
322
- def test_calculate_pricing(self):
323
- """اختبار حساب التسعير"""
324
- # حساب التسعير
325
- result = self.pricing_engine.calculate_pricing(1, "comprehensive")
326
- self.assertTrue(result)
327
-
328
- # انتظار اكتمال التسعير
329
- import time
330
- max_wait = 5 # ثوانٍ
331
- waited = 0
332
- while self.pricing_engine.pricing_in_progress and waited < max_wait:
333
- time.sleep(0.5)
334
- waited += 0.5
335
-
336
- # التحقق من نتائج التسعير
337
- self.assertFalse(self.pricing_engine.pricing_in_progress)
338
- results = self.pricing_engine.get_pricing_results()
339
- self.assertEqual(results["status"], "اكتمل التسعير")
340
- self.assertEqual(results["project_id"], 1)
341
- self.assertEqual(results["strategy"], "comprehensive")
342
-
343
- # التحقق من وجود التكاليف المباشرة
344
- self.assertIn("direct_costs", results)
345
- self.assertIn("total_direct_costs", results["direct_costs"])
346
-
347
- # التحقق من وجود التكاليف غير المباشرة
348
- self.assertIn("indirect_costs", results)
349
- self.assertIn("total_indirect_costs", results["indirect_costs"])
350
-
351
- # التحقق من وجود تكاليف المخاطر
352
- self.assertIn("risk_costs", results)
353
- self.assertIn("total_risk_cost", results["risk_costs"])
354
-
355
- # التحقق من وجود ملخص التسعير
356
- self.assertIn("summary", results)
357
- self.assertIn("final_price", results["summary"])
358
-
359
-
360
- class TestRiskAnalyzer(unittest.TestCase):
361
- """اختبار محلل المخاطر"""
362
-
363
- def setUp(self):
364
- """إعداد بيئة الاختبار"""
365
- self.config = AppConfig()
366
- self.risk_analyzer = RiskAnalyzer(self.config)
367
-
368
- def test_analyze_risks(self):
369
- """اختبار تحليل المخاطر"""
370
- # تحليل المخاطر
371
- result = self.risk_analyzer.analyze_risks(1, "comprehensive")
372
- self.assertTrue(result)
373
-
374
- # انتظار اكتمال التحليل
375
- import time
376
- max_wait = 5 # ثوانٍ
377
- waited = 0
378
- while self.risk_analyzer.analysis_in_progress and waited < max_wait:
379
- time.sleep(0.5)
380
- waited += 0.5
381
-
382
- # التحقق من نتائج التحليل
383
- self.assertFalse(self.risk_analyzer.analysis_in_progress)
384
- results = self.risk_analyzer.get_analysis_results()
385
- self.assertEqual(results["status"], "اكتمل التحليل")
386
- self.assertEqual(results["project_id"], 1)
387
- self.assertEqual(results["method"], "comprehensive")
388
-
389
- # التحقق من وجود المخاطر المحددة
390
- self.assertIn("identified_risks", results)
391
- self.assertTrue(len(results["identified_risks"]) > 0)
392
-
393
- # التحقق من وجود فئات المخاطر
394
- self.assertIn("risk_categories", results)
395
-
396
- # التحقق من وجود مصفوفة المخاطر
397
- self.assertIn("risk_matrix", results)
398
-
399
- # التحقق من وجود استراتيجيات التخفيف
400
- self.assertIn("mitigation_strategies", results)
401
- self.assertTrue(len(results["mitigation_strategies"]) > 0)
402
-
403
- # التحقق من وجود ملخص التحليل
404
- self.assertIn("summary", results)
405
- self.assertIn("overall_risk_level", results["summary"])
406
-
407
-
408
- class TestAIAssistant(unittest.TestCase):
409
- """اختبار المساعد الذكي"""
410
-
411
- def setUp(self):
412
- """إعداد بيئة الاختبار"""
413
- self.config = AppConfig()
414
- self.assistant = AIAssistant(self.config)
415
-
416
- def test_process_query(self):
417
- """اختبار معالجة الاستعلام"""
418
- # معالجة استعلام
419
- query = "كيف يمكنني تحليل مستند مناقصة؟"
420
- result = self.assistant.process_query(query)
421
- self.assertTrue(result)
422
-
423
- # انتظار اكتمال المعالجة
424
- import time
425
- max_wait = 5 # ثوانٍ
426
- waited = 0
427
- while self.assistant.processing_in_progress and waited < max_wait:
428
- time.sleep(0.5)
429
- waited += 0.5
430
-
431
- # التحقق من نتائج المعالجة
432
- self.assertFalse(self.assistant.processing_in_progress)
433
- results = self.assistant.get_processing_results()
434
- self.assertEqual(results["status"], "اكتملت المعالجة")
435
- self.assertEqual(results["query"], query)
436
-
437
- # التحقق من وجود استجابة
438
- self.assertIn("response", results)
439
- self.assertTrue(len(results["response"]) > 0)
440
-
441
- # التحقق من وجود اقتراحات
442
- self.assertIn("suggestions", results)
443
- self.assertTrue(len(results["suggestions"]) > 0)
444
-
445
- def test_conversation_history(self):
446
- """اختبار سجل المحادثة"""
447
- # معالجة استعلام
448
- query = "ما هي استراتيجيات التسعير المتاحة؟"
449
- self.assistant.process_query(query)
450
-
451
- # انتظار اكتمال المعالجة
452
- import time
453
- max_wait = 5 # ثوانٍ
454
- waited = 0
455
- while self.assistant.processing_in_progress and waited < max_wait:
456
- time.sleep(0.5)
457
- waited += 0.5
458
-
459
- # التحقق من سجل المحادثة
460
- history = self.assistant.get_conversation_history()
461
- self.assertEqual(len(history), 2) # استعلام المستخدم واستجابة المساعد
462
- self.assertEqual(history[0]["role"], "user")
463
- self.assertEqual(history[0]["content"], query)
464
- self.assertEqual(history[1]["role"], "assistant")
465
-
466
- # مسح سجل المحادثة
467
- self.assertTrue(self.assistant.clear_conversation_history())
468
- history = self.assistant.get_conversation_history()
469
- self.assertEqual(len(history), 0)
470
-
471
-
472
- class TestStyling(unittest.TestCase):
473
- """اختبار وحدات التصميم"""
474
-
475
- def test_app_theme(self):
476
- """اختبار نمط التطبيق"""
477
- theme = AppTheme()
478
-
479
- # التحقق من الألوان
480
- self.assertIsNotNone(theme.get_color("bg_color"))
481
- self.assertIsNotNone(theme.get_color("fg_color"))
482
-
483
- # التحقق من الخطوط
484
- self.assertIsNotNone(theme.get_font("body"))
485
- self.assertIsNotNone(theme.get_font("title"))
486
-
487
- # التحقق من الأحجام
488
- self.assertIsNotNone(theme.get_size("padding_medium"))
489
- self.assertIsNotNone(theme.get_size("border_radius"))
490
-
491
- # تغيير النمط
492
- self.assertTrue(theme.set_theme("dark"))
493
- self.assertEqual(theme.current_theme, "dark")
494
-
495
- # تغيير اللغة
496
- self.assertTrue(theme.set_language("en"))
497
- self.assertEqual(theme.current_language, "en")
498
-
499
- def test_icon_generator(self):
500
- """اختبار مولد الأيقونات"""
501
- icon_generator = IconGenerator()
502
-
503
- # توليد الأيقونات الافتراضية
504
- icon_generator.generate_default_icons()
505
-
506
- # التحقق من وجود مجلد الأيقونات
507
- self.assertTrue(Path('assets/icons').exists())
508
-
509
- # التحقق من وجود بعض الأيقونات
510
- self.assertTrue(Path('assets/icons/dashboard.png').exists())
511
- self.assertTrue(Path('assets/icons/projects.png').exists())
512
-
513
- def test_chart_generator(self):
514
- """اختبار مولد الرسوم البيانية"""
515
- theme = AppTheme()
516
- chart_generator = ChartGenerator(theme)
517
-
518
- # إنشاء بيانات للرسم البياني الشريطي
519
- bar_data = {
520
- 'labels': ['الربع الأول', 'الربع الثاني', 'الربع الثالث', 'الربع الرابع'],
521
- 'values': [15000, 20000, 18000, 25000]
522
- }
523
-
524
- # إنشاء رسم بياني شريطي
525
- fig = chart_generator.create_bar_chart(
526
- bar_data,
527
- 'الإيرادات الفصلية',
528
- 'الفصل',
529
- 'الإيرادات (ريال)'
530
- )
531
-
532
- # التحقق من إنشاء الرسم البياني
533
- self.assertIsNotNone(fig)
534
-
535
- # حفظ الرسم البياني
536
- save_path = 'test_chart.png'
537
- chart_generator.create_bar_chart(
538
- bar_data,
539
- 'الإيرادات الفصلية',
540
- 'الفصل',
541
- 'الإيرادات (ريال)',
542
- save_path=save_path
543
- )
544
-
545
- # التحقق من وجود ملف الرسم البياني
546
- self.assertTrue(Path(save_path).exists())
547
-
548
- # حذف ملف الرسم البياني
549
- if Path(save_path).exists():
550
- Path(save_path).unlink()
551
-
552
-
553
- def run_tests():
554
- """تشغيل الاختبارات"""
555
- # إنشاء مجلد الاختبارات
556
- test_dir = Path('test_results')
557
- test_dir.mkdir(exist_ok=True)
558
-
559
- # إنشاء ملف لنتائج الاختبارات
560
- test_results_file = test_dir / 'test_results.txt'
561
-
562
- # تشغيل الاختبارات وحفظ النتائج
563
- with open(test_results_file, 'w', encoding='utf-8') as f:
564
- runner = unittest.TextTestRunner(stream=f, verbosity=2)
565
- suite = unittest.TestSuite()
566
-
567
- # إضافة اختبارات قاعدة البيانات
568
- suite.addTest(unittest.makeSuite(TestDatabaseConnector))
569
- suite.addTest(unittest.makeSuite(TestModels))
570
-
571
- # إضافة اختبارات الوحدات
572
- suite.addTest(unittest.makeSuite(TestDocumentAnalyzer))
573
- suite.addTest(unittest.makeSuite(TestPricingEngine))
574
- suite.addTest(unittest.makeSuite(TestRiskAnalyzer))
575
- suite.addTest(unittest.makeSuite(TestAIAssistant))
576
-
577
- # إضافة اختبارات التصميم
578
- suite.addTest(unittest.makeSuite(TestStyling))
579
-
580
- # تشغيل الاختبارات
581
- result = runner.run(suite)
582
-
583
- # كتابة ملخص النتائج
584
- f.write("\n\n=== ملخص نتائج الاختبارات ===\n")
585
- f.write(f"عدد الاختبارات: {result.testsRun}\n")
586
- f.write(f"عدد النجاحات: {result.testsRun - len(result.failures) - len(result.errors)}\n")
587
- f.write(f"عدد الإخفاقات: {len(result.failures)}\n")
588
- f.write(f"عدد الأخطاء: {len(result.errors)}\n")
589
-
590
- # طباعة ملخص النتائج
591
- logger.info(f"تم تشغيل {result.testsRun} اختبار")
592
- logger.info(f"النجاحات: {result.testsRun - len(result.failures) - len(result.errors)}")
593
- logger.info(f"الإخفاقات: {len(result.failures)}")
594
- logger.info(f"الأخطاء: {len(result.errors)}")
595
- logger.info(f"تم حفظ نتائج الاختبارات في: {test_results_file}")
596
-
597
- return result
598
-
599
-
600
- if __name__ == "__main__":
601
- run_tests()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_integrated_system.py DELETED
@@ -1,96 +0,0 @@
1
- """
2
- وحدة اختبار النظام المتكامل
3
- """
4
-
5
- import unittest
6
- import os
7
- import sys
8
- from pathlib import Path
9
-
10
- # إضافة المسار الرئيسي للنظام
11
- sys.path.append(str(Path(__file__).parent.parent))
12
-
13
- # استيراد الوحدات
14
- from modules.pricing.pricing_app import PricingApp
15
- from modules.ai_assistant.ai_app import AIAssistantApp
16
- from modules.document_analysis.document_app import DocumentAnalysisApp
17
- from modules.data_analysis.data_analysis_app import DataAnalysisApp
18
- from modules.resources.resources_app import ResourcesApp
19
-
20
- class TestIntegratedSystem(unittest.TestCase):
21
- """اختبارات النظام المتكامل"""
22
-
23
- def setUp(self):
24
- """إعداد بيئة الاختبار"""
25
- # التأكد من وجود جميع الملفات الرئيسية
26
- self.main_files = [
27
- "app.py",
28
- "config.py",
29
- "requirements.txt"
30
- ]
31
-
32
- # التأكد من وجود جميع المجلدات الرئيسية
33
- self.main_directories = [
34
- "modules",
35
- "assets",
36
- "data",
37
- "utils"
38
- ]
39
-
40
- # التأكد من وجود جميع وحدات النظام
41
- self.modules = [
42
- "modules/pricing",
43
- "modules/ai_assistant",
44
- "modules/document_analysis",
45
- "modules/data_analysis",
46
- "modules/resources",
47
- "modules/project_management",
48
- "modules/reports",
49
- "modules/risk_analysis"
50
- ]
51
-
52
- def test_main_files_exist(self):
53
- """اختبار وجود الملفات الرئيسية"""
54
- for file in self.main_files:
55
- file_path = Path(__file__).parent.parent / file
56
- self.assertTrue(file_path.exists(), f"الملف {file} غير موجود")
57
-
58
- def test_main_directories_exist(self):
59
- """اختبار وجود المجلدات الرئيسية"""
60
- for directory in self.main_directories:
61
- dir_path = Path(__file__).parent.parent / directory
62
- self.assertTrue(dir_path.exists(), f"المجلد {directory} غير موجود")
63
-
64
- def test_modules_exist(self):
65
- """اختبار وجود وحدات النظام"""
66
- for module in self.modules:
67
- module_path = Path(__file__).parent.parent / module
68
- self.assertTrue(module_path.exists(), f"الوحدة {module} غير موجودة")
69
-
70
- def test_pricing_module(self):
71
- """اختبار وحدة التسعير"""
72
- pricing_app = PricingApp()
73
- self.assertIsNotNone(pricing_app, "فشل إنشاء وحدة التسعير")
74
-
75
- def test_ai_assistant_module(self):
76
- """اختبار وحدة الذكاء الاصطناعي"""
77
- ai_app = AIAssistantApp()
78
- self.assertIsNotNone(ai_app, "فشل إنشاء وحدة الذكاء الاصطناعي")
79
-
80
- def test_document_analysis_module(self):
81
- """اختبار وحدة تحليل المستندات"""
82
- document_app = DocumentAnalysisApp()
83
- self.assertIsNotNone(document_app, "فشل إنشاء وحدة تحليل المستندات")
84
-
85
- def test_data_analysis_module(self):
86
- """اختبار وحدة تحليل البيانات"""
87
- data_analysis_app = DataAnalysisApp()
88
- self.assertIsNotNone(data_analysis_app, "فشل إنشاء وحدة تحليل البيانات")
89
-
90
- def test_resources_module(self):
91
- """اختبار وحدة الموارد"""
92
- resources_app = ResourcesApp()
93
- self.assertIsNotNone(resources_app, "فشل إنشاء وحدة الموارد")
94
-
95
- if __name__ == "__main__":
96
- unittest.main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_ui.py DELETED
@@ -1,413 +0,0 @@
1
- """
2
- وحدة اختبار واجهة المستخدم لنظام إدارة المناقصات - Hybrid Face
3
- """
4
-
5
- import os
6
- import sys
7
- import logging
8
- import unittest
9
- import tkinter as tk
10
- import customtkinter as ctk
11
- from pathlib import Path
12
-
13
- # تهيئة السجل
14
- logging.basicConfig(
15
- level=logging.INFO,
16
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
17
- )
18
- logger = logging.getLogger('test_ui')
19
-
20
- # إضافة المسار الرئيسي للتطبيق إلى مسار البحث
21
- sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
22
-
23
- # استيراد الوحدات المطلوبة للاختبار
24
- from styling.theme import AppTheme
25
- from styling.icons import IconGenerator
26
- from styling.charts import ChartGenerator
27
- from config import AppConfig
28
-
29
- class TestUIComponents(unittest.TestCase):
30
- """اختبار مكونات واجهة المستخدم"""
31
-
32
- def setUp(self):
33
- """إعداد بيئة الاختبار"""
34
- self.root = ctk.CTk()
35
- self.root.withdraw() # إخفاء النافذة أثناء الاختبار
36
- self.theme = AppTheme()
37
-
38
- def tearDown(self):
39
- """تنظيف بيئة الاختبار"""
40
- self.root.destroy()
41
-
42
- def test_styled_frame(self):
43
- """اختبار الإطار المنسق"""
44
- frame = self.theme.create_styled_frame(self.root)
45
- self.assertIsNotNone(frame)
46
- self.assertEqual(frame.cget("fg_color"), self.theme.get_color("card_bg_color"))
47
- self.assertEqual(frame.cget("corner_radius"), self.theme.get_size("border_radius"))
48
-
49
- def test_styled_button(self):
50
- """اختبار الزر المنسق"""
51
- button = self.theme.create_styled_button(self.root, "زر اختبار")
52
- self.assertIsNotNone(button)
53
- self.assertEqual(button.cget("text"), "زر اختبار")
54
- self.assertEqual(button.cget("fg_color"), self.theme.get_color("button_bg_color"))
55
- self.assertEqual(button.cget("text_color"), self.theme.get_color("button_fg_color"))
56
-
57
- def test_styled_label(self):
58
- """اختبار التسمية المنسقة"""
59
- label = self.theme.create_styled_label(self.root, "تسمية اختبار")
60
- self.assertIsNotNone(label)
61
- self.assertEqual(label.cget("text"), "تسمية اختبار")
62
- self.assertEqual(label.cget("text_color"), self.theme.get_color("fg_color"))
63
-
64
- def test_styled_entry(self):
65
- """اختبار حقل الإدخال المنسق"""
66
- entry = self.theme.create_styled_entry(self.root, "نص توضيحي")
67
- self.assertIsNotNone(entry)
68
- self.assertEqual(entry.cget("placeholder_text"), "نص توضيحي")
69
- self.assertEqual(entry.cget("fg_color"), self.theme.get_color("input_bg_color"))
70
- self.assertEqual(entry.cget("text_color"), self.theme.get_color("input_fg_color"))
71
-
72
- def test_styled_combobox(self):
73
- """اختبار القائمة المنسدلة المنسقة"""
74
- values = ["الخيار الأول", "الخيار الثاني", "الخيار الثالث"]
75
- combobox = self.theme.create_styled_combobox(self.root, values)
76
- self.assertIsNotNone(combobox)
77
- self.assertEqual(combobox.cget("values"), values)
78
- self.assertEqual(combobox.cget("fg_color"), self.theme.get_color("input_bg_color"))
79
- self.assertEqual(combobox.cget("text_color"), self.theme.get_color("input_fg_color"))
80
-
81
- def test_styled_checkbox(self):
82
- """اختبار خانة الاختيار المنسقة"""
83
- checkbox = self.theme.create_styled_checkbox(self.root, "خانة اختبار")
84
- self.assertIsNotNone(checkbox)
85
- self.assertEqual(checkbox.cget("text"), "خانة اختبار")
86
- self.assertEqual(checkbox.cget("fg_color"), self.theme.get_color("button_bg_color"))
87
- self.assertEqual(checkbox.cget("text_color"), self.theme.get_color("fg_color"))
88
-
89
- def test_styled_radio_button(self):
90
- """اختبار زر الراديو المنسق"""
91
- var = ctk.StringVar(value="1")
92
- radio_button = self.theme.create_styled_radio_button(self.root, "زر راديو اختبار", var, "1")
93
- self.assertIsNotNone(radio_button)
94
- self.assertEqual(radio_button.cget("text"), "زر راديو اختبار")
95
- self.assertEqual(radio_button.cget("fg_color"), self.theme.get_color("button_bg_color"))
96
- self.assertEqual(radio_button.cget("text_color"), self.theme.get_color("fg_color"))
97
-
98
- def test_styled_switch(self):
99
- """اختبار مفتاح التبديل المنسق"""
100
- switch = self.theme.create_styled_switch(self.root, "مفتاح اختبار")
101
- self.assertIsNotNone(switch)
102
- self.assertEqual(switch.cget("text"), "مفتاح اختبار")
103
- self.assertEqual(switch.cget("progress_color"), self.theme.get_color("button_bg_color"))
104
- self.assertEqual(switch.cget("text_color"), self.theme.get_color("fg_color"))
105
-
106
- def test_styled_slider(self):
107
- """اختبار شريط التمرير المنسق"""
108
- slider = self.theme.create_styled_slider(self.root)
109
- self.assertIsNotNone(slider)
110
- self.assertEqual(slider.cget("fg_color"), self.theme.get_color("input_border_color"))
111
- self.assertEqual(slider.cget("progress_color"), self.theme.get_color("button_bg_color"))
112
-
113
- def test_styled_progressbar(self):
114
- """اختبار شريط التقدم المنسق"""
115
- progressbar = self.theme.create_styled_progressbar(self.root)
116
- self.assertIsNotNone(progressbar)
117
- self.assertEqual(progressbar.cget("fg_color"), self.theme.get_color("input_border_color"))
118
- self.assertEqual(progressbar.cget("progress_color"), self.theme.get_color("button_bg_color"))
119
-
120
- def test_styled_tabview(self):
121
- """اختبار عرض التبويب المنسق"""
122
- tabview = self.theme.create_styled_tabview(self.root)
123
- self.assertIsNotNone(tabview)
124
- self.assertEqual(tabview.cget("fg_color"), self.theme.get_color("card_bg_color"))
125
-
126
- def test_styled_scrollable_frame(self):
127
- """اختبار الإطار القابل للتمرير المنسق"""
128
- scrollable_frame = self.theme.create_styled_scrollable_frame(self.root)
129
- self.assertIsNotNone(scrollable_frame)
130
- self.assertEqual(scrollable_frame.cget("fg_color"), "transparent")
131
-
132
- def test_styled_textbox(self):
133
- """اختبار مربع النص المنسق"""
134
- textbox = self.theme.create_styled_textbox(self.root)
135
- self.assertIsNotNone(textbox)
136
- self.assertEqual(textbox.cget("fg_color"), self.theme.get_color("input_bg_color"))
137
- self.assertEqual(textbox.cget("text_color"), self.theme.get_color("input_fg_color"))
138
-
139
- def test_styled_card(self):
140
- """اختبار البطاقة المنسقة"""
141
- card, content_frame = self.theme.create_styled_card(self.root, "بطاقة اختبار")
142
- self.assertIsNotNone(card)
143
- self.assertIsNotNone(content_frame)
144
- self.assertEqual(card.cget("fg_color"), self.theme.get_color("card_bg_color"))
145
-
146
- def test_styled_data_table(self):
147
- """اختبار جدول البيانات المنسق"""
148
- columns = ["العمود الأول", "العمود الثاني", "العمود الثالث"]
149
- data = [
150
- ["بيانات 1-1", "بيانات 1-2", "بيانات 1-3"],
151
- ["بيانات 2-1", "بيانات 2-2", "بيانات 2-3"]
152
- ]
153
- table_frame, data_frame = self.theme.create_styled_data_table(self.root, columns, data)
154
- self.assertIsNotNone(table_frame)
155
- self.assertIsNotNone(data_frame)
156
- self.assertEqual(table_frame.cget("fg_color"), self.theme.get_color("card_bg_color"))
157
-
158
- def test_theme_switching(self):
159
- """اختبار تبديل النمط"""
160
- # تعيين النمط الفاتح
161
- self.theme.set_theme("light")
162
- light_bg_color = self.theme.get_color("bg_color")
163
-
164
- # تعيين النمط الداكن
165
- self.theme.set_theme("dark")
166
- dark_bg_color = self.theme.get_color("bg_color")
167
-
168
- # التحقق من اختلاف الألوان
169
- self.assertNotEqual(light_bg_color, dark_bg_color)
170
-
171
- def test_language_switching(self):
172
- """اختبار تبديل اللغة"""
173
- # تعيين اللغة العربية
174
- self.theme.set_language("ar")
175
- ar_font = self.theme.get_font("body")
176
-
177
- # تعيين اللغة الإنجليزية
178
- self.theme.set_language("en")
179
- en_font = self.theme.get_font("body")
180
-
181
- # التحقق من اختلاف الخطوط
182
- self.assertNotEqual(ar_font[0], en_font[0])
183
-
184
-
185
- class TestUILayout(unittest.TestCase):
186
- """اختبار تخطيط واجهة المستخدم"""
187
-
188
- def setUp(self):
189
- """إعداد بيئة الاختبار"""
190
- self.root = ctk.CTk()
191
- self.root.withdraw() # إخفاء النافذة أثناء الاختبار
192
- self.theme = AppTheme()
193
-
194
- # إنشاء الإطار الرئيسي
195
- self.main_frame = self.theme.create_styled_frame(self.root)
196
- self.main_frame.pack(fill="both", expand=True)
197
-
198
- # إنشاء الشريط الجانبي
199
- self.sidebar_frame = self.theme.create_styled_frame(
200
- self.main_frame,
201
- fg_color=self.theme.get_color("sidebar_bg_color")
202
- )
203
- self.sidebar_frame.pack(side="left", fill="y", padx=0, pady=0)
204
-
205
- # إنشاء إطار المحتوى
206
- self.content_frame = self.theme.create_styled_frame(
207
- self.main_frame,
208
- fg_color=self.theme.get_color("bg_color")
209
- )
210
- self.content_frame.pack(side="right", fill="both", expand=True, padx=0, pady=0)
211
-
212
- def tearDown(self):
213
- """تنظيف بيئة الاخ��بار"""
214
- self.root.destroy()
215
-
216
- def test_sidebar_layout(self):
217
- """اختبار تخطيط الشريط الجانبي"""
218
- # إنشاء شعار التطبيق
219
- logo_label = self.theme.create_styled_label(
220
- self.sidebar_frame,
221
- "نظام إدارة المناقصات",
222
- font=self.theme.get_font("title"),
223
- text_color=self.theme.get_color("sidebar_fg_color")
224
- )
225
- logo_label.pack(padx=20, pady=20)
226
-
227
- # إنشاء أزرار الشريط الجانبي
228
- sidebar_buttons = []
229
- button_texts = [
230
- "لوحة التحكم", "المشاريع", "المستندات", "التسعير",
231
- "الموارد", "المخاطر", "التقارير", "الذكاء الاصطناعي"
232
- ]
233
-
234
- for text in button_texts:
235
- button_frame, button = self.theme.create_styled_sidebar_button(
236
- self.sidebar_frame,
237
- text
238
- )
239
- button_frame.pack(fill="x", padx=0, pady=2)
240
- sidebar_buttons.append(button)
241
-
242
- # التحقق من إنشاء الأزرار
243
- self.assertEqual(len(sidebar_buttons), len(button_texts))
244
- for i, button in enumerate(sidebar_buttons):
245
- self.assertEqual(button.cget("text"), button_texts[i])
246
-
247
- def test_content_layout(self):
248
- """اختبار تخطيط المحتوى"""
249
- # إنشاء شريط العنوان
250
- header_frame = self.theme.create_styled_frame(
251
- self.content_frame,
252
- fg_color=self.theme.get_color("card_bg_color")
253
- )
254
- header_frame.pack(fill="x", padx=20, pady=20)
255
-
256
- # إنشاء عنوان الصفحة
257
- page_title = self.theme.create_styled_label(
258
- header_frame,
259
- "لوحة التحكم",
260
- font=self.theme.get_font("title")
261
- )
262
- page_title.pack(side="left", padx=20, pady=20)
263
-
264
- # إنشاء زر البحث
265
- search_button = self.theme.create_styled_button(
266
- header_frame,
267
- "بحث"
268
- )
269
- search_button.pack(side="right", padx=20, pady=20)
270
-
271
- # إنشاء إطار البطاقات
272
- cards_frame = self.theme.create_styled_frame(
273
- self.content_frame,
274
- fg_color="transparent"
275
- )
276
- cards_frame.pack(fill="both", expand=True, padx=20, pady=20)
277
-
278
- # إنشاء بطاقات
279
- cards = []
280
- card_titles = [
281
- "المشاريع النشطة", "المناقصات الجديدة", "المخاطر العالية", "التقارير المعلقة"
282
- ]
283
-
284
- for i, title in enumerate(card_titles):
285
- card, card_content = self.theme.create_styled_card(
286
- cards_frame,
287
- title
288
- )
289
- card.grid(row=i//2, column=i%2, padx=10, pady=10, sticky="nsew")
290
- cards.append(card)
291
-
292
- # التحقق من إنشاء البطاقات
293
- self.assertEqual(len(cards), len(card_titles))
294
-
295
- # تهيئة أوزان الصفوف والأعمدة
296
- cards_frame.grid_columnconfigure(0, weight=1)
297
- cards_frame.grid_columnconfigure(1, weight=1)
298
- cards_frame.grid_rowconfigure(0, weight=1)
299
- cards_frame.grid_rowconfigure(1, weight=1)
300
-
301
- def test_responsive_layout(self):
302
- """اختبار التخطيط المتجاوب"""
303
- # تغيير حجم النافذة
304
- self.root.geometry("800x600")
305
- self.root.update()
306
-
307
- # التحقق من أن الإطار الرئيسي يملأ النافذة
308
- self.assertEqual(self.main_frame.winfo_width(), 800)
309
- self.assertEqual(self.main_frame.winfo_height(), 600)
310
-
311
- # تغيير حجم النافذة مرة أخرى
312
- self.root.geometry("1024x768")
313
- self.root.update()
314
-
315
- # التحقق من أن الإطار الرئيسي يملأ النافذة
316
- self.assertEqual(self.main_frame.winfo_width(), 1024)
317
- self.assertEqual(self.main_frame.winfo_height(), 768)
318
-
319
-
320
- class TestUIArabicSupport(unittest.TestCase):
321
- """اختبار دعم اللغة العربية في واجهة المستخدم"""
322
-
323
- def setUp(self):
324
- """إعداد بيئة الاختبار"""
325
- self.root = ctk.CTk()
326
- self.root.withdraw() # إخفاء النافذة أثناء الاختبار
327
- self.theme = AppTheme()
328
- self.theme.set_language("ar") # تعيين اللغة العربية
329
-
330
- def tearDown(self):
331
- """تنظيف بيئة الاختبار"""
332
- self.root.destroy()
333
-
334
- def test_arabic_text_display(self):
335
- """اختبار عرض النص العربي"""
336
- # إنشاء تسمية بنص عربي
337
- arabic_text = "هذا نص عربي للاختبار"
338
- label = self.theme.create_styled_label(self.root, arabic_text)
339
- self.assertEqual(label.cget("text"), arabic_text)
340
-
341
- # إنشاء زر بنص عربي
342
- button = self.theme.create_styled_button(self.root, "زر باللغة العربية")
343
- self.assertEqual(button.cget("text"), "زر باللغة العربية")
344
-
345
- # إنشاء حقل إدخال بنص توضيحي عربي
346
- entry = self.theme.create_styled_entry(self.root, "أدخل النص هنا")
347
- self.assertEqual(entry.cget("placeholder_text"), "أدخل النص هنا")
348
-
349
- def test_arabic_font(self):
350
- """اختبار الخط العربي"""
351
- # التحقق من استخدام خط يدعم العربية
352
- ar_font = self.theme.get_font("body")
353
- self.assertEqual(ar_font[0], "Cairo")
354
-
355
- def test_rtl_support(self):
356
- """اختبار دعم الكتابة من اليمين إلى اليسار"""
357
- # إنشاء إطار
358
- frame = self.theme.create_styled_frame(self.root)
359
- frame.pack(fill="both", expand=True)
360
-
361
- # إنشاء تسمية بنص عربي
362
- label = self.theme.create_styled_label(frame, "نص عربي من اليمين إلى اليسار")
363
- label.pack(anchor="e", padx=20, pady=20) # محاذاة إلى اليمين
364
-
365
- # التحقق من المحاذاة
366
- self.assertEqual(label.cget("anchor"), "w") # w تعني غرب (يسار)، لكن النص سيظهر من اليمين إلى اليسار
367
-
368
-
369
- def run_ui_tests():
370
- """تشغيل اختبارات واجهة المستخدم"""
371
- # إنشاء مجلد الاختبارات
372
- test_dir = Path('test_results')
373
- test_dir.mkdir(exist_ok=True)
374
-
375
- # إنشاء ملف لنتائج الاختبارات
376
- test_results_file = test_dir / 'ui_test_results.txt'
377
-
378
- # تشغيل الاختبارات وحفظ النتائج
379
- with open(test_results_file, 'w', encoding='utf-8') as f:
380
- runner = unittest.TextTestRunner(stream=f, verbosity=2)
381
- suite = unittest.TestSuite()
382
-
383
- # إضافة اختبارات مكونات واجهة المستخدم
384
- suite.addTest(unittest.makeSuite(TestUIComponents))
385
-
386
- # إضافة اختبارات تخطيط واجهة المستخدم
387
- suite.addTest(unittest.makeSuite(TestUILayout))
388
-
389
- # إضافة اختبارات دعم اللغة العربية
390
- suite.addTest(unittest.makeSuite(TestUIArabicSupport))
391
-
392
- # تشغيل الاختبارات
393
- result = runner.run(suite)
394
-
395
- # كتابة ملخص النتائج
396
- f.write("\n\n=== ملخص نتائج اختبارات واجهة المستخدم ===\n")
397
- f.write(f"عدد الاختبارات: {result.testsRun}\n")
398
- f.write(f"عدد النجاحات: {result.testsRun - len(result.failures) - len(result.errors)}\n")
399
- f.write(f"عدد الإخفاقات: {len(result.failures)}\n")
400
- f.write(f"عدد الأخطاء: {len(result.errors)}\n")
401
-
402
- # طباعة ملخص النتائج
403
- logger.info(f"تم تشغيل {result.testsRun} اختبار لواجهة المستخدم")
404
- logger.info(f"النجاحات: {result.testsRun - len(result.failures) - len(result.errors)}")
405
- logger.info(f"الإخفاقات: {len(result.failures)}")
406
- logger.info(f"الأخطاء: {len(result.errors)}")
407
- logger.info(f"تم حفظ نتائج الاختبارات في: {test_results_file}")
408
-
409
- return result
410
-
411
-
412
- if __name__ == "__main__":
413
- run_ui_tests()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/unit/test_reports.py DELETED
@@ -1,200 +0,0 @@
1
- """
2
- اختبارات وحدة تقارير المشاريع
3
- """
4
-
5
- import unittest
6
- import pandas as pd
7
- import numpy as np
8
- import sys
9
- import os
10
- from datetime import datetime, timedelta
11
-
12
- # إضافة المسار الرئيسي للمشروع لاستيراد الوحدات
13
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
14
-
15
- from modules.reports.reports_app import ReportsApp
16
-
17
-
18
- class TestReportsApp(unittest.TestCase):
19
- """اختبارات وحدة لتطبيق التقارير"""
20
-
21
- def setUp(self):
22
- """إعداد بيئة الاختبار"""
23
- self.reports_app = ReportsApp()
24
-
25
- # إنشاء بيانات محاكاة للمشاريع
26
- self.mock_projects = [
27
- {
28
- 'id': 1,
29
- 'name': 'مشروع 1',
30
- 'number': 'T-2024001',
31
- 'client': 'وزارة الصحة',
32
- 'location': 'الرياض',
33
- 'status': 'جديد',
34
- 'submission_date': '2024-01-15',
35
- 'tender_type': 'عامة',
36
- 'created_at': '2024-01-10'
37
- },
38
- {
39
- 'id': 2,
40
- 'name': 'مشروع 2',
41
- 'number': 'T-2024002',
42
- 'client': 'وزارة التعليم',
43
- 'location': 'جدة',
44
- 'status': 'قيد التسعير',
45
- 'submission_date': '2024-01-28',
46
- 'tender_type': 'خاصة',
47
- 'created_at': '2024-01-20'
48
- },
49
- {
50
- 'id': 3,
51
- 'name': 'مشروع 3',
52
- 'number': 'T-2024003',
53
- 'client': 'وزارة الصحة',
54
- 'location': 'الدمام',
55
- 'status': 'تم التقديم',
56
- 'submission_date': '2024-02-10',
57
- 'tender_type': 'عامة',
58
- 'created_at': '2024-02-01'
59
- },
60
- {
61
- 'id': 4,
62
- 'name': 'مشروع 4',
63
- 'number': 'T-2024004',
64
- 'client': 'شركة أرامكو',
65
- 'location': 'الظهران',
66
- 'status': 'تمت الترسية',
67
- 'submission_date': '2024-02-15',
68
- 'tender_type': 'عامة',
69
- 'created_at': '2024-02-05'
70
- },
71
- {
72
- 'id': 5,
73
- 'name': 'مشروع 5',
74
- 'number': 'T-2024005',
75
- 'client': 'شركة سابك',
76
- 'location': 'الجبيل',
77
- 'status': 'قيد التنفيذ',
78
- 'submission_date': '2024-02-20',
79
- 'tender_type': 'خاصة',
80
- 'created_at': '2024-02-10'
81
- }
82
- ]
83
-
84
- def test_get_total_projects(self):
85
- """اختبار حساب إجمالي عدد المشاريع"""
86
- # تعيين المشاريع في حالة الجلسة
87
- import streamlit as st
88
- st.session_state.projects = self.mock_projects
89
-
90
- # اختبار الدالة
91
- total = self.reports_app._get_total_projects()
92
- self.assertEqual(total, 5)
93
-
94
- def test_get_active_projects(self):
95
- """اختبار حساب عدد المشاريع النشطة"""
96
- # تعيين المشاريع في حالة الجلسة
97
- import streamlit as st
98
- st.session_state.projects = self.mock_projects
99
-
100
- # اختبار الدالة
101
- active = self.reports_app._get_active_projects()
102
- self.assertEqual(active, 4) # جميع المشاريع باستثناء الجديدة أو المنتهية أو الملغية
103
-
104
- def test_get_won_projects(self):
105
- """اختبار حساب عدد المشاريع المرساة"""
106
- # تعيين المشاريع في حالة الجلسة
107
- import streamlit as st
108
- st.session_state.projects = self.mock_projects
109
-
110
- # اختبار الدالة
111
- won = self.reports_app._get_won_projects()
112
- self.assertEqual(won, 2) # المشاريع المرساة وقيد التنفيذ والمنتهية
113
-
114
- def test_get_project_status_data(self):
115
- """اختبار الحصول على بيانات توزيع المشاريع حسب الحالة"""
116
- # تعيين المشاريع في حالة الجلسة
117
- import streamlit as st
118
- st.session_state.projects = self.mock_projects
119
-
120
- # اختبار الدالة
121
- status_data = self.reports_app._get_project_status_data()
122
-
123
- self.assertIsInstance(status_data, pd.DataFrame)
124
- self.assertEqual(len(status_data), 5) # 5 حالات مختلفة
125
-
126
- # التحقق من أن عدد المشاريع لكل حالة صحيح
127
- status_counts = status_data.set_index('status')['count'].to_dict()
128
- self.assertEqual(status_counts['جديد'], 1)
129
- self.assertEqual(status_counts['قيد التسعير'], 1)
130
- self.assertEqual(status_counts['تم التقديم'], 1)
131
- self.assertEqual(status_counts['تمت الترسية'], 1)
132
- self.assertEqual(status_counts['قيد التنفيذ'], 1)
133
-
134
- def test_get_monthly_project_data(self):
135
- """اختبار الحصول على بيانات اتجاه المشاريع الشهري"""
136
- # اختبار الدالة
137
- monthly_data = self.reports_app._get_monthly_project_data()
138
-
139
- self.assertIsInstance(monthly_data, pd.DataFrame)
140
- self.assertEqual(len(monthly_data), 6) # 6 أشهر
141
-
142
- # التحقق من وجود الأعمدة المطلوبة
143
- self.assertIn('month', monthly_data.columns)
144
- self.assertIn('new', monthly_data.columns)
145
- self.assertIn('submitted', monthly_data.columns)
146
- self.assertIn('won', monthly_data.columns)
147
-
148
- def test_get_project_value_data(self):
149
- """اختبار الحصول على بيانات توزيع قيم المشاريع"""
150
- # اختبار الدالة
151
- value_data = self.reports_app._get_project_value_data()
152
-
153
- self.assertIsInstance(value_data, pd.DataFrame)
154
- self.assertEqual(len(value_data), 7) # 7 نطاقات قيمة
155
-
156
- # التحقق من وجود الأعمدة المطلوبة
157
- self.assertIn('range', value_data.columns)
158
- self.assertIn('count', value_data.columns)
159
-
160
- def test_generate_sample_data(self):
161
- """اختبار توليد البيانات العشوائية للمحاكاة"""
162
- # مشاريع
163
- project_fields = ["اسم المشروع", "رقم المناقصة", "العميل", "الموقع", "الحالة"]
164
- project_data = self.reports_app._generate_sample_data("المشاريع", project_fields, 10)
165
-
166
- self.assertIsInstance(project_data, pd.DataFrame)
167
- self.assertEqual(len(project_data), 10)
168
- for field in project_fields:
169
- self.assertIn(field, project_data.columns)
170
-
171
- # تسعير
172
- pricing_fields = ["اسم المشروع", "رقم البند", "وصف البند", "الكمية", "سعر الوحدة"]
173
- pricing_data = self.reports_app._generate_sample_data("التسعير", pricing_fields, 10)
174
-
175
- self.assertIsInstance(pricing_data, pd.DataFrame)
176
- self.assertEqual(len(pricing_data), 10)
177
- for field in pricing_fields:
178
- self.assertIn(field, pricing_data.columns)
179
-
180
- # مخاطر
181
- risk_fields = ["اسم المشروع", "رمز المخاطرة", "وصف المخاطرة", "الفئة"]
182
- risk_data = self.reports_app._generate_sample_data("المخاطر", risk_fields, 10)
183
-
184
- self.assertIsInstance(risk_data, pd.DataFrame)
185
- self.assertEqual(len(risk_data), 10)
186
- for field in risk_fields:
187
- self.assertIn(field, risk_data.columns)
188
-
189
- # محتوى محلي
190
- local_content_fields = ["اسم المشروع", "الفئة", "البند", "المورد", "التكلفة"]
191
- local_content_data = self.reports_app._generate_sample_data("المحتوى المحلي", local_content_fields, 10)
192
-
193
- self.assertIsInstance(local_content_data, pd.DataFrame)
194
- self.assertEqual(len(local_content_data), 10)
195
- for field in local_content_fields:
196
- self.assertIn(field, local_content_data.columns)
197
-
198
-
199
- if __name__ == '__main__':
200
- unittest.main()