آخر درس قبل لا تروح تتعلّم جافا — وهو الدرس الذي يجعلك تتعلّمها بهدف.
لأنه في فرق كبير بين إنك تتعلّم جافا "عموماً" وإنك تتعلّمها وانت تعرف وين ستستخدم كل جزء. والثاني أسرع بمراتب، لأنه كل مفهوم بيلتصق بسياق حقيقي.
فهذا الدرس يوريك الكود الذي سنكتبه بباقي الكورس — ويربط كل قطعة جافا بمكانها.
بنهاية هذا الدرس، ستكون قادراً على:
كل ملف بمشروعك كلاس.
public class LoginPage extends BasePage {
private static final By EMAIL = By.cssSelector("[data-test='email']");
private static final By PASSWORD = By.cssSelector("[data-test='password']");
private static final By SUBMIT = By.cssSelector("[data-test='submit']");
public DashboardPage login(String email, String password) {
type(EMAIL, email);
type(PASSWORD, password);
click(SUBMIT);
return new DashboardPage();
}
}ماذا تحتاج تفهمه هون:
public class — تعريف كلاسextends — وراثةprivate static final — حقل ثابت مخفيnew DashboardPage() — إنشاء كائنخمسة مفاهيم، وهذا 90% من كودك اليومي.
public abstract class BasePage {
protected final WebDriver driver;
protected final WebDriverWait wait;
protected BasePage() {
this.driver = DriverManager.get();
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
protected void click(By locator) {
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
}
protected void type(By locator, String text) {
WebElement el = wait.until(ExpectedConditions.elementToBeClickable(locator));
el.clear();
el.sendKeys(text);
}
}ماذا تحتاج تفهمه:
abstract — كلاس لا تستطيع أن تنشئه مباشرةprotected — متاح للأبناء بسthis — الإشارة للكائن الحاليprotected مقابل private مقابل public مهم هون تحديداً:
- private — الكلاس هذا بس
- protected — الكلاس وأبناؤه ← وهذا ما بدنا للـ BasePage
- public — الكل
لو حطيت private بالـ BasePage، الصفحات لا ترى الدوال. وهذا أشهر خطأ بأول أسبوع بعد جافا.// قائمة عناصر
List<WebElement> rows = driver.findElements(By.cssSelector(".item-row"));
int count = rows.size();
// تحويل لقائمة نصوص
List<String> names = new ArrayList<>();
for (WebElement row : rows) {
names.add(row.getText());
}
// Map للكوكيز
Map<String, String> cookies = new HashMap<>();
cookies.put("session_token", token);
for (Map.Entry<String, String> entry : cookies.entrySet()) {
driver.manage().addCookie(new Cookie(entry.getKey(), entry.getValue()));
}ماذا تحتاج: List و ArrayList و Map و HashMap، والحلقة for-each، وsize() وadd() وput().
// نفس اللي فوق بـ Stream
List<String> names = driver.findElements(By.cssSelector(".item-row")).stream()
.map(WebElement::getText)
.toList();
// فلترة
List<WebElement> enabled = driver.findElements(By.cssSelector("button")).stream()
.filter(WebElement::isEnabled)
.toList();
// عدّ
long completed = todos.stream().filter(Todo::isCompleted).count();map و filter و toList و count. وبس.
الـ Streams المتقدمة (flatMap، Collectors.groupingBy) لن تحتاجها إلا نادراً. ولو احتجتها، بتدوّر عليها وقتها.
وWebElement::getText (مرجع دالة) تقرأ "خذ النص من كل عنصر" — أنضف من el -> el.getText()، لكن الاثنين نفس الشيء.// تجاهل متوقّع
protected void dismissAlertIfPresent() {
try {
driver.switchTo().alert().accept();
} catch (NoAlertPresentException ignored) {
// لا يوجد تنبيه — وهذا الوضع الطبيعي
}
}
// تنضيف مضمون
protected void withinFrame(By frame, Runnable actions) {
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(frame));
try {
actions.run();
} finally {
driver.switchTo().defaultContent(); // ← يعمل دايماً
}
}
// رمي استثناء واضح
public static WebDriver get() {
WebDriver driver = DRIVER.get();
if (driver == null) {
throw new IllegalStateException("ما في WebDriver لهاد الخيط");
}
return driver;
}ماذا تحتاج: try/catch، وfinally، وthrow new، والفرق بين الاستثناءات المفحوصة وغير المفحوصة.
finally هي الأهم هون. لأنها تضمن التنضيف حتى لو صار خطأ — وهذا يمنع "ضلّيت جوا الإطار" و"المتصفح ضلّ مفتوح" اللي ستشوفهم بالكورس.public enum Browser {
CHROME(() -> {
ChromeOptions options = new ChromeOptions();
options.addArguments("--window-size=1920,1080");
return new ChromeDriver(options);
}),
FIREFOX(() -> new FirefoxDriver());
private final Supplier<WebDriver> supplier;
Browser(Supplier<WebDriver> supplier) { this.supplier = supplier; }
public WebDriver create() { return supplier.get(); }
}ماذا تحتاج: تعريف enum، وإنه يستطيع يحمل حقول ودوال، وvalueOf وvalues().
public record UserData(String firstName, String lastName, String email, String password) {
public static UserData valid() {
String unique = Thread.currentThread().getId() + "-" + System.currentTimeMillis();
return new UserData("أحمد", "علي", "user-" + unique + "@test.com", "P@ss123");
}
public UserData withEmail(String email) {
return new UserData(firstName, lastName, email, password);
}
}ماذا تحتاج: تعريف record، وإنه بيولّد getters تلقائياً (بدون get)، وإنه غير قابل للتغيير.
// lambda بـ WebDriverWait
wait.until(d -> d.findElements(By.cssSelector(".row")).size() >= 3);
// Supplier
Supplier<WebDriver> chromeSupplier = () -> new ChromeDriver();
WebDriver driver = chromeSupplier.get();
// Runnable
withinFrame(PAYMENT_FRAME, () -> {
type(CARD_NUMBER, "4242424242424242");
click(PAY);
});ماذا تحتاج: صيغة x -> ... و() -> ...، وSupplier وRunnable وFunction الأساسيات.
List<WebElement> // قائمة من WebElement
Map<String, String> // خرائط نص لنص
ThreadLocal<WebDriver> // ThreadLocal يحمل WebDriver
Supplier<WebDriver> // مصنع بيرجّع WebDriver
Optional<TodoResponse> // ممكن يكون فيه قيمة أو لأThreadLocal<WebDriver> معناها "ThreadLocal يحمل WebDriver". لكن لن تحتاج تكتب كلاس generic بنفسك بمشروع أتمتة عادي.
وهذا بيوفّر عليك وقت كثير — لأنه كتابة الـ generics هي الجزء الصعب، والقراءة سهلة.| المفهوم | نسبة كودك |
|---|---|
| كلاسات وكائنات | 40% |
| دوال ومعاملات | 20% |
| وراثة وتجريد | 15% |
| قوائم وMaps | 10% |
| استثناءات | 5% |
| Enums و Records | 5% |
| Streams و Lambda | 5% |
ارجع لأمثلة الكود فوق. إذا تستطيع أن تقرا 6 من 9 منهم وتفهم ماذا يصير — أنت جاهز.
لا تستطيع؟ ارجع للجزء الناقص بكورس الجافا. وهذا أرخص من إنك تكمّل على أساس ناقص.
تعلّم جافا وانت تعرف وين ستستخدم كل جزء — أسرع بمراتب من تعلّمها "عموماً".
الكلاسات والكائنات 90% من كودك — وكل ملف بمشروعك كلاس.
protected هي المفتاح بـ BasePage — وprivate تجعل الصفحات لا تشوف الدوال.
تحتاج من الـ Streams: map و filter و toList و count. وبس.
finally أهم جزء بالاستثناءات — تمنع "ضلّيت جوا الإطار" و"المتصفح ضلّ مفتوح".
الـ Enums تحمل حقول ودوال — وهذا ما يجعل Driver Factory أنيق.
الـ Records بتولّد getters بدون get — وغير قابلة للتغيير، فآمنة بالتوازي.
تحتاج تقرا الـ Generics مش تكتبها — وهذا بيوفّر وقت كبير.
75% من كودك: كلاسات ودوال ووراثة. أتقن الثلاثة وأنت جاهز.
الفحص النهائي: اقرا 6 من 9 أمثلة فوق وافهمها.
وبهيك خلصنا قسم الجافا. يلا روح خذ الكورس على القناة، وشوفك بالقسم السادس — Maven و TestNG.
بالنجاح! 💪