نموذج كائنات المستند (DOM)

نموذج كائنات المستند (Document Object Model)، ويُختصر DOM، هو النموذج الحي الذي يبنيه المتصفح لصفحة الويب في الذاكرة. فعندما يحلّل المتصفح شيفرة HTML يبني شجرة من العُقد (Nodes): المستند في الجذر، وعقدة عنصر لكل عنصر، وعُقد نصية للكلمات، ثم يتيح لـJavaScript واجهة لقراءة هذه الشجرة وتغييرها. والمتصفح يرسم الشاشة من DOM ومن CSS، لا من ملف HTML.

مثال ملموس: صفحة سلة تُرسل <input id="qty" value="1">. فيضمّ DOM عنصر إدخال قيمة سمته value هي 1. وعندما يغيّر المستخدم الكمية إلى 3 تصبح الخاصية value للعنصر 3، بينما تبقى السمة 1، ولا يتغيّر مصدر الصفحة إطلاقاً. فالمصدر هو الصفحة كما أُرسلت، وDOM هو الصفحة كما هي الآن.

لماذا يهم

كل ما يفعله اختبار واجهة المستخدم يمرّ عبر DOM. فالمحدِّد مثل #qty استعلام على الشجرة الحالية، والنقرة تُسلَّم إلى عقدة عنصر، والتأكيد يقرأ خاصيةً أو نص عقدة. وإذا لم تعرف ما كان في DOM في تلك اللحظة، فلن تستطيع أن تحكم هل كشف الاختبار الفاشل عيباً، أم استخدم محدِّداً خاطئاً، أم بحث قبل الأوان.

ويهمّ ذلك في الاختبار اليدوي أيضاً. فبعض العيوب لا تظهر إلا في DOM: رسالة تُضاف لكنها تبقى مخفية، وحقل غير مربوط بتسميته، وزرّ موجود مرتين. وقراءة DOM تتيح لك أن تبلّغ عن السبب، أي العنصر وسماته وحالته، لا عن العَرَض وحده، وأن تتحقق هل المشكلة في الصفحة أم في اختبارك.

شجرة من العُقد

رأيت في درس كيف تعمل المتصفحات أن المتصفح يحلّل HTML. وناتج هذه الخطوة هو DOM: شجرة كل ما فيها عقدة. فعقدة المستند (Document) هي الجذر، وتصل إليها السكربتات باسم document. وكل عنصر HTML يصبح عقدة عنصر (Element)، أي فرعاً يمكن أن يحتوي عُقداً أخرى. والنص داخل العناصر يصبح عُقداً نصية (Text)، وهي أوراق الشجرة. وتعليقات HTML تصبح عُقد تعليق (Comment)، فهي موجودة في DOM مع أن شيئاً لا يعرضها.

السمات ليست أبناءً في الشجرة. فالسمة id="qty" محفوظة على عنصر الإدخال نفسه، كبطاقة ملصقة على صندوق لا كغرض بداخله. كما أن المسافات وفواصل الأسطر بين الوسوم تصبح عادةً عُقداً نصية، ولهذا يجد السكربت الذي يعدّ العُقد الأبناء لعنصرٍ ما عدداً أكبر مما توحي به الوسوم.

  • الأب والابن (Parent وChild): في النموذج أدناه، العنصر form هو أب العناصر label وinput وbutton وp، وهي أبناؤه.
  • الأشقاء (Siblings): أبناء الأب نفسه، مثل label وinput.
  • السلف والخلَف (Ancestor وDescendant): العنصر body سلفٌ لعنصر input، وكل ما داخل النموذج من خلَف النموذج.
  • والمحددات تستخدم العلاقات نفسها. ففي CSS يعني المحدِّد form > input عنصر إدخال هو ابن مباشر لنموذج، ويعني form input أي عنصر إدخال من خلَفه. وفي XPath تنتقل / إلى ابن، وتبحث // في كل الخلَف (وفي بداية التعبير في المستند كله)، ويُبقي الشرط [@id='qty'] العناصر التي لسمتها هذه القيمة فقط، وتصعد .. إلى الأب: فالتعبير //input/.. ينتقل من عنصر الإدخال إلى أبيه.
<form id="cart">
  <label for="qty">Quantity</label>
  <input id="qty" value="1">
  <button type="submit">Update</button>
  <p class="msg" hidden>Cart updated</p>
