diff --git a/app/Http/Controllers/SitemapController.php b/app/Http/Controllers/SitemapController.php index 89015e7..58feae8 100644 --- a/app/Http/Controllers/SitemapController.php +++ b/app/Http/Controllers/SitemapController.php @@ -12,6 +12,8 @@ public function __invoke(SpecializationCatalogService $catalog): Response $urls = [ route('home'), route('career-interests.index'), + route('major-choice.index'), + route('scientific-foundation.index'), route('specializations.index'), ]; diff --git a/docs/04-assessment/scientific_theoretical_framework.md b/docs/04-assessment/scientific_theoretical_framework.md index 0982f92..3009ede 100644 --- a/docs/04-assessment/scientific_theoretical_framework.md +++ b/docs/04-assessment/scientific_theoretical_framework.md @@ -108,7 +108,7 @@ ## 5. الأساس الثاني: النظرية المعرفية الاجتماعية للمسار المهني SCCT -طوّر Lent وBrown وHackett النظرية لتفسير نشوء الاهتمامات والاختيارات والأداء من خلال تفاعل فعالية الذات، وتوقعات النتائج، والأهداف، وخبرات التعلم، والدعم والعوائق السياقية. المرجع المؤسس هو [Lent, Brown, & Hackett, 1994](https://doi.org/10.1016/0001-8791(94)90026-4). +طوّر Lent وBrown وHackett النظرية لتفسير نشوء الاهتمامات والاختيارات والأداء من خلال تفاعل فعالية الذات، وتوقعات النتائج، والأهداف، وخبرات التعلم، والدعم والعوائق السياقية. المرجع المؤسس هو [Lent, Brown, & Hackett, 1994](https://doi.org/10.1006/jvbe.1994.1027). ### 5.1 ما الذي نأخذه منها؟ @@ -450,7 +450,7 @@ SCCT **إطار مساند أساسي** لبنود قليلة ومنفصلة ب ## 17. مراجع البحث الأساسية 1. Holland, J. L. (1997). *Making Vocational Choices* (3rd ed.). Psychological Assessment Resources. الأساس النظري لنموذج RIASEC. -2. Lent, R. W., Brown, S. D., & Hackett, G. (1994). [Toward a Unifying Social Cognitive Theory of Career and Academic Interest, Choice, and Performance](https://doi.org/10.1016/0001-8791(94)90026-4). *Journal of Vocational Behavior, 45*, 79–122. +2. Lent, R. W., Brown, S. D., & Hackett, G. (1994). [Toward a Unifying Social Cognitive Theory of Career and Academic Interest, Choice, and Performance](https://doi.org/10.1006/jvbe.1994.1027). *Journal of Vocational Behavior, 45*, 79–122. 3. Eccles, J. S., & Wigfield, A. (2020). [From Expectancy–Value Theory to Situated Expectancy–Value Theory](https://doi.org/10.1016/j.cedpsych.2020.101859). *Contemporary Educational Psychology, 61*. 4. Nye, C. D., Su, R., Rounds, J., & Drasgow, F. (2012). [Vocational Interests and Performance](https://doi.org/10.1177/1745691612449021). *Perspectives on Psychological Science, 7*(4), 384–403. 5. Nye, C. D., Su, R., Rounds, J., & Drasgow, F. (2017). [Interest Congruence and Performance](https://doi.org/10.1016/j.jvb.2016.11.002). *Journal of Vocational Behavior, 98*, 138–151. diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 2ee262b..a3c69e2 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -122,5 +122,12 @@ class="h-full w-full object-cover"
+ + @endsection diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 541ab02..1f1b4a5 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -22,6 +22,8 @@ $seoIsIndexablePublicPage = request()->routeIs( 'home', 'career-interests.index', + 'major-choice.index', + 'scientific-foundation.index', 'specializations.index', 'specializations.show', ); @@ -231,7 +233,7 @@ class="{{ request()->routeIs('assessment.show') ? 'w-full flex-1 break-words px-2 py-2 sm:px-4 sm:py-3 lg:px-5 lg:py-3' : (request()->routeIs('assessment.intro') ? 'w-full flex-1 break-words px-2 py-2 sm:px-4 sm:py-3 lg:px-5 lg:py-3' - : (request()->routeIs('home') || request()->routeIs('specializations.index') || request()->routeIs('specializations.show') || request()->routeIs('specializations.compare') || request()->routeIs('results.show') || request()->routeIs('profile.results.index') || request()->routeIs('profile.show') || request()->routeIs('login') || request()->routeIs('register') || request()->routeIs('password.*') + : (request()->routeIs('home') || request()->routeIs('major-choice.index') || request()->routeIs('scientific-foundation.index') || request()->routeIs('specializations.index') || request()->routeIs('specializations.show') || request()->routeIs('specializations.compare') || request()->routeIs('results.show') || request()->routeIs('profile.results.index') || request()->routeIs('profile.show') || request()->routeIs('login') || request()->routeIs('register') || request()->routeIs('password.*') ? 'w-full flex-1 break-words px-2 py-2 sm:px-4 sm:py-3 lg:px-5 lg:py-5' : 'container-page flex-1 break-words py-8 lg:py-12')) }}"> @@ -298,6 +300,12 @@ class="site-footer-social-link"
  • اختبار الميول المهنية
  • +
  • + كيف أختار تخصصي؟ +
  • +
  • + الأساس العلمي +
  • التخصصات
  • diff --git a/resources/views/seo/career-interests.blade.php b/resources/views/seo/career-interests.blade.php index 9ef773d..2664003 100644 --- a/resources/views/seo/career-interests.blade.php +++ b/resources/views/seo/career-interests.blade.php @@ -117,6 +117,9 @@ class="absolute inset-0 h-full w-full object-cover" تصفح دليل التخصصات الجامعية + + تعرّف إلى الأساس العلمي للتقييم + diff --git a/resources/views/seo/choose-major.blade.php b/resources/views/seo/choose-major.blade.php new file mode 100644 index 0000000..5bd0915 --- /dev/null +++ b/resources/views/seo/choose-major.blade.php @@ -0,0 +1,142 @@ +@extends('layouts.app') + +@section('title', 'كيف أختار تخصصي الجامعي؟ | دليل عملي للطلاب') +@section('seo_description', 'كيف تختار تخصصك الجامعي؟ ابدأ بفهم ميولك وقدراتك وقيمك، ثم افهم طبيعة التخصص وقارن الخيارات بخطوات عملية تساعدك على اتخاذ قرار واعٍ بعد الثانوية.') + +@section('content') +
    + + +
    +
    +
    +

    دليل عملي بعد الثانوية

    +

    + كيف أختار تخصصي الجامعي؟ +

    +

    + لا يبدأ الاختيار باسم التخصص، بل بفهم نفسك أولًا، ثم فهم الخيارات المتاحة، ثم المقارنة بينها بوعي. الهدف ليس العثور على «تخصص مثالي»، بل الوصول إلى قرار تستطيع تفسيره والتحقق منه. +

    + + +
    + +
    + طالب يفكر في اختيار تخصصه الجامعي بعد الثانوية + +
    +
    +
    + +
    +

    ثلاث خطوات أساسية

    +

    ابدأ بهذه الطريقة بدل البحث العشوائي

    + +
      +
    1. + 1 +

      افهم نفسك

      +

      راجع ميولك، الأنشطة التي تستمتع بها، قدراتك التي تريد تطويرها، قيمك وأهدافك، وما تعرفه فعلًا عن نفسك من التجربة.

      +
    2. +
    3. + 2 +

      افهم الخيارات

      +

      اقرأ طبيعة الدراسة، المواد والمهارات المطلوبة، الأنشطة داخل التخصص، والمسارات المهنية بدل الاعتماد على اسم التخصص فقط.

      +
    4. +
    5. + 3 +

      قارن بوعي

      +

      ابحث عن نقاط التقارب والتعارض، ثم تحقق من الخيارات الأقرب إليك بقراءة أعمق أو تجربة صغيرة أو سؤال شخص لديه خبرة حقيقية.

      +
    6. +
    +
    + +
    +

    ما العوامل التي أضعها أمامي قبل اختيار التخصص؟

    + +
    +
    +

    ميولك

    +

    ما الأنشطة التي تستمتع بها وتريد تكرارها؟ الميول نقطة بداية، وليست حكمًا نهائيًا على مستقبلك.

    +
    +
    +

    قدراتك واستعدادك للتعلم

    +

    لا تسأل فقط: «هل أنا جيد الآن؟» بل أيضًا: «هل أنا مستعد لتعلم المهارات التي يتطلبها هذا التخصص؟»

    +
    +
    +

    طبيعة الدراسة

    +

    افهم كيف ستتعلم فعليًا: قراءة، تحليل، مختبر، تدريب عملي، تصميم، تواصل، مشاريع أو عمل ميداني.

    +
    +
    +

    قيمك وأهدافك

    +

    ما الذي تريد أن يكون مهمًا في حياتك ودراستك وعملك؟ لا تجعل قرارك مجرد استجابة لرأي الآخرين.

    +
    +
    +

    الظروف الواقعية

    +

    ضع في الحسبان الجامعة المتاحة، التكلفة، المكان، متطلبات القبول، الوقت والقيود التي تحتاج إلى خطة أو بديل.

    +
    +
    +

    المعلومة والتجربة

    +

    كلما عرفت التخصص من مصادر موثوقة وجرّبت نشاطًا قريبًا منه، أصبح قرارك مبنيًا على معرفة أكثر وتخمين أقل.

    +
    +
    +
    + +
    +
    +

    محتار أي تخصص أدخل؟ لا تبدأ بهذه الأخطاء

    +
      +
    • اختيار التخصص لأن اسمه مشهور فقط.
    • +
    • الاعتماد على الراتب أو المكانة وحدهما.
    • +
    • ترك شخص آخر يقرر بدلًا عنك دون فهمك لطبيعة الاختيار.
    • +
    • اعتبار نتيجة اختبار واحد إجابة نهائية لا تحتاج إلى تحقق.
    • +
    • رفض تخصص قبل معرفة مواده وأنشطته ومساراته الفعلية.
    • +
    +
    + +
    +

    هل اختبار الميول يكفي لاختيار التخصص؟

    +

    + لا. اختبار الميول يساعدك على فهم المجالات والأنشطة التي تجذبك أكثر، لكنه لا يقيس كل شيء ولا يقرر عنك. استخدم نتيجته مع فهم طبيعة الدراسة وقدراتك وقيمك وظروفك الواقعية. +

    + + تعرّف إلى اختبار الميول المهنية + +
    +
    + +
    +

    خطوتك التالية: حوّل الحيرة إلى مقارنة

    +

    + اختر تخصصين أو ثلاثة يبدون قريبين منك، واقرأ لكل واحد طبيعة الدراسة والمهارات والأنشطة والمسارات المهنية. سجّل ما يجذبك وما يقلقك، ثم ارجع إلى ميولك وظروفك بدل الاختيار بالانطباع الأول. +

    + +
    +
    +@endsection diff --git a/resources/views/seo/json-ld.blade.php b/resources/views/seo/json-ld.blade.php index 3d68b37..d4d47c2 100644 --- a/resources/views/seo/json-ld.blade.php +++ b/resources/views/seo/json-ld.blade.php @@ -79,6 +79,58 @@ ]; } + if (request()->routeIs('major-choice.index')) { + $seoBreadcrumbId = $seoCanonical.'#breadcrumb'; + $seoGraph[2]['breadcrumb'] = [ + '@id' => $seoBreadcrumbId, + ]; + + $seoGraph[] = [ + '@type' => 'BreadcrumbList', + '@id' => $seoBreadcrumbId, + 'itemListElement' => [ + [ + '@type' => 'ListItem', + 'position' => 1, + 'name' => 'الرئيسية', + 'item' => $seoHomeUrl, + ], + [ + '@type' => 'ListItem', + 'position' => 2, + 'name' => 'كيف أختار تخصصي الجامعي؟', + 'item' => $seoCanonical, + ], + ], + ]; + } + + if (request()->routeIs('scientific-foundation.index')) { + $seoBreadcrumbId = $seoCanonical.'#breadcrumb'; + $seoGraph[2]['breadcrumb'] = [ + '@id' => $seoBreadcrumbId, + ]; + + $seoGraph[] = [ + '@type' => 'BreadcrumbList', + '@id' => $seoBreadcrumbId, + 'itemListElement' => [ + [ + '@type' => 'ListItem', + 'position' => 1, + 'name' => 'الرئيسية', + 'item' => $seoHomeUrl, + ], + [ + '@type' => 'ListItem', + 'position' => 2, + 'name' => 'الأساس العلمي لمسارك', + 'item' => $seoCanonical, + ], + ], + ]; + } + if (request()->routeIs('specializations.index')) { $seoBreadcrumbId = $seoCanonical.'#breadcrumb'; $seoGraph[2]['breadcrumb'] = [ diff --git a/resources/views/seo/scientific-foundation.blade.php b/resources/views/seo/scientific-foundation.blade.php new file mode 100644 index 0000000..b8ecd69 --- /dev/null +++ b/resources/views/seo/scientific-foundation.blade.php @@ -0,0 +1,165 @@ +@extends('layouts.app') + +@section('title', 'الأساس العلمي لاختبار الميول المهنية') +@section('seo_description', 'تعرّف إلى الأساس العلمي لمسارك: نموذج هولاند RIASEC لقياس الميول المهنية، والأطر المساندة لفهم الثقة والقيم والعوائق واتخاذ قرار تخصص أكثر وعيًا.') + +@section('content') +
    + + +
    +

    كيف يعمل مسارك علميًا؟

    +

    + الأساس العلمي لاختبار الميول المهنية في مسارك +

    +

    + يبني مسارك التقييم على قياس الميول المهنية بوصفها انجذابًا إلى أنشطة وموضوعات وبيئات تعليمية ومهنية، ثم يستخدم أطرًا مساندة لفهم الثقة والخبرة والقيم والعوائق. النتيجة أداة للاستكشاف وليست تشخيصًا للشخصية أو حكمًا نهائيًا على التخصص. +

    + + +
    + +
    +

    ما الذي يقيسه مسارك فعلًا؟

    +

    + البناء الأساسي في التقييم هو الميول المهنية: ما الأنشطة أو الموضوعات أو البيئات التي تميل إلى فعلها أو استكشافها أكثر. لا نفترض أن الميل يساوي القدرة، ولا نخلط رغبة الطالب بما تريده الأسرة أو بما يبدو أعلى مكانة. +

    + +
    +
    +

    نقيس بصورة أساسية

    +

    تفضيل الأنشطة والموضوعات والبيئات المرتبطة بمجالات الميول المهنية.

    +
    +
    +

    ونفصل عنه

    +

    الخبرة السابقة، والثقة في التعلم، والقيم، والتكلفة المتوقعة، والدعم والعوائق؛ لأنها تساعد على القرار لكنها ليست «درجة ميل» واحدة.

    +
    +
    +
    + +
    +
    +

    القياس الأساسي

    +

    نموذج هولاند RIASEC

    +

    + يقسم RIASEC الميول والبيئات المهنية إلى ستة مجالات واسعة. في مسارك يحصل الطالب على درجات منفصلة لهذه المجالات، ولا يُحصر في «نوع شخصية» واحد ثابت. +

    +
    + +
    +
    +

    عملي/تطبيقي

    +

    الانجذاب إلى الأدوات والمواد والبناء والتنفيذ والعمل الميداني.

    +
    +
    +

    بحثي/تحليلي

    +

    الانجذاب إلى الاستقصاء والتفسير والتحليل وحل المسائل.

    +
    +
    +

    فني/إبداعي

    +

    الانجذاب إلى التعبير والتصميم وصنع بدائل بصرية أو مفاهيمية.

    +
    +
    +

    اجتماعي/مساند

    +

    الانجذاب إلى التعليم والرعاية والإرشاد والتواصل المساعد.

    +
    +
    +

    مبادر/تأثيري

    +

    الانجذاب إلى القيادة والإقناع والتفاوض وتحريك العمل.

    +
    +
    +

    تنظيمي/إجرائي

    +

    الانجذاب إلى الدقة والبيانات والسجلات والإجراءات والتنظيم.

    +
    +
    +
    + +
    +
    +

    ما الأطر المساندة للقرار؟

    +

    + لا يحوّل مسارك كل نظرية إلى اختبار مستقل. بعض الأطر تستخدم لفهم النتيجة أو تنظيم الأسئلة التي تساعد الطالب على التفكير بعد ظهور ميوله. +

    +
    + +
    +

    SCCT — الثقة والخبرة والدعم والعوائق

    +

    + تساعد النظرية المعرفية الاجتماعية للمسار المهني على التمييز بين ما يميل إليه الطالب وبين ثقته الحالية في قدرته على تعلمه، مع الانتباه إلى الخبرة السابقة والدعم والعوائق. هذه العوامل لا تُدمج تلقائيًا في درجة RIASEC. +

    +
    + +
    +

    SEVT — القيمة والتكلفة والسياق

    +

    + يساعد نموذج التوقع والقيمة على التفكير في المتعة والأهمية والمنفعة والتكلفة المتوقعة. يستخدم مسارك هذه الأفكار في المقارنة الواعية، لا لتحويل الظروف المالية أو الاجتماعية إلى «ميل». +

    +
    +
    + +
    +

    كيف تتحول النظريات إلى رحلة عملية؟

    +

    + يستخدم مسارك هيكلًا بسيطًا للقرار: افهم نفسك من خلال ميولك وخبرتك وقيمك وقيودك، ثم افهم الخيارات من خلال طبيعة الدراسة والمهارات والمسارات، ثم قارن بوعي وابحث عن التقارب والتعارض وتحقق بنفسك قبل اتخاذ القرار. +

    + + اقرأ دليل اختيار التخصص الجامعي + +
    + +
    +

    ما الذي لا يدّعي مسارك قياسه أو ضمانه؟

    + +
    +

    لا يقيس الذكاء أو القدرة العقلية العامة.

    +

    لا يحدد الأهلية الرسمية للقبول الجامعي.

    +

    لا يشخّص الصحة النفسية أو الشخصية الشاملة.

    +

    لا يضمن النجاح الدراسي أو الوظيفة أو الدخل.

    +

    لا يقرر أن هناك «تخصصًا صحيحًا وحيدًا» للطالب.

    +

    ولا يحوّل النتيجة إلى حكم نهائي غير قابل للمراجعة.

    +

    تُفسَّر درجات الميول داخل ملف الطالب نفسه، ولا تُقدَّم الدرجات بوصفها مقارنة بمعيار وطني يمني غير متاح للمشروع.

    +
    +
    + +
    +

    المراجع العلمية الأساسية

    +

    + الإطار العلمي الكامل للمشروع يوثّق المراجع وقرارات القياس وحدود الاستخدام. من المراجع الأساسية المستخدمة في بناء الإطار: +

    + + +
    +
    +@endsection diff --git a/resources/views/specializations/index.blade.php b/resources/views/specializations/index.blade.php index 44146ad..bc7c443 100644 --- a/resources/views/specializations/index.blade.php +++ b/resources/views/specializations/index.blade.php @@ -5,6 +5,12 @@ @section('content')
    + +
    + + @if (!empty($specializations) && count($specializations) > 0)
    @foreach ($specializations as $specialization) diff --git a/resources/views/specializations/show.blade.php b/resources/views/specializations/show.blade.php index 50972cd..7d03b4d 100644 --- a/resources/views/specializations/show.blade.php +++ b/resources/views/specializations/show.blade.php @@ -17,6 +17,14 @@ @endphp
    @endif + + @if (!empty($specId))
    diff --git a/routes/web.php b/routes/web.php index bf87750..4718f4a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,6 +16,12 @@ Route::view('/career-interests-test', 'seo.career-interests') ->name('career-interests.index'); +Route::view('/how-to-choose-university-major', 'seo.choose-major') + ->name('major-choice.index'); + +Route::view('/scientific-foundation', 'seo.scientific-foundation') + ->name('scientific-foundation.index'); + Route::get('/sitemap.xml', SitemapController::class)->name('sitemap'); Route::get('/specializations', [SpecializationController::class, 'index']) diff --git a/tests/Feature/SeoHeadTest.php b/tests/Feature/SeoHeadTest.php index e4d4731..5424dd0 100644 --- a/tests/Feature/SeoHeadTest.php +++ b/tests/Feature/SeoHeadTest.php @@ -213,6 +213,8 @@ public function test_public_pages_emit_one_absolute_social_image_contract(): voi $urls = [ route('home'), route('career-interests.index'), + route('major-choice.index'), + route('scientific-foundation.index'), route('specializations.index'), route('specializations.show', 'human_medicine'), ]; diff --git a/tests/Feature/SeoIndexingFoundationTest.php b/tests/Feature/SeoIndexingFoundationTest.php index 60e974f..6ae79d9 100644 --- a/tests/Feature/SeoIndexingFoundationTest.php +++ b/tests/Feature/SeoIndexingFoundationTest.php @@ -41,11 +41,13 @@ public function test_sitemap_contains_only_the_public_indexable_surface(): void preg_match_all('/(.*?)<\/loc>/', $xml, $matches); $locations = $matches[1] ?? []; - $this->assertCount(13, $locations); - $this->assertCount(13, array_unique($locations)); + $this->assertCount(15, $locations); + $this->assertCount(15, array_unique($locations)); $this->assertContains(route('home'), $locations); $this->assertContains(route('career-interests.index'), $locations); + $this->assertContains(route('major-choice.index'), $locations); + $this->assertContains(route('scientific-foundation.index'), $locations); $this->assertContains(route('specializations.index'), $locations); $this->assertContains(route('specializations.show', 'computer_science'), $locations); $this->assertContains(route('specializations.show', 'human_medicine'), $locations); diff --git a/tests/Feature/SeoInternalLinkingTest.php b/tests/Feature/SeoInternalLinkingTest.php new file mode 100644 index 0000000..d760832 --- /dev/null +++ b/tests/Feature/SeoInternalLinkingTest.php @@ -0,0 +1,133 @@ +get(route('home')); + + $response->assertOk(); + + $html = $response->getContent(); + + $this->assertIsString($html); + + $context = $this->extractNav($html, 'مسارات إرشادية مرتبطة'); + + $this->assertStringContainsString('href="'.route('career-interests.index').'"', $context); + $this->assertStringContainsString('href="'.route('major-choice.index').'"', $context); + $this->assertStringContainsString('href="'.route('scientific-foundation.index').'"', $context); + } + + public function test_specialization_catalog_has_visible_breadcrumb_and_contextual_guidance_links(): void + { + $response = $this->get(route('specializations.index')); + + $response->assertOk(); + + $html = $response->getContent(); + + $this->assertIsString($html); + + $breadcrumb = $this->extractNav($html, 'مسار الصفحة'); + $context = $this->extractNav($html, 'مسارات إرشادية مرتبطة'); + + $this->assertStringContainsString('href="'.route('home').'"', $breadcrumb); + $this->assertStringContainsString('دليل التخصصات الجامعية', $breadcrumb); + + $this->assertStringContainsString('href="'.route('major-choice.index').'"', $context); + $this->assertStringContainsString('href="'.route('career-interests.index').'"', $context); + $this->assertStringContainsString('href="'.route('scientific-foundation.index').'"', $context); + } + + public function test_specialization_detail_visible_breadcrumb_matches_the_public_hierarchy(): void + { + $response = $this->get(route('specializations.show', 'human_medicine')); + + $response->assertOk(); + + $html = $response->getContent(); + + $this->assertIsString($html); + + $breadcrumb = $this->extractNav($html, 'مسار الصفحة'); + $context = $this->extractNav($html, 'مسارات إرشادية مرتبطة'); + + $this->assertStringContainsString('href="'.route('home').'"', $breadcrumb); + $this->assertStringContainsString('href="'.route('specializations.index').'"', $breadcrumb); + $this->assertStringContainsString('الطب البشري', $breadcrumb); + + $this->assertStringContainsString('href="'.route('major-choice.index').'"', $context); + $this->assertStringContainsString('href="'.route('career-interests.index').'"', $context); + } + + public function test_all_public_guides_and_catalog_pages_expose_a_visible_breadcrumb(): void + { + $urls = [ + route('career-interests.index'), + route('major-choice.index'), + route('scientific-foundation.index'), + route('specializations.index'), + route('specializations.show', 'human_medicine'), + ]; + + foreach ($urls as $url) { + $response = $this->get($url); + + $response->assertOk(); + + $html = $response->getContent(); + + $this->assertIsString($html); + $this->assertSame( + 1, + substr_count($html, 'aria-label="مسار الصفحة"'), + $url + ); + } + } + + public function test_footer_lists_each_public_guidance_destination_once(): void + { + $response = $this->get(route('home')); + + $response->assertOk(); + + $html = $response->getContent(); + + $this->assertIsString($html); + + $footer = $this->extractNav($html, 'روابط التذييل'); + + foreach ([ + route('home'), + route('career-interests.index'), + route('major-choice.index'), + route('scientific-foundation.index'), + route('specializations.index'), + ] as $url) { + $this->assertSame( + 1, + substr_count($footer, 'href="'.$url.'"'), + $url + ); + } + } + + private function extractNav(string $html, string $ariaLabel): string + { + preg_match( + '/]*aria-label="'.preg_quote($ariaLabel, '/').'"[^>]*>(.*?)<\/nav>/s', + $html, + $matches + ); + + $this->assertArrayHasKey(1, $matches, 'Expected navigation: '.$ariaLabel); + + return $matches[1]; + } +} diff --git a/tests/Feature/SeoMajorChoiceGuideTest.php b/tests/Feature/SeoMajorChoiceGuideTest.php new file mode 100644 index 0000000..a4f72bb --- /dev/null +++ b/tests/Feature/SeoMajorChoiceGuideTest.php @@ -0,0 +1,87 @@ +get(route('major-choice.index')); + + $response + ->assertOk() + ->assertSee( + 'كيف أختار تخصصي الجامعي؟ | دليل عملي للطلاب | مسارك', + false + ) + ->assertSee('كيف أختار تخصصي الجامعي؟') + ->assertSee('افهم نفسك') + ->assertSee('افهم الخيارات') + ->assertSee('قارن بوعي') + ->assertSee('محتار أي تخصص أدخل؟') + ->assertDontSee('', false) + ->assertSee( + '', + false + ) + ->assertSee( + '', + false + ); + } + + public function test_major_choice_guide_links_to_interest_exploration_and_specializations(): void + { + $response = $this->get(route('major-choice.index')); + + $response + ->assertOk() + ->assertSee('href="'.route('career-interests.index').'"', false) + ->assertSee('href="'.route('specializations.index').'"', false); + } + + public function test_major_choice_guide_emits_webpage_image_and_two_level_breadcrumb_json_ld(): void + { + $response = $this->get(route('major-choice.index')); + + $response->assertOk(); + + $html = $response->getContent(); + + $this->assertIsString($html); + + preg_match( + '/