{"checkpoints":{"s-rn-1":[{"q":"<p>يسأل مدير منتج هل ستبدو قائمة Trailhead «أصلية» أم «كأنها موقع ويب داخل تطبيق». ما الإجابة الدقيقة؟</p>","options":[{"text":"ستبدو كموقع ويب، لأن React Native يرسم HTML داخل WebView.","why":"لا يوجد WebView ولا HTML في الأمر. يرسم React Native عناصر المنصّة."},{"text":"يعتمد ذلك على تفعيل الـ New Architecture في app.json.","why":"الـ New Architecture هي المعمارية الوحيدة في React Native الحالي؛ فلا شيء يُفعَّل، وهي لا تحدّد هل الـ views أصلية."},{"text":"ستستخدم views حقيقية من المنصّة، فيتصرّف التمرير وتحديد النص وحقول الإدخال كما في التطبيقات الأخرى على الهاتف.","why":"صحيح. الـ components الخاصة بك تصف الواجهة؛ وينشئ React Native الـ native views ويحدّثها انطلاقًا من هذا الوصف."},{"text":"ستبدو أصلية على iOS وكموقع ويب على Android.","why":"المنصّتان تحصلان على native views. وAndroid لا يُرسم بطريقة مختلفة."}],"answer":2},{"q":"<p>ثبّتّ <code dir=\"ltr\">react-native-keyboard-controller</code> في Trailhead، وحفظت، فانهار التطبيق في Expo Go برسالة تقول إن وحدة أصلية غير موجودة. ما الذي يحدث؟</p>","options":[{"text":"تحتاج المكتبة إلى إيقاف Fast Refresh.","why":"يستبدل Fast Refresh كود JavaScript فقط. والقطعة الناقصة كود أصلي."},{"text":"تحتاج إلى تشغيل <code dir=\"ltr\">npx expo start --clear</code> لإعادة بناء الكود الأصلي.","why":"مسح ذاكرة Metro المؤقتة يعيد ضبط تجميع JavaScript؛ ولا يترجم كودًا أصليًا أبدًا."},{"text":"لا يدعم Expo Go إلا المكتبات المكتوبة بـ TypeScript.","why":"اللغة ليست المشكلة. لا يستطيع Expo Go تشغيل إلا الكود الأصلي الذي تُرجم داخل Expo Go نفسه."},{"text":"تحتوي المكتبة على كود أصلي لا يتضمّنه Expo Go، فتحتاج إلى development build لتطبيقك الخاص.","why":"صحيح. يترجم الـ development build الوحدات الأصلية التي يستخدمها مشروعك، مع أدوات التطوير نفسها التي في Expo Go."}],"answer":3},{"q":"<p>أيّ مقطع يعرض إحصاءات الرحلة دون أن ينهار حين تكون <code dir=\"ltr\">hike.elevationM</code> صفرًا؟</p>","options":[{"text":"<code dir=\"ltr\">&lt;View&gt;&lt;Text&gt;{hike.distanceKm} km&lt;/Text&gt;{hike.elevationM &gt; 0 &amp;&amp; &lt;Text&gt;{hike.elevationM} m up&lt;/Text&gt;}&lt;/View&gt;</code>","why":"صحيح. كل نص يقع داخل Text، والشرط قيمة منطقية حقيقية، فلا يرسم الصفر شيئًا."},{"text":"<code dir=\"ltr\">&lt;View&gt;&lt;Text&gt;{hike.distanceKm} km&lt;/Text&gt;{hike.elevationM &amp;&amp; &lt;Text&gt;{hike.elevationM} m up&lt;/Text&gt;}&lt;/View&gt;</code>","why":"حين يكون الارتفاع صفرًا، تُقيَّم <code dir=\"ltr\">0 &amp;&amp; …</code> إلى 0، ويحاول React Native رسمها نصًّا مباشرة داخل الـ View."},{"text":"<code dir=\"ltr\">&lt;View&gt;{hike.distanceKm} km&lt;Text&gt;{hike.elevationM} m up&lt;/Text&gt;&lt;/View&gt;</code>","why":"المسافة و&quot;km&quot; نصّ خام داخل View، وهذا يرمي خطأ مع كل رحلة."}],"answer":0},{"q":"<p>يقول مختبِر إن زرّ Save في نموذج التسجيل السريع في Trailhead «أحيانًا لا يلاحظ النقرات» أثناء المشي. الزر Pressable بارتفاع 28 نقطة. ما الذي يجب أن تغيّره أولًا؟</p>","options":[{"text":"استبدل Pressable بالـ component المدمج Button.","why":"لا يمكن تنسيق Button ليناسب التصميم، ولا يحلّ مشكلة الحجم."},{"text":"اجعل منطقة اللمس بارتفاع 44 نقطة على الأقل، عبر الحشو أو <code dir=\"ltr\">hitSlop</code>، وأعطِ استجابة بصرية عند الضغط.","why":"صحيح. الأهداف الصغيرة صعبة الإصابة أثناء الحركة؛ والمنطقة الأكبر مع الاستجابة الفورية تصلحان نصفَي الشكوى."},{"text":"انتقل إلى <code dir=\"ltr\">onLongPress</code> لتُتجاهَل النقرات العرضية.","why":"هذا يجعل كل حفظ أبطأ، ولا يساعد المستخدمين على إصابة الهدف."}],"answer":1},{"q":"<p>يخزّن Trailhead حقل المسافة باستخدام <code dir=\"ltr\">const [km, setKm] = useState(0)</code> و<code dir=\"ltr\">onChangeText={(t) =&gt; setKm(Number(t))}</code>. لا يستطيع المستخدمون كتابة “8.4”. لماذا؟</p>","options":[{"text":"<code dir=\"ltr\">onChangeText</code> لا يُطلَق مع النقاط العشرية.","why":"يُطلَق مع كل تغيير، بما في ذلك النقطة."},{"text":"يجب ضبط <code dir=\"ltr\">keyboardType</code> على <code dir=\"ltr\">number</code> لتعمل الأعداد العشرية.","why":"هذه القيمة غير موجودة؛ والمشكلة في الـ state لا في لوحة المفاتيح."},{"text":"كتابة “8.” تصبح <code dir=\"ltr\">Number(&quot;8.&quot;)</code>، أي 8، فتختفي النقطة من الحقل المضبوط قبل أن يستطيع المستخدم إضافة الـ 4.","why":"صحيح. احتفظ بالنص الخام في الـ state، وحلّله حين تحتاج إلى الرقم."},{"text":"قيم TextInput محصورة في الأعداد الصحيحة على Android.","why":"قيم TextInput نصوص دائمًا، على المنصّتين."}],"answer":2}],"s-rn-2":[{"q":"<p>يكتب زميل <code dir=\"ltr\">style={{ padding: '12px 16px', fontSize: '15px' }}</code> على بطاقة في Trailhead. ماذا يجب أن يقول تعليق المراجعة؟</p>","options":[{"text":"استخدم وحدات <code dir=\"ltr\">rem</code> ليتكيّف النص مع إعدادات إمكانية الوصول.","why":"لا توجد وحدات rem في React Native. والنص يتكيّف أصلًا مع إعدادات الخط لدى المستخدم."},{"text":"انقل هذه القيم إلى ملف CSS يُستورد في أعلى الـ component.","why":"لا يحمّل React Native ملفات CSS؛ التنسيقات كائنات JavaScript."},{"text":"لا بأس؛ يحوّل React Native نصوص CSS تلقائيًا.","why":"لا يفعل. النصوص متعدّدة القيم ووحدات px إما أخطاء أو تُتجاهَل."},{"text":"استخدم أرقامًا بلا وحدات ومفاتيح مفصّلة: <code dir=\"ltr\">paddingVertical: 12, paddingHorizontal: 16, fontSize: 15</code>.","why":"صحيح. رقم واحد بلا وحدة لكل خاصية، يُقاس بالنقاط."}],"answer":3},{"q":"<p>شاشة تعرض ScrollView داخل View. محتوى الـ ScrollView مقطوع في الأسفل ولا يتمرّر. وعلى الـ ScrollView <code dir=\"ltr\">style={{ flex: 1 }}</code>. ما الناقص؟</p>","options":[{"text":"<code dir=\"ltr\">overflow: 'scroll'</code> على الـ ScrollView.","why":"يتمرّر ScrollView وحده؛ وتنسيقات overflow لا تنشئ تمريرًا."},{"text":"<code dir=\"ltr\">contentContainerStyle={{ flex: 1 }}</code>","why":"هذا يمدّ المحتوى إلى ارتفاع منطقة العرض، وكثيرًا ما يوقف التمرير بدلًا من إصلاحه."},{"text":"<code dir=\"ltr\">flex: 1</code> على الـ View الأب، ليرث الـ ScrollView ارتفاعًا محدودًا يتمرّر داخله.","why":"صحيح. دون أب محدود، يكبر الـ ScrollView بحجم محتواه فتقطعه الشاشة."},{"text":"<code dir=\"ltr\">horizontal={false}</code>","why":"هذه هي القيمة الافتراضية أصلًا."}],"answer":2},{"q":"<p>شاشة الإعدادات في Trailhead ليس لها ترويسة، وعلى هاتف Android حديث يقع العنوان تحت ساعة شريط الحالة. ما الإصلاح الصحيح؟</p>","options":[{"text":"أضف حشوًا للمحتوى بقيمة <code dir=\"ltr\">useSafeAreaInsets().top</code> من react-native-safe-area-context.","why":"صحيح. يرسم Android التطبيقات من الحافة إلى الحافة، فيجب على الشاشات التي ليس لها ترويسة أن تطبّق الهامش العلوي بنفسها."},{"text":"استخدم <code dir=\"ltr\">SafeAreaView</code> من react-native.","why":"إنه مُهمَل، ولم يطبّق الحشو يومًا إلا على iOS."},{"text":"أضف <code dir=\"ltr\">marginTop: 24</code>، وهو ارتفاع شريط الحالة في Android.","why":"ارتفاعات شريط الحالة تختلف بحسب الجهاز والإعداد؛ والقيمة الثابتة خاطئة على هواتف كثيرة."},{"text":"أخفِ شريط الحالة على Android.","why":"يفقد المستخدمون الساعة ومستوى البطارية، وتبقى مناطق الإيماءات في الأسفل متداخلة."}],"answer":0},{"q":"<p>لكل صفّ في قائمة Trailhead مفتاح «موسَّع» مخزّن في <code dir=\"ltr\">useState</code> داخل الصف. أيّ خطأ سيبلّغ عنه المستخدمون في النهاية؟</p>","options":[{"text":"تتوسّع الصفوف على iOS لا على Android.","why":"الـ state تعمل بالطريقة نفسها على المنصّتين؛ ولا شيء هنا خاص بمنصّة."},{"text":"تنطوي الصفوف الموسَّعة وحدها بعد التمرير بعيدًا ثم العودة.","why":"صحيح. يُزيل FlatList الصفوف البعيدة لتوفير الذاكرة، وتضيع الـ state المحلية معها."},{"text":"ينهار التطبيق حين يُوسَّع صفّان.","why":"يمكن لعدّة صفوف أن تحمل state في الوقت نفسه دون مشكلات."}],"answer":1},{"q":"<p>أيّ تغيير يحسّن أكثر من غيره FlatList فيه 800 رحلة، يعرض كل صف فيها صورة بعرض 4000 بكسل مصغّرة إلى صورة مصغّرة بعرض 56 نقطة؟</p>","options":[{"text":"زِد <code dir=\"ltr\">windowSize</code> لإبقاء صفوف أكثر مركّبة.","why":"صفوف مركّبة أكثر تعني صورًا بحجمها الكامل أكثر في الذاكرة، وهذا يزيد الأمر سوءًا."},{"text":"غلّف الـ FlatList بـ ScrollView ليُرسم مرة واحدة.","why":"هذا يعطّل الـ virtualization ويرسم كل صفّ بصورته الضخمة."},{"text":"استبدل FlatList بـ SectionList.","why":"SectionList قائمة افتراضية (virtualized) أيضًا؛ والصور ستكلّف الشيء نفسه."},{"text":"حمّل نسخًا مصغّرة من الصور للقائمة، ويُفضَّل مع component صور يدعم التخزين المؤقت مثل expo-image.","why":"صحيح. فكّ ترميز صور ضخمة لصور مصغّرة صغيرة يهدر الذاكرة والوقت في كل صفّ مرئي."}],"answer":3}],"s-rn-3":[{"q":"<p>يحتاج Trailhead إلى شاشة على <code dir=\"ltr\">/hikes/42/photos</code>. أيّ ملف ينشئها؟</p>","options":[{"text":"<code dir=\"ltr\">src/app/hikes/photos/[id].tsx</code>","why":"هذا يخدم <code dir=\"ltr\">/hikes/photos/42</code>، والأجزاء بترتيب خاطئ."},{"text":"<code dir=\"ltr\">src/app/(hikes)/[id]/photos.tsx</code>","why":"المجموعة بين قوسين لا تضيف جزءًا إلى الـ URL، فهذا يخدم <code dir=\"ltr\">/42/photos</code>."},{"text":"<code dir=\"ltr\">src/app/hikes/[id]/photos.tsx</code>","why":"صحيح. كل مجلّد جزء، و<code dir=\"ltr\">[id]</code> يلتقط الـ 42."},{"text":"<code dir=\"ltr\">src/components/HikePhotos.tsx</code>","why":"الملفات خارج <code dir=\"ltr\">src/app</code> ليست مسارات أبدًا."}],"answer":2},{"q":"<p>في شاشة التفاصيل، <code dir=\"ltr\">const { id } = useLocalSearchParams&lt;{ id: string }&gt;()</code> ثم يُستدعى <code dir=\"ltr\">useHike(Number(id))</code>. يصل رابط إلى <code dir=\"ltr\">/hikes/abc</code>. ماذا يجب أن تعرض الشاشة؟</p>","options":[{"text":"شاشة خطأ حمراء، لأن Number(“abc”) يرمي خطأ.","why":"Number(“abc”) لا يرمي خطأ؛ بل يُرجع NaN، ولا تطابقه أي رحلة."},{"text":"حالة ودودة «الرحلة غير موجودة»، لأن البحث لا يُرجع شيئًا والشاشة تتعامل مع هذه الحالة.","why":"صحيح. الـ params نصوص غير موثوقة، فكل شاشة لها params تحتاج إلى حالة «غير موجود»."},{"text":"أول رحلة في القائمة، كحلّ احتياطي.","why":"عرض الرحلة الخطأ أسوأ من قول إنها غير موجودة."},{"text":"لا شيء؛ يرفض Expo Router مطابقة المعرّفات غير الرقمية.","why":"الجزء الديناميكي يطابق أي قيمة؛ والتحقّق مهمّتك أنت."}],"answer":1},{"q":"<p>بعد حذف رحلة في شاشة تفاصيلها، يستدعي الكود <code dir=\"ltr\">router.push('/')</code>. ما المشكلة التي ينشئها هذا؟</p>","options":[{"text":"تُضاف نسخة ثانية من السجلّ فوق الأولى، فتعيد إيماءة الرجوع المستخدمَ إلى شاشة الرحلة المحذوفة.","why":"صحيح. استخدم <code dir=\"ltr\">router.back()</code> (أو <code dir=\"ltr\">replace</code>) لتغادر الشاشة المحذوفة الـ stack."},{"text":"يختفي شريط الـ tabs نهائيًا.","why":"إضافة مسار لا تُزيل layout الـ tabs."},{"text":"يعمل reducer الرحلات مرتين.","why":"التنقّل لا يرسل actions؛ والتكرار في الـ stack لا في الـ state."}],"answer":0},{"q":"<p>أيّ عبارة عن <code dir=\"ltr\">HikesProvider</code> في Trailhead، مع اثنين من الـ contexts منفصلَين للـ state والـ dispatch، صحيحة؟</p>","options":[{"text":"الـ components التي تقرأ الرحلات لا يُعاد رسمها أبدًا حين تتغيّر الرحلات.","why":"يُعاد رسمها؛ وهكذا تعرض القائمة الجديدة."},{"text":"يجعل الـ reducer يعمل على thread في الخلفية.","why":"الـ reducers تعمل على الـ JS thread مثل كل كود React."},{"text":"يُلغي الحاجة إلى إرجاع مصفوفات جديدة من الـ reducer.","why":"عدم التعديل المباشر ما زال مطلوبًا؛ فـ React تقارن المراجع."},{"text":"زرّ حذف لا يستخدم إلا dispatch لا يُعاد رسمه حين تتغيّر القائمة، لأن context الـ dispatch لا يتغيّر أبدًا.","why":"صحيح. <code dir=\"ltr\">dispatch</code> ثابت، فالـ components التي لا تقرأ غيره تتخطّى تحديثات القائمة."}],"answer":3},{"q":"<p>على iOS، يضطر مستخدمو نموذج إضافة رحلة في Trailhead إلى النقر على Save مرتين كلما كانت لوحة المفاتيح مفتوحة. أيّ prop يصلح ذلك؟</p>","options":[{"text":"<code dir=\"ltr\">autoFocus</code> على زرّ Save.","why":"الأزرار لا تأخذ التركيز بهذه الطريقة؛ والنقرة الأولى ستظل تُبتلع."},{"text":"<code dir=\"ltr\">behavior=&quot;height&quot;</code> على الـ KeyboardAvoidingView.","why":"هذا يغيّر طريقة تجنّب التخطيط للوحة المفاتيح، لا طريقة معالجة النقرات."},{"text":"<code dir=\"ltr\">keyboardShouldPersistTaps=&quot;handled&quot;</code> على الـ ScrollView الذي يحوي النموذج.","why":"صحيح. عندها تمرّ النقرات على الأزرار بينما لوحة المفاتيح مفتوحة."},{"text":"<code dir=\"ltr\">submitBehavior=&quot;blurAndSubmit&quot;</code> على آخر حقل.","why":"هذا يؤثّر في مفتاح الإدخال، لا في النقرات على زرّ Save."}],"answer":2}],"s-rn-4":[{"q":"<p>تعرض شاشة الاستكشاف في Trailhead “Could not load trails” مع أن الـ API يستجيب. يسجّل الخادم 200. ويُظهر التصحيح أن <code dir=\"ltr\">res.ok</code> قيمتها true لكن القائمة فارغة. أيّ حالة كان يجب أن تعرضها الشاشة؟</p>","options":[{"text":"خطأ، لأن الاستجابة الفارغة فشل.","why":"الطلب نجح؛ لم يفشل شيء."},{"text":"تحميل، حتى يصل درب واحد على الأقل.","why":"انتهى الطلب؛ والبقاء في حالة التحميل سيدور إلى الأبد."},{"text":"خطأ، مع زرّ إعادة محاولة، لتجربة خادم آخر.","why":"إعادة طلب ناجح تُرجع القائمة الفارغة نفسها."},{"text":"نجاح بصفر عناصر، يُعرض حالةَ فراغ مثل “No trails within 50 km”.","why":"صحيح. الفراغ نتيجة ناجحة تستحق رسالة مصمّمة خاصة بها."}],"answer":3},{"q":"<p>أين يجب أن يحتفظ Trailhead بالوحدات التي اختارها المستخدم (km أو mi)؟</p>","options":[{"text":"في expo-secure-store، لأن كل إعداد بيانات شخصية.","why":"SecureStore للأسرار مثل الـ tokens؛ وتفضيل الوحدات ليس حسّاسًا."},{"text":"في AsyncStorage (أو expo-sqlite/kv-store)، ضمن قيمة JSON صغيرة للتفضيلات.","why":"صحيح. التفضيلات الصغيرة غير السرّية هي ما وُجد تخزين المفتاح-القيمة من أجله."},{"text":"في جدول SQLite جديد بصفّ واحد وعمود واحد.","why":"ينجح، لكنه أثقل من اللازم لتفضيل واحد."},{"text":"في متغيّر بيئة يبدأ بـ <code dir=\"ltr\">EXPO_PUBLIC_</code>.","why":"هذه ثابتة وقت البناء لكل المستخدمين؛ ولا تستطيع تخزين اختيار مستخدم."}],"answer":1},{"q":"<p>رفض مستخدم في Trailhead الموقع رفضًا دائمًا، ثم فعّله لاحقًا في الإعدادات، وعاد إلى التطبيق. ما زالت شاشة التتبّع تقول إن الموقع مُطفأ. ما الخطأ المرجّح؟</p>","options":[{"text":"تحقّقت الشاشة من الإذن مرة واحدة فقط عند تركيبها، بدلًا من التحقّق مجددًا حين استعادت التركيز.","why":"صحيح. أعِد التحقّق باستخدام <code dir=\"ltr\">getForegroundPermissionsAsync()</code> داخل <code dir=\"ltr\">useFocusEffect</code> أو حين يعود التطبيق إلى المقدّمة."},{"text":"يحتاج iOS إلى إعادة تثبيت التطبيق قبل أن يُطبَّق تغيير الإذن.","why":"تغييرات الأذونات في الإعدادات تُطبَّق فورًا على التطبيق المثبّت."},{"text":"يجب إعادة ضبط <code dir=\"ltr\">canAskAgain</code> على true في تخزين التطبيق.","why":"نظام التشغيل هو مالك هذه القيمة؛ وتطبيقك يقرؤها فقط."}],"answer":0},{"q":"<p>يضيف الإصدار الثالث من Trailhead عمود <code dir=\"ltr\">difficulty</code> إلى جدول الرحلات. يعدّل زميل كتلة الإصدار 0 في الـ migration لتتضمّن العمود الجديد. ماذا يحدث للمستخدمين الحاليين؟</p>","options":[{"text":"يحصلون على العمود الجديد تلقائيًا في التشغيل التالي.","why":"قاعدة بياناتهم تجاوزت الإصدار 0 أصلًا، فلا تعمل تلك الكتلة لديهم أبدًا."},{"text":"يُحذف سجلّ رحلاتهم ويُعاد إنشاؤه.","why":"لا شيء يحذفه؛ الكتلة المعدّلة ببساطة لا تعمل."},{"text":"لا يتغيّر شيء لديهم، فتفشل الاستعلامات التي تستخدم <code dir=\"ltr\">difficulty</code> بالخطأ “no such column”.","why":"صحيح. أضف خطوة جديدة مرقّمة (<code dir=\"ltr\">ALTER TABLE … ADD COLUMN</code>) بدلًا من تعديل خطوة أُطلقت."},{"text":"يرفض SQLite ملف قاعدة البيانات عند بدء التشغيل.","why":"الملف صالح؛ كل ما في الأمر أنه يفتقد العمود الجديد."}],"answer":2},{"q":"<p>تُظهر رحلة متتبَّعة 14 km لمشي يعرف المتنزّه أنه كان 9 km. وفي المسار تعرّجات حادّة كثيرة تحت غطاء الغابة. ما أفضل إصلاح؟</p>","options":[{"text":"انتقل إلى <code dir=\"ltr\">Accuracy.Lowest</code> لتوفير البطارية.","why":"الدقّة الأقل تجعل القراءات أسوأ لا أفضل."},{"text":"قرّب كل إحداثية إلى ثلاث منازل عشرية.","why":"هذا يثبّت النقاط على شبكة خشنة بنحو 100 m، فيضيف خطأه الخاص."},{"text":"تجاهل القراءات التي تتجاوز <code dir=\"ltr\">coords.accuracy</code> فيها حدًّا معيّنًا قبل إضافتها إلى المسار.","why":"صحيح. القراءات منخفضة الجودة تسبّب التعرّجات؛ وتصفيتها تُبقي المسافة صادقة."},{"text":"اضرب النتيجة في معامل تصحيح قدره 0.65.","why":"المعامل الثابت خاطئ لكل رحلة أخرى."}],"answer":2}],"s-rn-5":[{"q":"<p>يبدو Trailhead متثاقلًا. ويُظهر Performance Monitor (من قائمة التطوير) أن الـ UI thread يُسقط إطارات بينما الـ JS thread بخير. أيّ سبب يناسب؟</p>","options":[{"text":"component يُعاد رسمه مع كل ضغطة مفتاح.","why":"إعادة الرسم عمل JavaScript؛ وكانت ستظهر انخفاضًا في الـ JS thread."},{"text":"صور كبيرة جدًا يُفكّ ترميزها وتُرسم في views صغيرة، أو views متداخلة بعمق مع ظلال.","why":"صحيح. هذا عمل رسم أصلي، ويظهر على الـ UI thread."},{"text":"طلب شبكة بطيء.","why":"انتظار الشبكة لا يستهلك إطارات على أيٍّ من الـ threads."}],"answer":1},{"q":"<p>أيّ اختبار مكتوب بالطريقة التي توصي بها React Native Testing Library؟</p>","options":[{"text":"خذ snapshot للشجرة المرسومة وحدّث الـ snapshot كلما تغيّر.","why":"الـ snapshots تسجّل كل ما يُرسم، بما فيه الأخطاء، وتنكسر مع التغييرات غير الضارة."},{"text":"اكتب assertion بأن <code dir=\"ltr\">setDraft</code> الداخلي في الـ component استُدعي بكائن محدّد.","why":"هذا يختبر التنفيذ، فتكسره إعادة الهيكلة حتى حين يكون السلوك صحيحًا."},{"text":"اعثر على الحقل بمعرّف الاختبار <code dir=\"ltr\">input-2</code> واقرأ <code dir=\"ltr\">props.value</code> الخاصة به.","why":"ينجح، لكنه لا يعكس ما يراه المستخدمون ويفوّت مشكلات إمكانية الوصول."},{"text":"اكتب في الحقل الذي تسميته “Distance (km)”، واضغط زرّ “Save” بدوره، وتوقّع أن تستقبل <code dir=\"ltr\">onSave</code> القيمة 8.4.","why":"صحيح. يشغّل الـ component بالطريقة التي يستخدمه بها المستخدم (وقارئ الشاشة)."}],"answer":3},{"q":"<p>يعمل build المعاينة لديك، لكن الـ production build ينهار عند التشغيل بسبب متغيّر بيئة مفقود. ماذا يخبرك هذا؟</p>","options":[{"text":"يمكن أن تختلف builds الإنتاج عن builds التطوير والمعاينة؛ فثبّت build الإصدار الفعلي واختبره دائمًا قبل الإرسال.","why":"صحيح. يمكن أن تضبط الـ build profiles متغيّرات بيئة مختلفة، وbuilds الإصدار ليس فيها Metro تعود إليه."},{"text":"EAS Build غير موثوق ويجب أن تبني محليًا.","why":"البناء المحلي بالإعدادات نفسها سينهار بالطريقة نفسها."},{"text":"عدّلت المتاجر الملف التنفيذي أثناء الرفع.","why":"المتاجر لا تغيّر كود JavaScript الخاص بك ولا بيئته."}],"answer":0},{"q":"<p>نشرت إصلاح JavaScript باستخدام <code dir=\"ltr\">eas update --channel production</code>، لكن المستخدمين على builds مصنوعة من الـ profile المسمّى <code dir=\"ltr\">preview</code> لا يتلقّونه. لماذا؟</p>","options":[{"text":"تستغرق التحديثات أسبوعًا لتنتشر.","why":"تصبح التحديثات متاحة خلال دقائق؛ والمشكلة في الاستهداف."},{"text":"builds المعاينة لا تستطيع تلقّي التحديثات إطلاقًا.","why":"تستطيع، على قناتها الخاصة."},{"text":"builds المعاينة تستمع إلى قناة preview، فلا تحصل إلا على التحديثات المنشورة هناك.","why":"صحيح. الـ <code dir=\"ltr\">channel</code> الخاصة بكل build profile تحدّد أي التحديثات يتلقّاها."},{"text":"يجب رفع إصدار وقت التشغيل مع كل تحديث.","why":"رفعه سيجعل التحديث يتخطّى الـ builds الحالية، لا أن يصل إلى المزيد منها."}],"answer":2},{"q":"<p>يضيف Trailhead 1.3 وحدة أصلية. ما الطريقة الصحيحة لإيصالها إلى المستخدمين؟</p>","options":[{"text":"انشر <code dir=\"ltr\">eas update</code> على production؛ وسينزّل المستخدمون الوحدة.","why":"لا تستطيع التحديثات إيصال كود أصلي."},{"text":"انشر تحديثًا واطلب من المستخدمين إعادة التشغيل مرتين.","why":"إعادة التشغيل لا تترجم كودًا أصليًا داخل التطبيق."},{"text":"أبقِ <code dir=\"ltr\">version</code> كما هو لتظل التحديثات الحالية تنطبق.","why":"عندها قد تتلقّى الـ builds القديمة كود JavaScript يستدعي وحدة لا تملكها."},{"text":"ارفع <code dir=\"ltr\">version</code>، واصنع production build، وأرسله للمراجعة، وأطلقه؛ وعندها تستهدف تحديثات 1.3 إصدار وقت التشغيل الجديد.","why":"صحيح. التغييرات الأصلية تنتقل دائمًا في ملف تطبيق جديد."}],"answer":3}]},"final":[{"q":"<p>أيّ وصف لمعمارية Trailhead دقيق؟</p>","options":[{"text":"تُرسم components من React كـ HTML يعرضه WebView.","why":"لا يوجد HTML ولا WebView في مسار الرسم في React Native."},{"text":"يُولَّد كود Swift وKotlin من الـ JSX الخاص بك وقت البناء.","why":"لا يُترجَم الـ JSX إلى Swift أو Kotlin؛ بل يعمل بوصفه JavaScript."},{"text":"تعمل JavaScript على الـ UI thread وترسم على الشاشة مباشرة.","why":"تعمل JavaScript على thread خاص بها؛ والـ UI thread يرسم الـ native views."},{"text":"تعمل JavaScript في Hermes على thread خاص بها، وينشئ مُصيِّر Fabric الـ native views التي يرسمها الـ UI thread.","why":"صحيح. هذا هو مسار الـ New Architecture الذي يستخدمه كل تطبيق React Native حالي."}],"answer":3},{"q":"<p>بطاقة رحلة جديدة تبدو منضغطة: العنوان يفيض ويدفع شارة التاريخ خارج الشاشة. الصف <code dir=\"ltr\">flexDirection: 'row'</code>. أيّ تغيير هو الأسلوب المعتاد؟</p>","options":[{"text":"أعطِ العنوان <code dir=\"ltr\">width: 200</code>.","why":"العرض الثابت ينكسر على أحجام الشاشات الأخرى ومع النص الأكبر."},{"text":"أعطِ الشارة <code dir=\"ltr\">position: 'absolute'</code>.","why":"إخراج الشارة من التدفّق يجعل العنوان يمتدّ تحتها."},{"text":"أعطِ حاوية العنوان <code dir=\"ltr\">flex: 1</code> والـ Text الخاصية <code dir=\"ltr\">numberOfLines={1}</code>.","why":"صحيح. تمتصّ الحاوية العرض المتبقي، وتُقتطع العناوين الطويلة."},{"text":"حوّل الصف إلى <code dir=\"ltr\">flexDirection: 'column'</code>.","why":"هذا يغيّر التصميم بدلًا من إصلاح الفيضان."}],"answer":2},{"q":"<p>أيّ إعداد قائمة صحيح لـ 1,000 رحلة مجمّعة بحسب الشهر مع ترويسات لاصقة؟</p>","options":[{"text":"ScrollView مع استدعاءات <code dir=\"ltr\">.map()</code> متداخلة للأشهر والرحلات.","why":"يرسم كل صفّ دفعة واحدة، وهذا بطيء ويستهلك الذاكرة بشراهة."},{"text":"SectionList بأقسام على شكل <code dir=\"ltr\">{ title, data }</code> وkeyExtractor يُرجع معرّف كل رحلة.","why":"صحيح. يجعل الصفوف افتراضية (virtualized) ويرسم ترويسات أقسام لاصقة."},{"text":"FlatList لكل شهر داخل ScrollView.","why":"تداخل القوائم الافتراضية داخل ScrollView يُبطل الـ virtualization ويُطلق تحذيرًا."}],"answer":1},{"q":"<p>tab الإحصاءات في Trailhead جزء من مجموعة <code dir=\"ltr\">(tabs)</code>، و<code dir=\"ltr\">hikes/[id]</code> في الـ Stack الجذري. يفتح مستخدم في الإحصاءات رحلة ثم يسحب للرجوع. أين يصل؟</p>","options":[{"text":"إلى tab الإحصاءات، مع بقاء موضع التمرير كما هو.","why":"صحيح. أُضيفت شاشة التفاصيل فوق الـ tabs؛ وإزالتها تكشف الـ tab الذي جاء منه، وهو ما زال مركّبًا."},{"text":"إلى الـ tab الأول، لأن مجموعة الـ tabs تُعاد ضبطها.","why":"الـ tabs تحتفظ بالـ state الخاصة بها؛ ولا شيء يُعاد ضبطه عند الرجوع."},{"text":"إلى شاشة تسجيل الدخول.","why":"الرجوع يُزيل شاشة واحدة؛ ولا يغادر المنطقة المحميّة."},{"text":"إلى لا مكان؛ شاشات التفاصيل في الـ Stack الجذري لا يمكن الرجوع منها بالسحب.","why":"شاشات الـ Stack تدعم إيماءة الرجوع افتراضيًا."}],"answer":0},{"q":"<p>ما السبب الرئيسي الذي يجعل Trailhead يُبقي التحليل والتحقّق في دوال نقية مثل <code dir=\"ltr\">parseDistanceKm</code> و<code dir=\"ltr\">validateHikeForm</code>؟</p>","options":[{"text":"الدوال النقية تعمل على الـ UI thread، فهي أسرع.","why":"تعمل على الـ JS thread مثل كل شيء آخر في كود تطبيقك."},{"text":"يشترط React Native أن يعيش التحقّق خارج الـ components.","why":"لا يوجد شرط كهذا؛ إنه قرار تصميمي."},{"text":"يمكن اختبارها في أجزاء من الثانية باستخدام Jest العادي، دون أجهزة أو mocks أو رسم.","why":"صحيح. على الجوّال، توفّر حلقة الاختبار السريعة وقتًا أكبر بكثير مما على الويب."},{"text":"تجعل حزمة JavaScript أصغر.","why":"حجم الحزمة بالكاد يتغيّر؛ والعائد هو قابلية الاختبار والوضوح."}],"answer":2},{"q":"<p>يفتح deep link العنوان <code dir=\"ltr\">trailhead://hikes/9999</code>، لكن تلك الرحلة غير موجودة. ماذا يجب أن يحدث؟</p>","options":[{"text":"ينهار التطبيق، وهذا مقبول مع الروابط غير الصالحة.","why":"أي رابط من أي مكان يستطيع فتح تطبيقك؛ والانهيار غير مقبول أبدًا."},{"text":"تعرض شاشة التفاصيل رسالة واضحة «الرحلة غير موجودة» مع طريق للعودة إلى السجلّ.","why":"صحيح. عامل الـ deep links بوصفها مدخلات غير موثوقة وصمّم حالة «غير موجود»."},{"text":"يعيد Expo Router التوجيه بصمت إلى <code dir=\"ltr\">+not-found.tsx</code> لأن المعرّف مجهول.","why":"يطابق الـ router شكل الـ URL فقط؛ ولا يعرف أي المعرّفات موجودة في بياناتك."}],"answer":1},{"q":"<p>يجب أن يتذكّر Trailhead الـ token الخاص بتسجيل الدخول، و600 رحلة، والوحدات التي يفضّلها المستخدم. أيّ توزيع صحيح؟</p>","options":[{"text":"الثلاثة في AsyncStorage تحت مفاتيح منفصلة.","why":"سيكون الـ token غير مشفّر، وستكون الرحلات بطيئة الاستعلام."},{"text":"الثلاثة في expo-secure-store.","why":"صُمّم SecureStore للأسرار الصغيرة، لا لسجلّ من 600 عنصر."},{"text":"الـ token في SQLite، والرحلات في AsyncStorage، والوحدات في SecureStore.","why":"كل عنصر في المخزن الأقل ملاءمة له."},{"text":"الـ token في expo-secure-store، والرحلات في expo-sqlite، والوحدات في AsyncStorage.","why":"صحيح. الأسرار مشفّرة، والسجلّات قابلة للاستعلام، والتفضيلات بسيطة."}],"answer":3},{"q":"<p>ما أفضل لحظة يطلب فيها Trailhead إذن الكاميرا؟</p>","options":[{"text":"عند أول تشغيل، مع الموقع والإشعارات، للتخلّص من الأمر.","why":"يرفض المستخدمون ما لا يفهمونه، وعلى iOS يصعب التراجع عن الرفض الأول."},{"text":"أبدًا؛ لا يحتاج expo-image-picker إلى إذن من أجل الكاميرا.","why":"تشغيل الكاميرا يتطلّب دائمًا إذن الكاميرا."},{"text":"حين ينقر المستخدم على “Take photo” في رحلة، بعد شرح قصير إن لزم.","why":"صحيح. السبب واضح في تلك اللحظة، فيوافق الناس."},{"text":"بعد أن يسجّل المستخدم عشر رحلات، كمكافأة.","why":"الطلب في لحظة لا علاقة لها بالأمر مربك بقدر الطلب عند التشغيل."}],"answer":2},{"q":"<p>تبدأ شاشة تتبّع <code dir=\"ltr\">watchPositionAsync</code> داخل effect. أيّ تنظيف صحيح؟</p>","options":[{"text":"خزّن الاشتراك واستدعِ <code dir=\"ltr\">subscription.remove()</code> في دالة التنظيف الخاصة بالـ effect، مع التعامل مع حالة وصوله بعد الإزالة.","why":"صحيح. وإلا يستمر الـ GPS في العمل واستنزاف البطارية بعد إغلاق الشاشة."},{"text":"استدعِ <code dir=\"ltr\">Location.stopAllAsync()</code> حين ينتقل التطبيق إلى الخلفية.","why":"هذه الدالة غير موجودة؛ أنت تزيل الاشتراك الذي أنشأته."},{"text":"لا حاجة إلى تنظيف؛ يوقف React Native الاشتراكات الأصلية عند الإزالة.","why":"الاشتراكات الأصلية لا تعرف شيئًا عن الـ components الخاصة بك."}],"answer":0},{"q":"<p>تحتاج إلى معرفة أي components يُعاد رسمها أثناء تمرير سجلّ Trailhead. أيّ أداة تجيب عن ذلك مباشرة؟</p>","options":[{"text":"LogBox","why":"تعرض LogBox الأخطاء والتحذيرات، لا نشاط الرسم."},{"text":"الـ Profiler في React Native DevTools، مع تسجيل سبب رسم كل component.","why":"صحيح. يُظهر ما رُسم، وكم استغرق، وما الذي تغيّر."},{"text":"console في Xcode","why":"يعرض السجلّات الأصلية؛ ولا يعرف شيئًا عن عمليات الرسم في React."},{"text":"<code dir=\"ltr\">eas build --profile development</code>","why":"هذا يبني تطبيقًا؛ ولا يفحصه."}],"answer":1},{"q":"<p>أيّ تسلسل يطلق الإصدار الأول من Trailhead على الـ App Store؟</p>","options":[{"text":"<code dir=\"ltr\">eas update --channel production</code>، ثم انتظار المراجعة.","why":"التحديثات تعدّل التثبيتات الموجودة؛ ولا تستطيع إنشاء الإصدار الأول في المتجر."},{"text":"<code dir=\"ltr\">npx expo start --no-dev</code>، ثم رفع رمز QR إلى App Store Connect.","why":"خادم التطوير ليس build؛ والمتاجر تحتاج إلى ملف تنفيذي موقَّع."},{"text":"<code dir=\"ltr\">eas submit</code>، ثم <code dir=\"ltr\">eas build --profile production</code>.","why":"لا تستطيع إرسال build غير موجود بعد."},{"text":"<code dir=\"ltr\">eas build --platform ios --profile production</code>، ثم <code dir=\"ltr\">eas submit --platform ios</code>، والاختبار في TestFlight، ثم الإرسال للمراجعة.","why":"صحيح. ابنِ، وارفع، واختبر الملف التنفيذي الحقيقي، ثم المراجعة."}],"answer":3},{"q":"<p>يصل انهيار في تحليل الفاصلة في Trailhead إلى بيئة الإنتاج. أيّ استجابة هي الأسرع مع بقائها آمنة؟</p>","options":[{"text":"أزل التطبيق من المتجر حتى يجتاز إصدار جديد المراجعة.","why":"إزالة التطبيق تؤذي كل مستخدم بسبب خطأ تستطيع إصلاحه عبر الهواء."},{"text":"انشر الإصلاح مباشرة على production دون اختبار لتوفير الوقت.","why":"التحديث غير المختبَر قد يكسر أكثر مما يصلح، للجميع، وبسرعة."},{"text":"أصلحه واختبره، وانشره باستخدام <code dir=\"ltr\">eas update</code> على قناة preview، وتحقّق منه على هاتف حقيقي، ثم انشره على production.","why":"صحيح. إصلاحات JavaScript وحدها يمكن أن تُطلق في اليوم نفسه، مع فحص سريع أولًا."},{"text":"ارفع إصدار وقت التشغيل ليصل الإصلاح إلى كل الـ builds.","why":"إصدار وقت تشغيل جديد سيمنع التحديث من الوصول إلى الـ builds الحالية."}],"answer":2}],"glossary":[{"id":"native-view","term":"Native view","definition":"عنصر واجهة من المنصّة، مثل UIView على iOS أو android.view.View على Android، ينشئه React Native ويحدّثه انطلاقًا من الـ components الخاصة بك بدلًا من رسم HTML.","lesson":"l-rn-1-1"},{"id":"new-architecture","term":"New Architecture","definition":"نظام الرسم والوحدات الأصلية في React Native، المبني على JSI ومُصيِّر Fabric والـ TurboModules. وهو المعمارية الوحيدة منذ الإصدار 0.82 من React Native.","lesson":"l-rn-1-1"},{"id":"hermes","term":"Hermes","definition":"محرّك JavaScript المصمّم لـ React Native. يشغّل كود JavaScript في تطبيقك على thread خاص به، منفصل عن الـ UI thread الذي يرسم الـ views ويستقبل اللمسات.","lesson":"l-rn-1-1"},{"id":"expo-go","term":"Expo Go","definition":"تطبيق من المتجر فيه مجموعة ثابتة من الوحدات الأصلية، يحمّل كود JavaScript الخاص بمشروعك من خادم التطوير. ممتاز للتعلّم، لكنه لا يستطيع أن يضمّ كودًا أصليًا مخصّصًا.","lesson":"l-rn-1-2"},{"id":"development-build","term":"Development build","definition":"ملف تطبيقك الخاص، بوحداتك الأصلية وهويّتك وإعداداتك، إضافةً إلى أدوات التطوير مثل Fast Refresh. تنشئه باستخدام npx expo run أو EAS Build.","lesson":"l-rn-1-2"},{"id":"metro","term":"Metro","definition":"مُجمِّع JavaScript وخادم التطوير الذي يشغّله الأمر npx expo start. يقدّم كودك إلى Expo Go أو إلى الـ development build، وعليه يعتمد Fast Refresh.","lesson":"l-rn-1-2"},{"id":"pressable","term":"Pressable","definition":"الـ component الأساسي لكل ما يُنقَر. يُبلغ عن الضغطات والضغطات المطوّلة، ويمكن أن يكون التنسيق الخاص به دالة لحالة الضغط لإعطاء استجابة بصرية فورية.","lesson":"l-rn-1-4"},{"id":"hit-slop","term":"hitSlop","definition":"prop في Pressable يوسّع منطقة اللمس إلى ما وراء العنصر المرئي، فتستطيع الأيقونات الصغيرة مع ذلك بلوغ الحدّ الأدنى لهدف اللمس وهو 44 نقطة.","lesson":"l-rn-1-4"},{"id":"style-array","term":"مصفوفة التنسيق (style array)","definition":"prop التنسيق حين يُعطى مصفوفةً من كائنات التنسيق. يدمجها React Native من اليسار إلى اليمين، فتفوز المفاتيح اللاحقة، وتُتخطّى القيم الزائفة.","lesson":"l-rn-2-1"},{"id":"density-independent-point","term":"النقطة المستقلّة عن الكثافة (density-independent point)","definition":"الوحدة التي تقف خلف كل رقم بلا وحدة في تنسيقات React Native. تحوّل المنصّة النقاط إلى بكسلات فعلية، فتبدو الأحجام متماثلة على الشاشات منخفضة الكثافة وعالية الكثافة.","lesson":"l-rn-2-1"},{"id":"main-axis","term":"المحور الرئيسي (main axis)","definition":"الاتجاه الذي ترتّب فيه حاوية flex أبناءها، ويحدّده flexDirection. وهو عمودي افتراضيًا في React Native، ويحدّد justifyContent مواضع الأبناء على امتداده.","lesson":"l-rn-2-2"},{"id":"safe-area","term":"Safe area","definition":"جزء الشاشة الذي لا تغطّيه النتوءات ولا الزوايا المستديرة ولا مؤشّر الشاشة الرئيسية ولا أشرطة النظام. والشاشات التي ليس لها ترويسة تضيف حشوها بنفسها عبر هوامش react-native-safe-area-context.","lesson":"l-rn-2-3"},{"id":"virtualization","term":"Virtualization","definition":"التقنية التي يستخدمها FlatList وSectionList لتركيب الصفوف القريبة من منطقة العرض فقط، واستبدال البقية بفراغ، فتبقى القوائم الطويلة سريعة وخفيفة على الذاكرة.","lesson":"l-rn-2-4"},{"id":"key-extractor","term":"keyExtractor","definition":"prop في FlatList يُرجع مفتاحًا ثابتًا وفريدًا لكل عنصر، وهو معرّفه عادةً، فتحتفظ الصفوف بهويّتها الصحيحة حين تُضاف العناصر أو تُحذف أو يُعاد ترتيبها.","lesson":"l-rn-2-4"},{"id":"file-based-routing","term":"التوجيه المبني على الملفات (file-based routing)","definition":"نموذج Expo Router الذي يكون فيه كل ملف داخل src/app شاشةً ومساره هو الـ URL، مع ملفات _layout تغلّف المسارات المجاورة بأداة تنقّل.","lesson":"l-rn-3-1"},{"id":"stack-navigator","term":"Stack navigator","definition":"أداة تنقّل تضيف الشاشات بعضها فوق بعض مع حركات أصلية وإيماءات رجوع. وتبقى الشاشات التي تحتها مركّبة، محتفظةً بالـ state وموضع التمرير.","lesson":"l-rn-3-1"},{"id":"route-group","term":"مجموعة المسارات (route group)","definition":"مجلّد اسمه بين قوسين، مثل (tabs). يتيح لمساراته مشاركة layout واحد دون أن يضيف جزءًا إلى عناوين URL الخاصة بها.","lesson":"l-rn-3-2"},{"id":"dynamic-route","term":"المسار الديناميكي (dynamic route)","definition":"ملف مسار اسمه بين أقواس مربّعة، مثل [id].tsx، يطابق أي جزء مفرد من الـ URL ويكشفه param نصيًا عبر useLocalSearchParams.","lesson":"l-rn-3-2"},{"id":"protected-route","term":"المسار المحمي (protected route)","definition":"مسار مغلّف بـ Stack.Protected مع شرط guard. ما دام الشرط false يكون المسار غير متاح، ويُعاد توجيه أي تنقّل إليه، بما في ذلك من الـ deep links.","lesson":"l-rn-3-5"},{"id":"deep-link","term":"Deep link","definition":"رابط URL، مثل trailhead://hikes/42، يفتح التطبيق مباشرة على شاشة محدّدة. ويربطه Expo Router بملف المسار المناسب تلقائيًا.","lesson":"l-rn-3-5"},{"id":"async-storage","term":"AsyncStorage","definition":"مخزن مفتاح-قيمة للنصوص، غير متزامن وغير مشفّر. يناسب التفضيلات الصغيرة، لا الأسرار ولا المجموعات الكبيرة من السجلّات التي تحتاج إلى الاستعلام عنها.","lesson":"l-rn-4-2"},{"id":"secure-store","term":"SecureStore","definition":"الوحدة expo-secure-store، التي تشفّر القيم الصغيرة مثل الـ tokens باستخدام Keychain في iOS وKeystore في Android.","lesson":"l-rn-4-2"},{"id":"migration","term":"ترحيل قاعدة البيانات (database migration)","definition":"خطوة مرقّمة بإصدار ترقّي مخطّط قاعدة بيانات موجودة، كإضافة عمود، وتُنفَّذ مرة واحدة على كل جهاز بالترتيب، وتُتتبَّع عادةً عبر PRAGMA user_version في SQLite.","lesson":"l-rn-4-2"},{"id":"config-plugin","term":"Config plugin","definition":"مُدخل في app.json يكتب الإعدادات الأصلية، مثل أوصاف الأذونات في iOS أو مُدخلات الـ manifest في Android، حين يُبنى التطبيق. وأي تغيير فيه يحتاج إلى build جديد.","lesson":"l-rn-4-3"},{"id":"can-ask-again","term":"canAskAgain","definition":"حقل في استجابة الأذونات في Expo. حين تكون قيمته false، لا يعرض النظام نافذة الإذن مرة أخرى، فلا يستطيع تغيير إجابة المستخدم إلا تطبيق الإعدادات.","lesson":"l-rn-4-3"},{"id":"react-native-devtools","term":"React Native DevTools","definition":"أداة التصحيح المدمجة، تُفتح بالضغط على j في طرفية Metro، وتقدّم console ونقاط توقّف ومستكشف Components وProfiler لتطبيقك العامل.","lesson":"l-rn-5-1"},{"id":"jest-expo","term":"jest-expo","definition":"preset لأداة Jest يضبط التحويلات اللازمة لـ React Native ويصنع mock للجانب الأصلي من وحدات Expo، فتعمل الاختبارات في Node دون جهاز.","lesson":"l-rn-5-2"},{"id":"eas-build","term":"EAS Build","definition":"خدمة Expo السحابية التي تترجم ملفات تطبيقات Android وiOS وتوقّعها من مشروعك، باستخدام profiles معرّفة في eas.json مثل development وpreview وproduction.","lesson":"l-rn-5-3"},{"id":"eas-update","term":"EAS Update","definition":"خدمة Expo لنشر كود JavaScript وassets جديدة إلى الـ builds المثبّتة عبر الهواء، دون إصدار في المتجر. ولا تستطيع إيصال تغييرات الكود الأصلي.","lesson":"l-rn-5-4"},{"id":"runtime-version","term":"إصدار وقت التشغيل (runtime version)","definition":"وسم مشترك بين build والتحديثات المتوافقة معه. لا ينطبق التحديث إلا على الـ builds التي لها إصدار وقت التشغيل نفسه، وهذا يحمي الـ builds القديمة من كود JavaScript يحتاج إلى كود أصلي أحدث.","lesson":"l-rn-5-4"}]}