</form>
نموذج السلة كما يحفظه المتصفح: عُقد العناصر تتفرّع من عقدة المستند، والعُقد النصية هي الأوراق، والسمات مثل value="1" محفوظة على عنصرها لا داخله.

نموذج السلة في DOM

الجزء 1:#document
└ html
  ├ head
  └ body
    └ الجزء 2:form id="cart"
      ├ label for="qty"
      │ └ الجزء 4:"Quantity"
      ├ input الجزء 3:id="qty" value="1"
      ├ button type="submit"
      │ └ الجزء 4:"Update"
      └ p class="msg" الجزء 5:hidden
        └ الجزء 4:"Cart updated"
  1. 1عقدة المستندالجذر، واسمه document في السكربتات: كل بحث عن عنصر يبدأ منه أو من عنصر وُجد قبلاً
  2. 2عقدة عنصرواحدة لكل عنصر، من html إلى p. والعنصر form أبٌ لأربعة أبناء هم أشقاء فيما بينهم
  3. 3السماتمحفوظة على العنصر، وليست أبناءً في الشجرة. بعد أن يغيّر المستخدم الكمية إلى 3 تصبح الخاصية value "3" بينما تبقى هذه السمة "1"
  4. 4العُقد النصيةالكلمات التي يقرؤها المستخدم، وهي أوراق بلا أبناء، وتطابقها المحددات النصية
  5. 5موجود لكنه غير معروضالسمة hidden تُبقي الفقرة خارج الشاشة، لكن الاستعلام يجدها مع ذلك: فالموجود ليس بالضرورة مرئياً

حُذفت أبناء head والعُقد النصية التي لا تحوي إلا مسافات بين الوسوم.

جرّبها: منسّق HTML

السمات والخصائص

السمة (Attribute) تُكتب في HTML، مثل value="1". أما الخاصية (Property) فتنتمي إلى كائن JavaScript الذي يمثّل العنصر، مثل qty.value. ويُبقي المتصفح الاثنين متطابقين في كثير من السمات، ويسمّي معيار HTML ذلك الانعكاس (Reflecting): فتغيير الخاصية id يغيّر السمة id والعكس. ولبعض الأزواج أسماء مختلفة: فالسمة class تقابلها الخاصية className، والسمة for تقابلها htmlFor.

أما الأزواج الأهم للمختبِر فهي التي لا تبقى متطابقة:

  • value في حقل الإدخال: السمة هي القيمة الابتدائية، ويمكن قراءتها أيضاً من الخاصية defaultValue. أما الخاصية value فهي ما يحويه الحقل الآن. والكتابة في الحقل تغيّر الخاصية وحدها.
  • checked في مربع الاختيار: السمة تقول هل يبدأ محدَّداً (defaultChecked)، والخاصية checked تقول هل هو محدَّد الآن.
  • href في الرابط: يُعيد getAttribute('href') ما كُتب في HTML، مثل /help، بينما تُعيد الخاصية href العنوان الكامل الذي حلّله المتصفح، مثل https://shop.example/help.
  • السمات المنطقية (Boolean) مثل disabled وrequired: العبرة بوجودها فقط. فالسمة disabled="false" تعطّل الزر مع ذلك، لأن السمة موجودة. والسمة hidden تعمل بالطريقة نفسها: فبأي قيمة، حتى hidden="false"، يبقى العنصر مخفياً.
// After the user changed the quantity from 1 to 3
const qty = document.querySelector('#qty');
qty.getAttribute('value');  // "1"  attribute: what the HTML said
qty.defaultValue;           // "1"  property that reflects the attribute
qty.value;                  // "3"  property: what the field holds now

كيف تغيّر JavaScript شجرة DOM

لغة JavaScript التي تعرّفت عليها في الدرس السابق تغيّر الصفحة بتغيير DOM، ثم يعيد المتصفح رسم الشاشة من الشجرة الجديدة. ويكاد كل تغيير يكون واحدة من عمليات قليلة: إيجاد عقدة، أو إنشاء عقدة، أو إدراجها، أو حذفها، أو تغيير نصها أو سماتها أو خصائصها. وتجد السكربتات العناصر عادةً عبر document.querySelector()، الذي يأخذ محدِّد CSS ويُعيد أول عنصر مطابق، أو null إن لم يطابقه شيء، أما querySelectorAll() فيُعيد كل التطابقات.

