Vue.js
Vue.js یک فریمورک قابل دسترس، کارآمد و چندمنظوره برای ساخت رابطهای کاربری وب است. شما میتوانید کامپوننتهای Vue.js را مستقیماً در یک مرورگر واقعی با استفاده از WebdriverIO و browser runner آن تست کنید.
راهاندازی
برای راهاندازی WebdriverIO در پروژه Vue.js خود، دستورالعملها را در مستندات تست کامپوننت ما دنبال کنید. اطمینان حاصل کنید که vue
را به عنوان پیشتنظیم در گزینههای runner خود انتخاب کردهاید، مثلاً:
// wdio.conf.js
export const config = {
// ...
runner: ['browser', {
preset: 'vue'
}],
// ...
}
اگر شما از قبل از Vite به عنوان سرور توسعه استفاده میکنید، میتوانید به سادگی پیکربندی خود را در vite.config.ts
در پیکربندی WebdriverIO خود مجدداً استفاده کنید. برای اطلاعات بیشتر، به viteConfig
در گزینههای runner مراجعه کنید.
پیشتنظیم Vue نیاز به نصب @vitejs/plugin-vue
دارد. همچنین ما استفاده از Testing Library را برای رندر کردن کامپوننت در صفحه تست توصیه میکنیم. بنابراین شما باید وابستگیهای اضافی زیر را نصب کنید:
- npm
- Yarn
- pnpm
npm install --save-dev @testing-library/vue @vitejs/plugin-vue
yarn add --dev @testing-library/vue @vitejs/plugin-vue
pnpm add --save-dev @testing-library/vue @vitejs/plugin-vue
سپس میتوانید تستها را با اجرای دستور زیر شروع کنید:
npx wdio run ./wdio.conf.js
نوشتن تستها
با فرض اینکه شما کامپوننت Vue.js زیر را دارید:
<template>
<div>
<p>Times clicked: {{ count }}</p>
<button @click="increment">increment</button>
</div>
</template>
<script>
export default {
data: () => ({
count: 0,
}),
methods: {
increment() {
this.count++
},
},
}
</script>
در تست خود، کامپوننت را در DOM رندر کنید و بررسیهای خود را روی آن انجام دهید. ما توصیه میکنیم از @vue/test-utils
یا @testing-library/vue
برای اتصال کامپوننت به صفحه تست استفاده کنید. برای تعامل با کامپوننت، از دستورات WebdriverIO استفاده کنید زیرا رفتار آنها به تعاملات واقعی کاربر نزدیکتر است، مثلاً:
- @vue/test-utils
- @testing-library/vue
import { $, expect } from '@wdio/globals'
import { mount } from '@vue/test-utils'
import Component from './components/Component.vue'
describe('Vue Component Testing', () => {
it('increments value on click', async () => {
// The render method returns a collection of utilities to query your component.
const wrapper = mount(Component, { attachTo: document.body })
expect(wrapper.text()).toContain('Times clicked: 0')
const button = await $('aria/increment')
// Dispatch a native click event to our button element.
await button.click()
await button.click()
expect(wrapper.text()).toContain('Times clicked: 2')
await expect($('p=Times clicked: 2')).toExist() // same assertion with WebdriverIO
})
})
import { $, expect } from '@wdio/globals'
import { render } from '@testing-library/vue'
import Component from './components/Component.vue'
describe('Vue Component Testing', () => {
it('increments value on click', async () => {
// The render method returns a collection of utilities to query your component.
const { getByText } = render(Component)
// getByText returns the first matching node for the provided text, and
// throws an error if no elements match or if more than one match is found.
getByText('Times clicked: 0')
const button = await $(getByText('increment'))
// Dispatch a native click event to our button element.
await button.click()
await button.click()
getByText('Times clicked: 2') // assert with Testing Library
await expect($('p=Times clicked: 2')).toExist() // assert with WebdriverIO
})
})
شما میتوانید یک نمونه کامل از مجموعه تست کامپوننت WebdriverIO برای Vue.js را در مخزن نمونه ما پیدا کنید.
تست کامپوننتهای ناهمگام در Vue3
اگر از Vue نسخه 3 استفاده میکنید و در حال تست کامپوننتهای ناهمگام مانند زیر هستید:
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
<template>
{{ posts }}
</template>
ما توصیه میکنیم از @vue/test-utils
و یک wrapper suspense کوچک برای رندر کردن کامپوننت استفاده کنید. متأسفانه @testing-library/vue
هنوز از این پشتیبانی نمیکند. یک فایل helper.ts
با محتوای زیر ایجاد کنید:
import { mount, type VueWrapper as VueWrapperImport } from '@vue/test-utils'
import { Suspense } from 'vue'
export type VueWrapper = VueWrapperImport<any>
const scheduler = typeof setImmediate === 'function' ? setImmediate : setTimeout
export function flushPromises(): Promise<void> {
return new Promise((resolve) => {
scheduler(resolve, 0)
})
}
export function wrapInSuspense(
component: ReturnType<typeof defineComponent>,
{ props }: { props: object },
): ReturnType<typeof defineComponent> {
return defineComponent({
render() {
return h(
'div',
{ id: 'root' },
h(Suspense, null, {
default() {
return h(component, props)
},
fallback: h('div', 'fallback'),
}),
)
},
})
}
export function renderAsyncComponent(vueComponent: ReturnType<typeof defineComponent>, props: object): VueWrapper{
const component = wrapInSuspense(vueComponent, { props })
return mount(component, { attachTo: document.body })
}
سپس کامپوننت را به صورت زیر وارد و تست کنید:
import { $, expect } from '@wdio/globals'
import { renderAsyncComponent, flushPromises, type VueWrapper } from './helpers.js'
import AsyncComponent from '/components/SomeAsyncComponent.vue'
describe('Testing Async Components', () => {
let wrapper: VueWrapper
it('should display component correctly', async () => {
const props = {}
wrapper = renderAsyncComponent(AsyncComponent, { props })
await flushPromises()
await expect($('...')).toBePresent()
})
afterEach(() => {
wrapper.unmount()
})
})
تست کامپوننتهای Vue در Nuxt
اگر از فریمورک وب Nuxt استفاده میکنید، WebdriverIO به طور خودکار ویژگی auto-import را فعال میکند و تست کامپوننتهای Vue و صفحات Nuxt را آسان میسازد. با این حال، هر ماژول Nuxt که ممکن است در پیکربندی خود تعریف کنید و به زمینه برنامه Nuxt نیاز داشته باشد، نمیتواند پشتیبانی شود.
دلایل این امر عبارتند از:
- WebdriverIO نمیتواند یک برنامه Nuxt را فقط در محیط مرورگر راهاندازی کند
- وابستگی بیش از حد تستهای کامپوننت به محیط Nuxt پیچیدگی ایجاد میکند و ما توصیه میکنیم این تستها را به عنوان تستهای e2e اجرا کنید
WebdriverIO همچنین سرویسی برای اجرای تستهای e2e روی برنامههای Nuxt ارائه میدهد، برای اطلاعات بیشتر به webdriverio-community/wdio-nuxt-service
مراجعه کنید.
شبیهسازی composables داخلی
در صورتی که کامپوننت شما از یک composable بومی Nuxt استفاده میکند، مثلاً useNuxtData
، WebdriverIO به طور خودکار این توابع را شبیهسازی میکند و به شما امکان میدهد رفتار آنها را تغییر دهید یا آنها را بررسی کنید، مثلاً:
import { mocked } from '@wdio/browser-runner'
// e.g. your component uses calls `useNuxtData` the following way
// `const { data: posts } = useNuxtData('posts')`
// in your test you can assert against it
expect(useNuxtData).toBeCalledWith('posts')
// and change their behavior
mocked(useNuxtData).mockReturnValue({
data: [...]
})
مدیریت composables شخص ثالث
تمام ماژولهای شخص ثالث که میتوانند پروژه Nuxt شما را تقویت کنند، نمیتوانند به طور خودکار شبیهسازی شوند. در این موارد، شما باید آنها را به صورت دستی شبیهسازی کنید. به عنوان مثال، با فرض اینکه برنامه شما از ماژول پلاگین Supabase استفاده میکند:
export default defineNuxtConfig({
modules: [
"@nuxtjs/supabase",
// ...
],
// ...
});
و شما در جایی در composables خود یک نمونه از Supabase ایجاد میکنید، مثلاً:
const superbase = useSupabaseClient()
تست به دلیل زیر با شکست مواجه میشود:
ReferenceError: useSupabaseClient is not defined
در اینجا، ما توصیه میکنیم یا کل ماژولی که از تابع useSupabaseClient
استفاده میکند را شبیهسازی کنید یا یک متغیر جهانی ایجاد کنید که این تابع را شبیهسازی میکند، مثلاً:
import { fn } from '@wdio/browser-runner'
globalThis.useSupabaseClient = fn().mockReturnValue({})