سرویس مقایسه تصویر (تست رگرسیون بصری)
@wdio/visual-service یک پکیج شخص ثالث است، برای اطلاعات بیشتر لطفاً به GitHub | npm مراجعه کنید
برای مستندات تست بصری با WebdriverIO، لطفاً به مستندات مراجعه کنید. این پروژه شامل تمام ماژولهای مرتبط برای اجرای تستهای بصری با WebdriverIO است. در دایرکتوری ./packages
موارد زیر را خواهید یافت:
@wdio/visual-testing
: سرویس WebdriverIO برای یکپارچهسازی تستهای بصریwebdriver-image-comparison
: یک ماژول مقایسه تصویر که میتواند برای چارچوبهای مختلف تست خودکار NodeJS که از پروتکل WebDriver پشتیبانی میکنند، استفاده شود
اجراکننده Storybook (بتا)
برای اطلاعات بیشتر درباره اجراکننده Storybook بتا کلیک کنید
اجراکننده Storybook همچنان در مرحله بتا است، مستندات بعداً به صفحات مستندات WebdriverIO منتقل خواهد شد.
این ماژول اکنون از Storybook با یک اجراکننده بصری جدید پشتیبانی میکند. این اجراکننده به طور خودکار یک نمونه storybook محلی/راه دور را اسکن میکند و اسکرینشات عنصر از هر کامپوننت ایجاد میکند. این کار با اضافه کردن
export const config: WebdriverIO.Config = {
// ...
services: ["visual"],
// ....
};
به services
و اجرای npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook
از طریق خط فرمان انجام میشود.
به صورت پیشفرض از Chrome در حالت headless استفاده خواهد کرد.
[!NOTE]
- بیشتر گزینههای تست بصری برای اجراکننده Storybook نیز کار میکنند، به مستندات WebdriverIO مراجعه کنید.
- اجراکننده Storybook تمام قابلیتهای شما را بازنویسی میکند و فقط میتواند روی مرورگرهایی که پشتیبانی میکند اجرا شود، به
--browsers
مراجعه کنید.- اجراکننده Storybook از یک پیکربندی موجود که از قابلیتهای Multiremote استفاده میکند پشتیبانی نمیکند و خطا خواهد داد.
- اجراکننده Storybook فقط از Desktop Web پشتیبانی میکند، نه Mobile Web.
گزینههای سرویس اجراکننده Storybook
گزینههای سرویس میتوانند به این صورت ارائه شوند
export const config: WebdriverIO.Config = {
// ...
services: [
[
'visual',
{
// برخی گزینههای پیشفرض
baselineFolder: join(process.cwd(), './__snapshots__/'),
debug: true,
// گزینههای storybook، به گزینههای cli برای توضیحات مراجعه کنید
storybook: {
additionalSearchParams: new URLSearchParams({foo: 'bar', abc: 'def'}),
clip: false,
clipSelector: ''#some-id,
numShards: 4,
// `skipStories` میتواند یک رشته ('example-button--secondary')،
// یک آرایه (['example-button--secondary', 'example-button--small'])
// یا یک regex باشد که باید به صورت رشته ارائه شود ("/.*button.*/gm")
skipStories: ['example-button--secondary', 'example-button--small'],
url: 'https://www.bbc.co.uk/iplayer/storybook/',
version: 6,
// اختیاری - امکان اورراید کردن مسیر خط مبنا را فراهم میکند. به صورت پیشفرض خطوط مبنا را بر اساس دسته و کامپوننت گروهبندی میکند (مثلاً forms/input/baseline.png)
getStoriesBaselinePath: (category, component) => `path__${category}__${component}`,
},
},
],
],
// ....
}
گزینههای CLI اجراکننده Storybook
--additionalSearchParams
- نوع:
string
- اجباری: خیر
- پیشفرض: ''
- مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --additionalSearchParams="foo=bar&abc=def"
پارامترهای جستجوی اضافی را به URL استوریبوک اضافه میکند. برای اطلاعات بیشتر به مستندات URLSearchParams مراجعه کنید. رشته باید یک رشته URLSearchParams معتبر باشد.
[!NOTE] علامتهای نقل قول دوگانه برای جلوگیری از تفسیر
&
به عنوان جداکننده دستور مورد نیاز است. به عنوان مثال با--additionalSearchParams="foo=bar&abc=def"
URL استوریبوک زیر برای تست داستانها ایجاد میشود:http://storybook.url/iframe.html?id=story-id&foo=bar&abc=def
.
--browsers
- نوع:
string
- اجباری: خیر
- پیشفرض:
chrome
، میتوانید ازchrome|firefox|edge|safari
انتخاب کنید - مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --browsers=chrome,firefox,edge,safari
- نکته: فقط از طریق CLI در دسترس است
از مرورگرهای ارائه شده برای گرفتن اسکرینشات کامپوننت استفاده میکند
[!NOTE] اطمینان حاصل کنید که مرورگرهایی که میخواهید روی آنها اجرا کنید روی دستگاه محلی شما نصب شدهاند
--clip
- نوع:
boolean
- اجباری: خیر
- پیشفرض:
true
- مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --clip=false
وقتی غیرفعال است، یک اسکرینشات ویوپورت ایجاد میکند. وقتی فعال است، اسکرینشاتهای عنصر بر اساس --clipSelector
ایجاد میکند که فضای سفید اطراف اسکرینشات کامپوننت را کاهش داده و اندازه اسکرینشات را کم میکند.
--clipSelector
- نوع:
string
- اجباری: خیر
- پیشفرض:
#storybook-root > :first-child
برای Storybook V7 و#root > :first-child:not(script):not(style)
برای Storybook V6، همچنین به--version
مراجعه کنید - مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --clipSelector="#some-id"
این انتخابگر برای موارد زیر استفاده میشود:
- انتخاب عنصر برای گرفتن اسکرینشات
- برای عنصری که قبل از گرفتن اسکرینشات باید قابل مشاهده باشد
--devices
- نوع:
string
- اجباری: خیر
- پیشفرض: میتوانید از
deviceDescriptors.ts
انتخاب کنید - مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --devices="iPhone 14 Pro Max","Pixel 3 XL"
- نکته: فقط از طریق CLI در دسترس است
از دستگاههای ارائه شده که با deviceDescriptors.ts
مطابقت دارند برای گرفتن اسکرینشات کامپوننت استفاده میکند
[!NOTE]
- اگر پیکربندی دستگاهی را از دست دادهاید، لطفاً یک درخواست ویژگی ارسال کنید
- این فقط با Chrome کار میکند:
- اگر
--devices
را ارائه دهید، تمام نمونههای Chrome در حالت شبیهسازی موبایل اجرا میشوند- اگر مرورگرهای دیگری مانند
--devices --browsers=firefox,safari,edge
نیز ارائه دهید، به طور خودکار Chrome را در حالت شبیهسازی موبایل اضافه میکند- اجراکننده Storybook به طور پیشفرض اسنپشاتهای عنصر ایجاد میکند، اگر میخواهید اسکرینشات کامل شبیهسازی شده موبایل را ببینید،
--clip=false
را از طریق خط فرمان ارائه دهید- نام فایل به عنوان مثال به شکل
__snapshots__/example/button/desktop_chrome/example-button--large-local-chrome-iPhone-14-Pro-Max-430x932-dpr-3.png
خواهد بود- منبع: تست یک وبسایت موبایل روی یک دسکتاپ با استفاده از شبیهسازی موبایل میتواند مفید باشد، اما تستکنندگان باید از تفاوتهای ظریف متعددی آگاه باشند مانند:
- GPU کاملاً متفاوت، که ممکن است منجر به تغییرات عملکردی بزرگی شود؛
- رابط کاربری موبایل شبیهسازی نمیشود (به ویژه، مخفی کردن نوار url بر ارتفاع صفحه تأثیر میگذارد)؛
- پاپآپ رفع ابهام (جایی که یکی از چند هدف لمسی را انتخاب میکنید) پشتیبانی نمیشود؛
- بسیاری از APIهای سختافزاری (به عنوان مثال، رویداد orientationchange) در دسترس نیستند.
--headless
- نوع:
boolean
- اجباری: خیر
- پیشفرض:
true
- مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --headless=false
- نکته: فقط از طریق CLI در دسترس است
این تستها را به طور پیشفرض در حالت headless اجرا میکند (وقتی مرورگر از آن پشتیبانی میکند) یا میتواند غیرفعال شود
--numShards
- نوع:
number
- اجباری: خیر
- پیشفرض:
true
- مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --numShards=10
این تعداد نمونههای موازی خواهد بود که برای اجرای داستانها استفاده میشود. این توسط maxInstances
در فایل wdio.conf
شما محدود خواهد شد.
[!IMPORTANT] هنگام اجرا در حالت
headless
، برای جلوگیری از ناپایداری به دلیل محدودیتهای منابع، تعداد را بیش از 20 افزایش ندهید
--skipStories
- نوع:
string|regex
- اجباری: خیر
- پیشفرض: null
- مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --skipStories="/.*button.*/gm"
این میتواند:
- یک رشته (
example-button--secondary,example-button--small
) - یا یک regex (
"/.*button.*/gm"
)
باشد تا داستانهای خاصی را رد کند. از id
داستان که در URL داستان قابل یافتن است استفاده کنید. به عنوان مثال، id
در این URL http://localhost:6006/?path=/story/example-page--logged-out
برابر است با example-page--logged-out
--url
- نوع:
string
- اجباری: خیر
- پیشفرض:
http://127.0.0.1:6006
- مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --url="https://example.com"
URL جایی که نمونه Storybook شما میزبانی میشود.
--version
- نوع:
number
- اجباری: خیر
- پیشفرض: 7
- مثال:
npx wdio tests/configs/wdio.local.desktop.storybook.conf.ts --storybook --version=6
این نسخه Storybook است، به طور پیشفرض 7
است. این برای دانستن اینکه آیا انتخابگر clipSelector
نسخه V6 باید استفاده شود، ضروری است.
تست تعاملی Storybook
تست تعاملی Storybook به شما اجازه میدهد با کامپوننت خود از طریق ایجاد اسکریپتهای سفارشی با دستورات WDIO برای قرار دادن یک کامپوننت در یک وضعیت خاص تعامل داشته باشید. به عنوان مثال، به قطعه کد زیر نگاه کنید:
import { browser, expect } from "@wdio/globals";
describe("Storybook Interaction", () => {
it("should create screenshots for the logged in state when it logs out", async () => {
const componentId = "example-page--logged-in";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
await $("button=Log out").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
});
it("should create screenshots for the logged out state when it logs in", async () => {
const componentId = "example-page--logged-out";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
await $("button=Log in").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
});
});
دو تست روی دو کامپوننت مختلف اجرا میشوند. هر تست ابتدا یک وضعیت را تنظیم کرده و سپس یک اسکرینشات میگیرد. همچنین متوجه خواهید شد که یک دستور سفارشی جدید معرفی شده است که میتوانید آن را اینجا پیدا کنید.
فایل مشخصات بالا را میتوان در یک پوشه ذخیره کرد و با دستور زیر به خط فرمان اضافه کرد:
pnpm run test.local.desktop.storybook.localhost -- --spec='tests/specs/storybook-interaction/*.ts'
اجراکننده Storybook ابتدا به طور خودکار نمونه Storybook شما را اسکن میکند و سپس تستهای شما را به داستانهایی که باید مقایسه شوند اضافه میکند. اگر نمیخواهید کامپوننتهایی که برای تست تعاملی استفاده میکنید دو بار مقایسه شوند، میتوانید یک فیلتر اضافه کنید تا داستانهای "پیشفرض" را از اسکن با ارائه فیلتر --skipStories
حذف کنید. این به شکل زیر خواهد بود:
pnpm run test.local.desktop.storybook.localhost -- --skipStories="/example-page.*/gm" --spec='tests/specs/storybook-interaction/*.ts'
دستور سفارشی جدید
یک دستور سفارشی جدید به نام browser.waitForStorybookComponentToBeLoaded({ id: 'componentId' })
به شیء browser/driver
اضافه خواهد شد که به طور خودکار کامپوننت را بارگذاری کرده و منتظر میماند تا کار انجام شود، بنابراین نیازی به استفاده از روش browser.url('url.com')
ندارید. میتوان از آن به این شکل استفاده کرد
import { browser, expect } from "@wdio/globals";
describe("Storybook Interaction", () => {
it("should create screenshots for the logged in state when it logs out", async () => {
const componentId = "example-page--logged-in";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
await $("button=Log out").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
});
it("should create screenshots for the logged out state when it logs in", async () => {
const componentId = "example-page--logged-out";
await browser.waitForStorybookComponentToBeLoaded({ id: componentId });
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-out-state`
);
await $("button=Log in").click();
await expect($("header")).toMatchElementSnapshot(
`${componentId}-logged-in-state`
);
});
});
گزینهها عبارتند از:
additionalSearchParams
- نوع:
URLSearchParams
- اجباری: خیر
- پیشفرض:
new URLSearchParams()
- مثال:
await browser.waitForStorybookComponentToBeLoaded({
additionalSearchParams: new URLSearchParams({ foo: "bar", abc: "def" }),
id: "componentId",
});
این پارامترهای جستجوی اضافی را به URL استوریبوک اضافه میکند، در مثال بالا URL به صورت http://storybook.url/iframe.html?id=story-id&foo=bar&abc=def
خواهد بود.
برای اطلاعات بیشتر به مستندات URLSearchParams مراجعه کنید.
clipSelector
- نوع:
string
- اجباری: خیر
- پیشفرض:
#storybook-root > :first-child
برای Storybook V7 و#root > :first-child:not(script):not(style)
برای Storybook V6 - مثال:
await browser.waitForStorybookComponentToBeLoaded({
clipSelector: "#your-selector",
id: "componentId",
});
این انتخابگر برای موارد زیر استفاده میشود:
- انتخاب عنصر برای گرفتن اسکرینشات
- برای عنصری که قبل از گرفتن اسکرینشات باید قابل مشاهده باشد
id
- نوع:
string
- اجباری: بله
- مثال:
await browser.waitForStorybookComponentToBeLoaded({ '#your-selector', id: 'componentId' })
از id
داستان که در URL داستان قابل یافتن است استفاده کنید. به عنوان مثال، id
در این URL http://localhost:6006/?path=/story/example-page--logged-out
برابر است با example-page--logged-out
timeout
- نوع:
number
- اجباری: خیر
- پیشفرض: 1100 میلیثانیه
- مثال:
await browser.waitForStorybookComponentToBeLoaded({
id: "componentId",
timeout: 20000,
});
حداکثر زمان انتظار برای قابل مشاهده شدن یک کامپوننت پس از بارگذاری در صفحه
url
- نوع:
string
- اجباری: خیر
- پیشفرض:
http://127.0.0.1:6006
- مثال:
await browser.waitForStorybookComponentToBeLoaded({
id: "componentId",
url: "https://your.url",
});
URL جایی که نمونه Storybook شما میزبانی میشود.
مشارکت
بهروزرسانی پکیجها
شما میتوانید پکیجها را با یک ابزار ساده CLI بهروزرسانی کنید. مطمئن شوید که تمام وابستگیها را نصب کردهاید، سپس میتوانید اجرا کنید
pnpm update.packages
این یک CLI را راهاندازی میکند که از شما سؤالات زیر را میپرسد
==========================
🤖 Package update Wizard 🧙
==========================
? Which version target would you like to update to? (Minor|Latest)
? Do you want to update the package.json files? (Y/n)
? Do you want to remove all "node_modules" and reinstall dependencies? (Y/n)
? Would you like reinstall the dependencies? (Y/n)
سؤالات
لطفاً به سرور Discord ما بپیوندید اگر سؤال یا مشکلی در مشارکت در این پروژه دارید. مشارکتکنندگان را در کانال 🙏-contributing
پیدا کنید.
مشکلات
اگر سؤال، باگ یا درخواست ویژگی دارید، لطفاً یک issue ثبت کنید. قبل از ارسال یک issue، لطفاً آرشیو issue را جستجو کنید تا موارد تکراری را کاهش دهید و FAQ را بخوانید.
اگر نمیتوانید آن را در آنجا پیدا کنید، میتوانید یک issue ارسال کنید که در آن میتوانید موارد زیر را ارسال کنید:
- 🐛گزارش باگ: یک گزارش ایجاد کنید تا به ما در بهبود کمک کنید
- 📖مستندات: پیشنهاداتی برای بهبود ارائه دهید یا مستندات گم شده/مبهم را گزارش دهید.
- 💡درخواست ویژگی: یک ایده برای این ماژول پیشنهاد دهید.
- 💬سؤال: سؤالات خود را بپرسید.
گردش کار توسعه
برای ایجاد یک PR برای این پروژه و شروع مشارکت، این راهنمای گام به گام را دنبال کنید:
-
پروژه را fork کنید.
-
پروژه را در جایی در کامپیوتر خود clone کنید
$ git clone https://github.com/webdriverio/visual-testing.git
-
به دایرکتوری بروید و پروژه را راهاندازی کنید
$ cd visual-testing
$ corepack enable
$ pnpm pnpm.install.workaround -
حالت watch را اجرا کنید که به طور خودکار کد را ترانسپایل میکند
$ pnpm watch
برای ساخت پروژه، اجرا کنید:
$ pnpm build
-
اطمینان حاصل کنید که تغییرات شما هیچ تستی را خراب نمیکند، اجرا کنید:
$ pnpm test
این پروژه از changesets برای ایجاد خودکار تغییرات و انتشارها استفاده میکند.
تستکردن
چندین تست باید اجرا شود تا بتوان ماژول را آزمایش کرد. هنگام افزودن یک PR، تمام تستها باید حداقل تستهای محلی را پاس کنند. هر PR به طور خودکار در Sauce Labs تست میشود، به خط لوله GitHub Actions ما مراجعه کنید. قبل از تأیید یک PR، مشارکتکنندگان اصلی PR را در برابر شبیهسازها/شبیهسازهای دستگاه واقعی آزمایش خواهند کرد.
تست محلی
ابتدا، یک خط مبنای محلی باید ایجاد شود. این کار میتواند با موارد زیر انجام شود:
// With the webdriver protocol
$ pnpm run test.local.init
این دستور یک پوشه به نام localBaseline
ایجاد میکند که تمام تصاویر خط مبنا را نگه میدارد.
سپس اجرا کنید:
// With the webdriver protocol
pnpm run test.local.desktop
این تمام تستها را روی یک دستگاه محلی روی Chrome اجرا میکند.
تست اجراکننده Storybook محلی (بتا)
ابتدا، یک خط مبنای محلی باید ایجاد شود. این کار میتواند با موارد زیر انجام شود:
pnpm run test.local.desktop.storybook
این تستهای Storybook را با Chrome در حالت headless در برابر یک مخزن Demo Storybook واقع در https://govuk-react.github.io/govuk-react/ اجرا میکند.
برای اجرای تستها با مرورگرهای بیشتر میتوانید موارد زیر را اجرا کنید
pnpm run test.local.desktop.storybook -- --browsers=chrome,firefox,edge,safari
[!NOTE] اطمینان حاصل کنید که مرورگرهایی که میخواهید روی آنها اجرا کنید روی دستگاه محلی شما نصب شدهاند
تست CI با Sauce Labs (برای یک PR ضروری نیست)
دستور زیر برای تست build در GitHub Actions استفاده میشود، فقط میتواند در آنجا استفاده شود و نه برای توسعه محلی.
$ pnpm run test.saucelabs
این روی تعداد زیادی پیکربندی تست میکند که میتوان آنها را اینجا یافت. تمام PRها به طور خودکار در برابر Sauce Labs بررسی میشوند.
انتشار
برای انتشار یک نسخه از هر یک از پکیجهای ذکر شده در بالا، کارهای زیر را انجام دهید:
- خط لوله انتشار را راهاندازی کنید
- یک PR انتشار ایجاد میشود، آن را توسط یکی دیگر از اعضای WebdriverIO بررسی و تأیید کنید
- PR را ادغام کنید
- خط لوله انتشار را دوباره راهاندازی کنید
- یک نسخه جدید باید منتشر شود 🎉
اعتبارات
@wdio/visual-testing
از یک مجوز منبع باز از LambdaTest و Sauce Labs استفاده میکند.