وأُطر العمل مثل React وAngular وVue تُجري الاستدعاءات نفسها نيابةً عنك. وعندما تتغيّر البيانات قد تحذف عنصراً وتُدرج عنصراً جديداً مكانه، فالعنصر الذي وجده اختبارك قبل لحظة قد لا يكون في الشجرة بعد الآن. عندها يفشل Selenium بالخطأ stale element reference، أما Playwright فيتجنّب معظم هذه الحالات لأنه يحدّد عنصراً محدَّثاً في كل مرة يُستخدم فيها المحدِّد.

  • مصدر الصفحة وDOM مختلفان. فأمر عرض مصدر الصفحة (View Page Source، أو Ctrl+U) يعرض ما أرسله الخادم، أما DOM فيضم أيضاً كل ما أضافته السكربتات بعد ذلك. وفي تطبيق الصفحة الواحدة (Single-Page Application) قد لا يزيد المصدر كثيراً على حاوية فارغة مثل <div id="root"></div>.
  • قد يصل المحتوى بعد ظهور الصفحة. فعندما يجلب سكربت بيانات ثم يُدرجها، لا يكون العنصر موجوداً في البداية، فعلى الاختبار أن ينتظره.
  • المحلِّل (Parser) يغيّر الشجرة أيضاً. فصف الجدول المكتوب مباشرةً داخل <table> يوضع داخل عنصر <tbody> يُدرجه المحلّل، لذا لا يطابق تعبير XPath المنسوخ من المصدر، مثل //table/tr، أي شيء.
  • النص المُدرج عبر innerHTML يُحلَّل على أنه شيفرة HTML، أما textContent فيُدرجه نصاً عادياً. فإذا ظهر اسم كُتب بالشكل <b>Ali</b> بخط عريض في مكانٍ ما، فمدخلات المستخدم تصل إلى DOM على أنها شيفرة: أبلغ عن ذلك، لأن المسار نفسه قد يسمح بثغرة البرمجة عبر المواقع (XSS).
const form = document.querySelector('#cart');   // find an element
const msg = form.querySelector('.msg');
msg.textContent = 'Cart updated: 3 items';     // replace its text
msg.hidden = false;                            // removes the hidden attribute
const note = document.createElement('p');      // create a node, not in the tree yet
note.textContent = 'Free delivery';
form.append(note);                             // insert it: now it can be seen
note.remove();                                 // remove it again

فحص DOM، ولماذا تعتمد عليه المحددات

انقر بزر الفأرة الأيمن على عنصر واختر Inspect (فحص)، فتُفتح أدوات المطور على لوحة Elements (وفي Firefox لوحة Inspector) وقد حُدِّد ذلك العنصر. تعرض هذه اللوحة DOM الحي لا المصدر: فالعناصر التي تضيفها السكربتات تظهر فيها، والمحذوفة تختفي، والسمات تتحدّث عند تغيّرها. لكن شجرتها تعرض السمات لا الخصائص، فبعد أن تغيّر الكمية إلى 3 تظل تعرض value="1". ولقراءة خاصية اكتب $0.value في لوحة Console، فالرمز $0 يشير إلى العنصر المحدَّد في لوحة Elements.

مصدر الصفحة لا يُظهر ما أضافته السكربتات ولا عنصر tbody الذي أدرجه المحلّل، وشجرة Elements تُظهرهما لكنها تعرض السمة value، فمن بين الثلاثة لا يقرأ ما كتبه المستخدم إلا Console عبر $0.value.

عرض مصدر الصفحةCtrl+U

شيفرة HTML كما أرسلها الخادم

العناصر التي أضافتها السكربتات:
لا
عنصر tbody أدرجه المحلّل:
لا
ما كتبه المستخدم في حقل:
لا
استخدمه من أجل:
رؤية ما سلّمه الخادم

لوحة Elements

DOM الحي: العُقد وسماتها الآن

العناصر التي أضافتها السكربتات:
نعم
عنصر tbody أدرجه المحلّل:
نعم
ما كتبه المستخدم في حقل:
لا: تعرض السمة value
استخدمه من أجل:
كتابة المحددات وتصحيحها

لوحة Console$0

أي خاصية للعنصر المحدَّد في لوحة Elements

العناصر التي أضافتها السكربتات:
نعم
عنصر tbody أدرجه المحلّل:
نعم
ما كتبه المستخدم في حقل:
نعم: $0.value
استخدمه من أجل:
قراءة الخصائص وعدّ تطابقات المحدِّد

