الگوی شیء صفحه
نسخه 5 WebdriverIO با در نظر گرفتن پشتیبانی از الگوی شیء صفحه طراحی شده است. با معرفی اصل "عناصر به عنوان شهروندان درجه یک"، اکنون امکان ساخت مجموعههای تست بزرگ با استفاده از این الگو فراهم شده است.
هیچ پکیج اضافی برای ایجاد اشیاء صفحه مورد نیاز نیست. مشخص شد که کلاسهای تمیز و مدرن تمام ویژگیهای لازم را در اختیار ما قرار میدهند:
- وراثت بین اشیاء صفحه
- بارگذاری تنبل (lazy loading) عناصر
- کپسولسازی متدها و اقدامات
هدف از استفاده از اشیاء صفحه، جدا کردن هرگونه اطلاعات صفحه از تستهای واقعی است. در حالت ایدهآل، باید تمام انتخابکنندهها یا دستورالعملهای خاص یک صفحه را در شیء صفحه ذخیره کنید، به طوری که حتی پس از طراحی مجدد کامل صفحه، همچنان بتوانید تست خود را اجرا کنید.
ساخت یک شیء صفحه
ابتدا به یک شیء صفحه اصلی نیاز داریم که آن را Page.js
مینامیم. این فایل شامل انتخابکنندهها یا متدهای عمومی خواهد بود که تمام اشیاء صفحه از آن ارث میبرند.
// Page.js
export default class Page {
constructor() {
this.title = 'My Page'
}
async open (path) {
await browser.url(path)
}
}
ما همیشه یک نمونه از شیء صفحه را export
میکنیم و هرگز آن نمونه را در تست ایجاد نمیکنیم. از آنجا که ما تستهای انتها به انتها مینویسیم، همیشه صفحه را به عنوان یک ساختار بدون حالت در نظر میگیریم - درست مانند هر درخواست HTTP که یک ساختار بدون حالت است.
البته، مرورگر میتواند اطلاعات جلسه را حمل کند و بنابراین میتواند صفحات مختلف را بر اساس جلسات مختلف نمایش دهد، اما این نباید در یک شیء صفحه منعکس شود. این نوع تغییرات حالت باید در تستهای واقعی شما قرار گیرند.
بیایید تست صفحه اول را شروع کنیم. برای اهداف نمایشی، ما از وبسایت The Internet توسط Elemental Selenium به عنوان خوکچه هندی استفاده میکنیم. بیایید سعی کنیم یک نمونه شیء صفحه برای صفحه ورود بسازیم.
گرفتن انتخابکنندههای خود با Get
اولین قدم نوشتن تمام انتخابکنندههای مهمی است که در شیء login.page
ما به عنوان توابع getter مورد نیاز هستند:
// login.page.js
import Page from './page'
class LoginPage extends Page {
get username () { return $('#username') }
get password () { return $('#password') }
get submitBtn () { return $('form button[type="submit"]') }
get flash () { return $('#flash') }
get headerLinks () { return $$('#header a') }
async open () {
await super.open('login')
}
async submit () {
await this.submitBtn.click()
}
}
export default new LoginPage()
تعریف انتخابکنندهها در توابع getter ممکن است کمی عجیب به نظر برسد، اما واقعاً مفید است. این توابع هنگامی که به خاصیت دسترسی پیدا میکنید ارزیابی میشوند، نه زمانی که شیء را ایجاد میکنید. با این کار، همیشه قبل از انجام عملیات روی آن، عنصر را درخواست میکنید.
زنجیره کردن دستورات
WebdriverIO به طور داخلی آخرین نتیجه یک دستور را به خاطر میسپارد. اگر یک دستور عنصر را با یک دستور اقدام زنجیره کنید، عنصر را از دستور قبلی پیدا میکند و از نتیجه برای اجرای اقدام استفاده میکند. با این کار میتوانید انتخابکننده (پارامتر اول) را حذف کنید و دستور به سادگی به صورت زیر خواهد بود:
await LoginPage.username.setValue('Max Mustermann')
که اساساً همان چیزی است که:
let elem = await $('#username')
await elem.setValue('Max Mustermann')
یا
await $('#username').setValue('Max Mustermann')
استفاده از اشیاء صفحه در تستهای خود
پس از تعریف عناصر و متدهای لازم برای صفحه، میتوانید نوشتن تست برای آن را شروع کنید. تمام کاری که باید برای استفاده از شیء صفحه انجام دهید، import
(یا require
) کردن آن است. همین!
از آنجا که شما یک نمونه از پیش ایجاد شده از شیء صفحه را صادر کردهاید، وارد کردن آن به شما امکان میدهد بلافاصله از آن استفاده کنید.
اگر از یک چارچوب اثبات استفاده میکنید، تستهای شما میتوانند حتی بیشتر بیانگر باشند:
// login.spec.js
import LoginPage from '../pageobjects/login.page'
describe('login form', () => {
it('should deny access with wrong creds', async () => {
await LoginPage.open()
await LoginPage.username.setValue('foo')
await LoginPage.password.setValue('bar')
await LoginPage.submit()
await expect(LoginPage.flash).toHaveText('Your username is invalid!')
})
it('should allow access with correct creds', async () => {
await LoginPage.open()
await LoginPage.username.setValue('tomsmith')
await LoginPage.password.setValue('SuperSecretPassword!')
await LoginPage.submit()
await expect(LoginPage.flash).toHaveText('You logged into a secure area!')
})
})
از نظر ساختاری، منطقی است که فایلهای spec و اشیاء صفحه را در دایرکتوریهای مختلف جدا کنید. علاوه بر این، میتوانید به هر شیء صفحه پایان .page.js
را بدهید. این مشخصتر میکند که شما یک شیء صفحه را وارد میکنید.
پیشرفت بیشتر
این اصل اساسی نحوه نوشتن اشیاء صفحه با WebdriverIO است. اما میتوانید ساختارهای شیء صفحه بسیار پیچیدهتری از این بسازید! به عنوان مثال، ممکن است اشیاء صفحه خاصی برای مودالها داشته باشید، یا یک شیء صفحه بزرگ را به کلاسهای مختلف تقسیم کنید (هر یک نشاندهنده بخش مختلفی از صفحه وب کلی) که از شیء صفحه اصلی ارث میبرند. این الگو واقعاً فرصتهای زیادی را برای جدا کردن اطلاعات صفحه از تستهای شما فراهم میکند، که برای حفظ ساختار و وضوح مجموعه تست شما در زمانی که پروژه و تعداد تستها رشد میکند، مهم است.
شما میتوانید این مثال (و حتی مثالهای بیشتر شیء صفحه) را در پوشه example
در GitHub پیدا کنید.