كل محدِّد استعلامٌ على هذه الشجرة الحية. فمحددات CSS وXPath تطابق أسماء العناصر وسماتها ومواقعها، والمحددات النصية تطابق نص العُقد، والمحددات المبنية على الدور مثل getByRole في Playwright تستخدم دوراً واسماً ميسَّراً (Accessible Name) يُحسبان من العنصر وسماته ونصه ومن عناصر مرتبطة به مثل تسميته <label>. لذلك تكتب المحددات وتصحّحها على DOM في الحالة التي سيصادفها الاختبار، لا على مصدر الصفحة أبداً.

  • عُدّ التطابقات قبل أن تعتمد على محدِّد: يجب أن يُعيد document.querySelectorAll('#cart button').length في لوحة Console القيمة 1. وفي Chrome وEdge يقبل البحث بالاختصار Ctrl+F في لوحة Elements أيضاً محدِّد CSS أو تعبير XPath.
  • افحص في الحالة الصحيحة: بعد تحميل البيانات، ومع فتح مربع الحوار، وبكل لغة تعمل بها حزمة الاختبارات.
  • الموجود ليس بالضرورة مرئياً. فالعنصر p الذي يحمل hidden موجود في DOM فيجده الاستعلام، لكن المستخدم لا يراه. وإذا كان المتطلب أن يرى المستخدم شيئاً فتأكّد من ظهوره.
  • الإطار المضمَّن (iframe) يحوي مستنداً منفصلاً، وجذر الظل (Shadow Root) يخفي الشجرة الداخلية لمكوّن عن الاستعلامات العادية، وكلاهما يحتاج المعالجة الخاصة الموضحة في دليل استراتيجيات المحددات.

مثال تطبيقي: ثلاثة فحوص على صفحة السلة

يؤتمت مختبِر نموذج السلة من هذا الدرس، ومعه جدول سجل الطلبات. والمتطلب: بعد أن يغيّر المستخدم الكمية وينقر «Update» تظهر الرسالة «Cart updated». ثلاثة فحوص تعطي نتائج مفاجئة، وإعادة كلٍّ منها في أدوات المطور تُظهر كيف يفسّره DOM.

  • الفحص الأول يفشل: بعد تغيير الكمية إلى 3 يقرأ الاختبار السمة value لحقل الإدخال فيحصل على "1". أعِد الفحص في لوحة Console بعد تحديد الحقل في لوحة Elements: يُعيد $0.getAttribute('value') القيمة "1"، بينما يُعيد $0.value القيمة "3". التطبيق سليم، لكن الاختبار قرأ السمة بدلاً من الخاصية. الإصلاح: اجعل الفحص يقرأ الخاصية value. وأدوات الأتمتة توفّر زوج السمة والخاصية نفسه بأسمائها الخاصة، ويبيّن درسا Playwright وSelenium أيّ استدعاء يقرأ كلاً منهما.
  • الفحص الثاني يفشل: التعبير //table[@id='orders']/tr المنسوخ من مصدر الصفحة لا يجد أي صف. وفي لوحة Console تنفّذ الدالة $x() تعبير XPath وتُعيد العناصر المطابقة: فيُعيد $x("//table[@id='orders']/tr") قائمة فارغة []، ولوحة Elements تُظهر عنصر tbody الذي أدرجه المحلّل. الإصلاح: حدّد الصفوف كما هي في DOM، فالاستدعاء $x("//table[@id='orders']/tbody/tr") يُعيد كل الصفوف.
  • الفحص الثالث ينجح، ولا ينبغي له ذلك: يقرأ الاختبار textContent للعنصر .msg فيجد "Cart updated: 3 items". لكن المختبِر الذي يشاهد التشغيل لا يرى أي رسالة. وفي لوحة Elements ما زال العنصر p يحمل hidden، وبعد تحديده يُعيد $0.hidden في لوحة Console القيمة true: غيّر السكربت النص ولم يُظهره قط. هذا عيب حقيقي، وقد فات الاختبارَ لأنه فحص النص لا الظهور.
  • ولترى بالضبط ما غيّره سكربت، انسخ outerHTML للنموذج قبل النقر على «Update» وبعده (انقر بالزر الأيمن على العنصر في لوحة Elements واختر Copy ثم Copy outerHTML) وقارن النسختين في أداة Text Diff. وكما في لوحة Elements، يعرض outerHTML السمات لا القيم المكتوبة في الحقول.

جرّبها: مقارنة النصوص (Diff)

أخطاء شائعة

  • اعتبار «عرض مصدر الصفحة» هو DOM، وكتابة المحددات أو تقارير الأخطاء بناءً عليه.
  • التحقق مما كتبه المستخدم بقراءة السمة value أو لوحة Elements؛ فالقيمة الحالية هي الخاصية value.
  • افتراض أن العنصر الذي يجده الاستعلام مرئي للمستخدم؛ فالوجود والظهور فحصان منفصلان.
  • فهم disabled="false" أو hidden="false" على أنه مفعَّل أو ظاهر؛ ففي هاتين السمتين العبرة بالوجود، أياً كانت القيمة.
  • الاحتفاظ بمرجع لعنصر عبر إعادة العرض (Re-render) واستخدامه لاحقاً، فينتهي الأمر بخطأ stale element.
  • استخدام محدِّد من «Copy XPath» أو من المصدر دون التحقق في DOM الحي من أنه يطابق عنصراً واحداً بالضبط.

من منظور ضمان الجودة

في الفريق، DOM أرض مشتركة بين المختبِرين والمطورين. وعندما تبلّغ عن عيب في الواجهة، أضف ما أظهره DOM: العنصر (محدِّداً قصيراً أو outerHTML الخاص به)، وقيم السمات أو الخصائص المهمة، وهل كان العنصر مفقوداً أم مخفياً أم مكرراً. فعبارة «بعد Update يحتفظ p.msg بالسمة hidden» تُصلَح في دقائق، أما «الرسالة لا تظهر» فتُرسل المطور في رحلة بحث.

وعندما تؤتمت، اطلب من المطورين مبكراً نقاط ربط مستقرة، مثل معرّفات ذات معنى وأسماء ميسَّرة أو سمة متفق عليها لمعرّف الاختبار، واسأل أي أجزاء الصفحة يُعاد عرضها عند تغيّر البيانات. وقبل أن تبلّغ عن اختبار واجهة فاشل على أنه عيب في المنتج، انظر إلى DOM في الحالة التي فشل فيها: فكثير من حالات الفشل مشكلات في الاختبار، كعنصر قديم (Stale) أو محدِّد منسوخ من المصدر أو فحص جرى قبل وصول المحتوى، وDOM يخبرك أيّها هي.

تمرين عملي

اقرأ DOM لنموذج اشتراك

المطلوب

فيما يلي جزء من صفحة اشتراك في نشرة بريدية. والمتطلب: يبقى زر «Join» معطَّلاً حتى تُقبل الشروط، وبعد النقر عليه تظهر الرسالة «Check your inbox». يكتب المستخدم ana@example.com ويحدّد مربع الاختيار. والسكربت في النهاية يعمل في كل مرة يتغيّر فيها مربع الاختيار، فيضبط الخاصية disabled للزر على عكس الخاصية checked لمربع الاختيار (فالرمز ! يعني «ليس»).

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

المادة
<section id="signup">
  <h2>Newsletter</h2>
  <!-- shown to visitors who are not signed in -->
  <label for="email">Email</label>
  <input id="email" type="email" value="">
  <label><input id="terms" type="checkbox"> I accept the terms</label>
  <button id="join" disabled>Join</button>
  <p id="status" hidden></p>
</section>
<script>
  const terms = document.querySelector('#terms');
  const join = document.querySelector('#join');
  terms.addEventListener('change', () => { join.disabled = !terms.checked; });
</script>
الخطوات
  1. ارسم شجرة DOM للعنصر section: عُقد العناصر والعُقد النصية وعقدة التعليق. واترك العُقد النصية التي لا تحوي إلا مسافات.
  2. بعد ما فعله المستخدم، اذكر السمة value والخاصية value لحقل البريد، والسمة checked والخاصية checked لمربع الاختيار.
  3. ماذا يحدث لسمة disabled في الزر عند تحديد المربع، وماذا تعرض لوحة Elements؟
  4. أيّ هذه المحددات يطابق عنصراً واحداً بالضبط: #signup label، #signup input، #terms، label[for=email]؟
  5. اختبار زميلك يجد #status بعد النقر على «Join» وينجح لأن العنصر موجود. ما الخطأ في هذا الفحص، وماذا سيجد فحص أفضل؟
إظهار الإجابة النموذجية
الإجابة النموذجية

الشجرة: للعنصر section#signup ستة أبناء من العناصر بالترتيب: h2 وlabel وinput#email وlabel وbutton#join وp#status، وعقدة التعليق بين h2 وlabel الأول. يحوي h2 العقدة النصية «Newsletter»، ويحوي label الأول «Email»، وللعنصر label الثاني ابنان: مربع الاختيار input#terms والعقدة النصية « I accept the terms»، ويحوي button النص «Join»، أما p#status فلا أبناء له. والسمات مثل for وtype وdisabled وhidden محفوظة على عناصرها، لا في الشجرة.

حقل البريد: السمة value ما زالت نصاً فارغاً كما كُتبت، والخاصية value هي "ana@example.com". مربع الاختيار: لا توجد سمة checked (فالاستدعاء getAttribute('checked') يُعيد null)، بينما قيمة الخاصية checked هي true.

يضبط السكربت الخاصية disabled على false، ولأن disabled سمة منعكسة يحذف المتصفح السمة، فتعرض لوحة Elements الوسم <button id="join">Join</button>. وإلغاء تحديد المربع يُعيد disabled.

المحددان #terms وlabel[for=email] يطابق كلٌّ منهما عنصراً واحداً. أما #signup label فيطابق تسميتين، و#signup input يطابق عنصري إدخال: حقل البريد ومربع الاختيار.

الفقرة موجودة في DOM منذ البداية، مخفية وفارغة، فعبارة «العنصر موجود» صحيحة قبل النقر وبعده ولا تثبت شيئاً. والفحص الأفضل يتأكد أن #status ظاهر ويحوي «Check your inbox». وفي هذه الصفحة سيفشل: فلا سكربت يملأ الرسالة أو يُظهرها، وهذا عيب يُبلَّغ عنه مع العنصر وسمته hidden دليلاً.

أسئلة شائعة

هل DOM هو نفسه HTML؟

لا. HTML هو النص الذي يرسله الخادم، وDOM هو الشجرة التي يبنيها المتصفح منه ويواصل تغييرها. فالمحلِّل يكمل الشيفرة ويصحّحها، والسكربتات تضيف العُقد وتحذفها وتغيّرها، فقد يختلف الاثنان كثيراً بعد تحميل الصفحة. وأمر عرض مصدر الصفحة يُظهر HTML، ولوحة Elements تُظهر DOM.

هل يحتاج المختبِر إلى تعلّم واجهة DOM البرمجية؟

تحتاج إلى قراءة DOM لا برمجته: أنواع العُقد وعلاقاتها، والفرق بين السمات والخصائص، وبضعة أوامر في Console مثل $0 وdocument.querySelectorAll للتحقق من قيمة أو محدِّد. والأتمتة تستخدم قدراً أكبر من الواجهة، لكن محدداتها وتأكيداتها تقوم على الأفكار نفسها.

لماذا لا يجد اختباري عنصراً أراه على الشاشة؟

غالباً لأن العنصر داخل إطار مضمَّن (iframe) وهو مستند منفصل، أو داخل جذر ظل (Shadow Root)، أو لم يكن قد أُضيف بعد حين بحث الاختبار، أو لأن المحدِّد كُتب من مصدر الصفحة لا من DOM الحي. وبعض ما يظهر على الشاشة ليس عناصر أصلاً: فالنص المرسوم على عنصر canvas مثلاً لا يجده أي محدِّد في DOM.

المراجع

  • HTML Living StandardWHATWGلغة HTML، بما فيها مراجع المحارف المسماة (الكيانات) وقواعد التحليل التي تتبعها المتصفحات.
  • WebDriver (W3C)W3C · 2026بروتوكول W3C الذي تنفّذه المتصفحات لتتحكم فيها أدوات الأتمتة مثل Selenium عن بُعد (الإصدار Level 2 مسودة عمل).
  • Document Object Model (DOM) — MDNMDN Web Docs (Mozilla)نموذج كائن المستند (DOM): شجرة العُقد التي يبنيها المتصفح من الصفحة، والتي تقرؤها السكربتات — وأدوات اختبار الواجهات — وتعدّلها.
  • Locators — PlaywrightMicrosoft (Playwright)كيف يعثر Playwright على العناصر، ولماذا تُعاد تحديد المحدِّدات وتنتظر تلقائياً، ولماذا يُوصى بالمحدِّدات المبنية على الدور أولاً.

تدرّب بأدوات متقن

تجميل HTML بمسافات بادئة صحيحة للعناصر المتداخلة، مع الحفاظ على كتل pre وscript وstyle.

التنسيق

للتعمّق أكثر

تقدّمك في هذا الموضوع

جزء من مسار تعلم ضمان الجودة